diff --git a/adbc.h b/adbc.h index 1ec2f05080..26b8da13b8 100644 --- a/adbc.h +++ b/adbc.h @@ -459,6 +459,24 @@ const struct AdbcError* AdbcErrorFromArrayStream(struct ArrowArrayStream* stream /// /// \see AdbcConnectionGetInfo #define ADBC_INFO_VENDOR_ARROW_VERSION 2 +/// \brief Indicates whether SQL queries are supported (type: bool). +/// +/// \see AdbcConnectionGetInfo +#define ADBC_INFO_VENDOR_SQL 3 +/// \brief Indicates whether Substrait queries are supported (type: bool). +/// +/// \see AdbcConnectionGetInfo +#define ADBC_INFO_VENDOR_SUBSTRAIT 4 +/// \brief The minimum supported Substrait version, or null if +/// Substrait is not supported (type: utf8). +/// +/// \see AdbcConnectionGetInfo +#define ADBC_INFO_VENDOR_SUBSTRAIT_MIN_VERSION 5 +/// \brief The maximum supported Substrait version, or null if +/// Substrait is not supported (type: utf8). +/// +/// \see AdbcConnectionGetInfo +#define ADBC_INFO_VENDOR_SUBSTRAIT_MAX_VERSION 6 /// \brief The driver name (type: utf8). /// @@ -754,6 +772,24 @@ const struct AdbcError* AdbcErrorFromArrayStream(struct ArrowArrayStream* stream /// schema of the data to append (ADBC_STATUS_ALREADY_EXISTS). /// \since ADBC API revision 1.1.0 #define ADBC_INGEST_OPTION_MODE_CREATE_APPEND "adbc.ingest.mode.create_append" +/// \brief The catalog of the table for bulk insert. +/// +/// The type is char*. +#define ADBC_INGEST_OPTION_TARGET_CATALOG "adbc.ingest.target_catalog" +/// \brief The schema of the table for bulk insert. +/// +/// The type is char*. +#define ADBC_INGEST_OPTION_TARGET_DB_SCHEMA "adbc.ingest.target_db_schema" +/// \brief Use a temporary table for ingestion. +/// +/// The value should be ADBC_OPTION_VALUE_ENABLED or +/// ADBC_OPTION_VALUE_DISABLED (the default). +/// +/// This is not supported with ADBC_INGEST_OPTION_TARGET_CATALOG and +/// ADBC_INGEST_OPTION_TARGET_DB_SCHEMA. +/// +/// The type is char*. +#define ADBC_INGEST_OPTION_TEMPORARY "adbc.ingest.temporary" /// @} diff --git a/c/driver/common/options.h b/c/driver/common/options.h index f42bb09046..ef4f1cf536 100644 --- a/c/driver/common/options.h +++ b/c/driver/common/options.h @@ -24,27 +24,6 @@ extern "C" { #endif -/// \brief The catalog of the table for bulk insert. -/// -/// The type is char*. -#define ADBC_INGEST_OPTION_TARGET_CATALOG "adbc.ingest.target_catalog" - -/// \brief The schema of the table for bulk insert. -/// -/// The type is char*. -#define ADBC_INGEST_OPTION_TARGET_DB_SCHEMA "adbc.ingest.target_db_schema" - -/// \brief Use a temporary table for ingestion. -/// -/// The value should be ADBC_OPTION_VALUE_ENABLED or -/// ADBC_OPTION_VALUE_DISABLED (the default). -/// -/// This is not supported with ADBC_INGEST_OPTION_TARGET_CATALOG and -/// ADBC_INGEST_OPTION_TARGET_DB_SCHEMA. -/// -/// The type is char*. -#define ADBC_INGEST_OPTION_TEMPORARY "adbc.ingest.temporary" - #ifdef __cplusplus } #endif diff --git a/go/adbc/adbc.go b/go/adbc/adbc.go index 6968faacf5..9f50935ac0 100644 --- a/go/adbc/adbc.go +++ b/go/adbc/adbc.go @@ -244,6 +244,9 @@ const ( OptionValueIngestModeAppend = "adbc.ingest.mode.append" OptionValueIngestModeReplace = "adbc.ingest.mode.replace" OptionValueIngestModeCreateAppend = "adbc.ingest.mode.create_append" + OptionValueIngestTargetCatalog = "adbc.ingest.target_catalog" + OptionValueIngestTargetDBSchema = "adbc.ingest.target_db_schema" + OptionValueIngestTemporary = "adbc.ingest.temporary" OptionKeyURI = "uri" OptionKeyUsername = "username" OptionKeyPassword = "password" @@ -344,7 +347,14 @@ const ( InfoVendorVersion InfoCode = 1 // VendorVersion // The database vendor/product Arrow library version (type: utf8) InfoVendorArrowVersion InfoCode = 2 // VendorArrowVersion - + // Indicates whether SQL queries are supported (type: bool). + InfoVendorSql InfoCode = 3 + // Indicates whether Substrait queries are supported (type: bool). + InfoVendorSubstrait InfoCode = 4 + // The minimum supported Substrait version, or null if Substrait is not supported (type: utf8). + InfoVendorSubstraitMinVersion InfoCode = 5 + // The maximum supported Substrait version, or null if Substrait is not supported (type: utf8). + InfoVendorSubstraitMaxVersion InfoCode = 6 // The driver name (type: utf8) InfoDriverName InfoCode = 100 // DriverName // The driver version (type: utf8) diff --git a/go/adbc/driver/flightsql/flightsql_connection.go b/go/adbc/driver/flightsql/flightsql_connection.go index 83807856ec..a17d8f2e3f 100644 --- a/go/adbc/driver/flightsql/flightsql_connection.go +++ b/go/adbc/driver/flightsql/flightsql_connection.go @@ -134,9 +134,13 @@ func (c *connectionImpl) SetAutocommit(enabled bool) error { } var adbcToFlightSQLInfo = map[adbc.InfoCode]flightsql.SqlInfo{ - adbc.InfoVendorName: flightsql.SqlInfoFlightSqlServerName, - adbc.InfoVendorVersion: flightsql.SqlInfoFlightSqlServerVersion, - adbc.InfoVendorArrowVersion: flightsql.SqlInfoFlightSqlServerArrowVersion, + adbc.InfoVendorName: flightsql.SqlInfoFlightSqlServerName, + adbc.InfoVendorVersion: flightsql.SqlInfoFlightSqlServerVersion, + adbc.InfoVendorArrowVersion: flightsql.SqlInfoFlightSqlServerArrowVersion, + adbc.InfoVendorSql: flightsql.SqlInfoFlightSqlServerSql, + adbc.InfoVendorSubstrait: flightsql.SqlInfoFlightSqlServerSubstrait, + adbc.InfoVendorSubstraitMinVersion: flightsql.SqlInfoFlightSqlServerSubstraitMinVersion, + adbc.InfoVendorSubstraitMaxVersion: flightsql.SqlInfoFlightSqlServerSubstraitMaxVersion, } func doGet(ctx context.Context, cl *flightsql.Client, endpoint *flight.FlightEndpoint, clientCache gcache.Cache, opts ...grpc.CallOption) (rdr *flight.Reader, err error) { @@ -564,21 +568,37 @@ func (c *connectionImpl) PrepareDriverInfo(ctx context.Context, infoCodes []adbc var adbcInfoCode adbc.InfoCode for i := 0; i < int(rec.NumRows()); i++ { - switch flightsql.SqlInfo(field.Value(i)) { - case flightsql.SqlInfoFlightSqlServerName: - adbcInfoCode = adbc.InfoVendorName - case flightsql.SqlInfoFlightSqlServerVersion: - adbcInfoCode = adbc.InfoVendorVersion - case flightsql.SqlInfoFlightSqlServerArrowVersion: - adbcInfoCode = adbc.InfoVendorArrowVersion - default: + + var found bool + idx := int(info.ValueOffset(i)) + flightSqlInfoCode := flightsql.SqlInfo(field.Value(i)) + for infocode := range adbcToFlightSQLInfo { + if adbcToFlightSQLInfo[infocode] == flightSqlInfoCode { + adbcInfoCode = infocode + found = true + break + } + } + + // SqlInfo on the server that does not have an explicit mapping to ADBC is ignored + if !found { continue } - // we know we're only doing string fields here right now - v := info.Field(info.ChildID(i)).(*array.String). - Value(int(info.ValueOffset(i))) - if err := driverInfo.RegisterInfoCode(adbcInfoCode, strings.Clone(v)); err != nil { + var v any + switch arr := info.Field(info.ChildID(i)).(type) { + case *array.String: + v = strings.Clone(arr.Value(idx)) + case *array.Boolean: + v = arr.Value(idx) + default: + return adbc.Error{ + Msg: fmt.Sprintf("unsupported field_type %T for info_value", arr), + Code: adbc.StatusInvalidArgument, + } + } + + if err := driverInfo.RegisterInfoCode(adbcInfoCode, v); err != nil { return err } } diff --git a/go/adbc/driver/flightsql/flightsql_database.go b/go/adbc/driver/flightsql/flightsql_database.go index 9f0848c3f9..21302c2cb1 100644 --- a/go/adbc/driver/flightsql/flightsql_database.go +++ b/go/adbc/driver/flightsql/flightsql_database.go @@ -383,10 +383,8 @@ func getFlightClient(ctx context.Context, loc string, d *databaseImpl, authMiddl target = "unix:" + uri.Path } - driverVersion, ok := d.DatabaseImplBase.DriverInfo.GetInfoDriverVersion() - if !ok { - driverVersion = driverbase.UnknownVersion - } + dv, _ := d.DatabaseImplBase.DriverInfo.GetInfoForInfoCode(adbc.InfoDriverVersion) + driverVersion := dv.(string) dialOpts := append(d.dialOpts.opts, grpc.WithConnectParams(d.timeout.connectParams()), grpc.WithTransportCredentials(creds), grpc.WithUserAgent("ADBC Flight SQL Driver "+driverVersion)) d.Logger.DebugContext(ctx, "new client", "location", loc) diff --git a/go/adbc/driver/internal/driverbase/connection.go b/go/adbc/driver/internal/driverbase/connection.go index fc1fc0d369..1745d3d19b 100644 --- a/go/adbc/driver/internal/driverbase/connection.go +++ b/go/adbc/driver/internal/driverbase/connection.go @@ -150,65 +150,43 @@ func (base *ConnectionImplBase) GetInfo(ctx context.Context, infoCodes []adbc.In infoValueBldr := bldr.Field(1).(*array.DenseUnionBuilder) strInfoBldr := infoValueBldr.Child(int(adbc.InfoValueStringType)).(*array.StringBuilder) intInfoBldr := infoValueBldr.Child(int(adbc.InfoValueInt64Type)).(*array.Int64Builder) + boolInfoBldr := infoValueBldr.Child(int(adbc.InfoValueBooleanType)).(*array.BooleanBuilder) for _, code := range infoCodes { - switch code { - case adbc.InfoDriverName: - name, ok := base.DriverInfo.GetInfoDriverName() - if !ok { - continue - } - - infoNameBldr.Append(uint32(code)) - infoValueBldr.Append(adbc.InfoValueStringType) - strInfoBldr.Append(name) - case adbc.InfoDriverVersion: - version, ok := base.DriverInfo.GetInfoDriverVersion() - if !ok { - continue - } - - infoNameBldr.Append(uint32(code)) - infoValueBldr.Append(adbc.InfoValueStringType) - strInfoBldr.Append(version) - case adbc.InfoDriverArrowVersion: - arrowVersion, ok := base.DriverInfo.GetInfoDriverArrowVersion() - if !ok { - continue - } + infoNameBldr.Append(uint32(code)) + value, ok := base.DriverInfo.GetInfoForInfoCode(code) + + // We want to return a null value if the info_code requested is set to nil. + // The null value needs a type so we arbitrarily choose string (type_code: 0) + if value == nil { + value = "" + ok = false + } - infoNameBldr.Append(uint32(code)) + switch v := value.(type) { + case string: infoValueBldr.Append(adbc.InfoValueStringType) - strInfoBldr.Append(arrowVersion) - case adbc.InfoDriverADBCVersion: - adbcVersion, ok := base.DriverInfo.GetInfoDriverADBCVersion() - if !ok { - continue + if ok { + strInfoBldr.Append(v) + } else { + strInfoBldr.AppendNull() } - - infoNameBldr.Append(uint32(code)) + case int64: infoValueBldr.Append(adbc.InfoValueInt64Type) - intInfoBldr.Append(adbcVersion) - case adbc.InfoVendorName: - name, ok := base.DriverInfo.GetInfoVendorName() - if !ok { - continue + if ok { + intInfoBldr.Append(v) + } else { + intInfoBldr.AppendNull() } - - infoNameBldr.Append(uint32(code)) - infoValueBldr.Append(adbc.InfoValueStringType) - strInfoBldr.Append(name) - default: - infoNameBldr.Append(uint32(code)) - value, ok := base.DriverInfo.GetInfoForInfoCode(code) - if !ok { - infoValueBldr.AppendNull() - continue + case bool: + infoValueBldr.Append(adbc.InfoValueBooleanType) + if ok { + boolInfoBldr.Append(v) + } else { + boolInfoBldr.AppendNull() } - - // TODO: Handle other custom info types - infoValueBldr.Append(adbc.InfoValueStringType) - strInfoBldr.Append(fmt.Sprint(value)) + default: + return nil, fmt.Errorf("no defined type code for info_value of type %T", v) } } diff --git a/go/adbc/driver/internal/driverbase/driver_info.go b/go/adbc/driver/internal/driverbase/driver_info.go index e68aa16c2c..7f98082b83 100644 --- a/go/adbc/driver/internal/driverbase/driver_info.go +++ b/go/adbc/driver/internal/driverbase/driver_info.go @@ -29,6 +29,20 @@ const ( DefaultInfoDriverADBCVersion = adbc.AdbcVersion1_1_0 ) +var infoValueTypeCodeForInfoCode = map[adbc.InfoCode]adbc.InfoValueTypeCode{ + adbc.InfoVendorName: adbc.InfoValueStringType, + adbc.InfoVendorVersion: adbc.InfoValueStringType, + adbc.InfoVendorArrowVersion: adbc.InfoValueStringType, + adbc.InfoDriverName: adbc.InfoValueStringType, + adbc.InfoDriverVersion: adbc.InfoValueStringType, + adbc.InfoDriverArrowVersion: adbc.InfoValueStringType, + adbc.InfoDriverADBCVersion: adbc.InfoValueInt64Type, + adbc.InfoVendorSql: adbc.InfoValueBooleanType, + adbc.InfoVendorSubstrait: adbc.InfoValueBooleanType, + adbc.InfoVendorSubstraitMinVersion: adbc.InfoValueStringType, + adbc.InfoVendorSubstraitMaxVersion: adbc.InfoValueStringType, +} + func DefaultDriverInfo(name string) *DriverInfo { defaultInfoVendorName := name defaultInfoDriverName := fmt.Sprintf("ADBC %s Driver - Go", name) @@ -73,104 +87,37 @@ func (di *DriverInfo) InfoSupportedCodes() []adbc.InfoCode { } func (di *DriverInfo) RegisterInfoCode(code adbc.InfoCode, value any) error { - switch code { - case adbc.InfoVendorName: - if err := ensureType[string](value); err != nil { - return fmt.Errorf("info_code %d: %w", code, err) - } - case adbc.InfoVendorVersion: - if err := ensureType[string](value); err != nil { - return fmt.Errorf("info_code %d: %w", code, err) - } - case adbc.InfoVendorArrowVersion: - if err := ensureType[string](value); err != nil { - return fmt.Errorf("info_code %d: %w", code, err) - } - case adbc.InfoDriverName: - if err := ensureType[string](value); err != nil { - return fmt.Errorf("info_code %d: %w", code, err) - } - case adbc.InfoDriverVersion: - if err := ensureType[string](value); err != nil { - return fmt.Errorf("info_code %d: %w", code, err) + infoValueTypeCode, isStandardInfoCode := infoValueTypeCodeForInfoCode[code] + if !isStandardInfoCode { + di.info[code] = value + return nil + } + + // If it is a standard InfoCode, we make sure to validate its type on write + var err error + switch infoValueTypeCode { + case adbc.InfoValueStringType: + if val, ok := value.(string); !ok { + err = fmt.Errorf("%s: expected info_value %v to be of type %T but found %T", code, value, val, value) } - case adbc.InfoDriverArrowVersion: - if err := ensureType[string](value); err != nil { - return fmt.Errorf("info_code %d: %w", code, err) + case adbc.InfoValueInt64Type: + if val, ok := value.(int64); !ok { + err = fmt.Errorf("%s: expected info_value %v to be of type %T but found %T", code, value, val, value) } - case adbc.InfoDriverADBCVersion: - if err := ensureType[int64](value); err != nil { - return fmt.Errorf("info_code %d: %w", code, err) + case adbc.InfoValueBooleanType: + if val, ok := value.(bool); !ok { + err = fmt.Errorf("%s: expected info_value %v to be of type %T but found %T", code, value, val, value) } } - di.info[code] = value - return nil + if err == nil { + di.info[code] = value + } + + return err } func (di *DriverInfo) GetInfoForInfoCode(code adbc.InfoCode) (any, bool) { val, ok := di.info[code] return val, ok } - -func (di *DriverInfo) GetInfoVendorName() (string, bool) { - return di.getStringInfoCode(adbc.InfoVendorName) -} - -func (di *DriverInfo) GetInfoVendorVersion() (string, bool) { - return di.getStringInfoCode(adbc.InfoVendorVersion) -} - -func (di *DriverInfo) GetInfoVendorArrowVersion() (string, bool) { - return di.getStringInfoCode(adbc.InfoVendorArrowVersion) -} - -func (di *DriverInfo) GetInfoDriverName() (string, bool) { - return di.getStringInfoCode(adbc.InfoDriverName) -} - -func (di *DriverInfo) GetInfoDriverVersion() (string, bool) { - return di.getStringInfoCode(adbc.InfoDriverVersion) -} - -func (di *DriverInfo) GetInfoDriverArrowVersion() (string, bool) { - return di.getStringInfoCode(adbc.InfoDriverArrowVersion) -} - -func (di *DriverInfo) GetInfoDriverADBCVersion() (int64, bool) { - return di.getInt64InfoCode(adbc.InfoDriverADBCVersion) -} - -func (di *DriverInfo) getStringInfoCode(code adbc.InfoCode) (string, bool) { - val, ok := di.GetInfoForInfoCode(code) - if !ok { - return "", false - } - - if err := ensureType[string](val); err != nil { - panic(err) - } - - return val.(string), true -} - -func (di *DriverInfo) getInt64InfoCode(code adbc.InfoCode) (int64, bool) { - val, ok := di.GetInfoForInfoCode(code) - if !ok { - return int64(0), false - } - - if err := ensureType[int64](val); err != nil { - panic(err) - } - - return val.(int64), true -} - -func ensureType[T any](value any) error { - typedVal, ok := value.(T) - if !ok { - return fmt.Errorf("expected info_value %v to be of type %T but found %T", value, typedVal, value) - } - return nil -} diff --git a/go/adbc/driver/internal/driverbase/driver_info_test.go b/go/adbc/driver/internal/driverbase/driver_info_test.go index 2bad25d056..b253c1b7a3 100644 --- a/go/adbc/driver/internal/driverbase/driver_info_test.go +++ b/go/adbc/driver/internal/driverbase/driver_info_test.go @@ -18,7 +18,6 @@ package driverbase_test import ( - "strings" "testing" "github.com/apache/arrow-adbc/go/adbc" @@ -45,11 +44,11 @@ func TestDriverInfo(t *testing.T) { require.ElementsMatch(t, expectedDefaultInfoCodes, driverInfo.InfoSupportedCodes()) // We get some formatted default values out of the box - vendorName, ok := driverInfo.GetInfoVendorName() + vendorName, ok := driverInfo.GetInfoForInfoCode(adbc.InfoVendorName) require.True(t, ok) require.Equal(t, "test", vendorName) - driverName, ok := driverInfo.GetInfoDriverName() + driverName, ok := driverInfo.GetInfoForInfoCode(adbc.InfoDriverName) require.True(t, ok) require.Equal(t, "ADBC test Driver - Go", driverName) @@ -59,22 +58,17 @@ func TestDriverInfo(t *testing.T) { // We cannot register a non-string value to that same info code err := driverInfo.RegisterInfoCode(adbc.InfoDriverVersion, 123) require.Error(t, err) - require.Equal(t, "info_code 101: expected info_value 123 to be of type string but found int", err.Error()) + require.Equal(t, "DriverVersion: expected info_value 123 to be of type string but found int", err.Error()) // We can also set vendor-specific info codes but they won't get type checked require.NoError(t, driverInfo.RegisterInfoCode(adbc.InfoCode(10_001), "string_value")) require.NoError(t, driverInfo.RegisterInfoCode(adbc.InfoCode(10_001), 123)) - // Retrieving known info codes is type-safe - driverVersion, ok := driverInfo.GetInfoDriverName() - require.True(t, ok) - require.NotEmpty(t, strings.Clone(driverVersion)) // do string stuff - - adbcVersion, ok := driverInfo.GetInfoDriverADBCVersion() - require.True(t, ok) - require.NotEmpty(t, adbcVersion+int64(123)) // do int64 stuff + // Once an info code has been registered, it is considered "supported" by the driver. + // This means that it will be returned if GetInfo is called with no parameters. + require.Contains(t, driverInfo.InfoSupportedCodes(), adbc.InfoCode(10_001)) - // We can also retrieve arbitrary info codes, but the result's type must be asserted + // We can retrieve arbitrary info codes, but the result's type must be asserted arrowVersion, ok := driverInfo.GetInfoForInfoCode(adbc.InfoDriverArrowVersion) require.True(t, ok) _, ok = arrowVersion.(string) diff --git a/go/adbc/driver/internal/driverbase/driver_test.go b/go/adbc/driver/internal/driverbase/driver_test.go index 309c529c1e..3d0b579bd8 100644 --- a/go/adbc/driver/internal/driverbase/driver_test.go +++ b/go/adbc/driver/internal/driverbase/driver_test.go @@ -232,6 +232,14 @@ func TestCustomizedDriver(t *testing.T) { "info_name": 2, "info_value": [0, "(unknown or development build)"] }, + { + "info_name": 3, + "info_value": [1, true] + }, + { + "info_name": 4, + "info_value": [1, false] + }, { "info_name": 100, "info_value": [0, "ADBC MockDriver Driver - Go"] @@ -506,6 +514,12 @@ func (c *connectionImpl) ListTableTypes(ctx context.Context) ([]string, error) { } func (c *connectionImpl) PrepareDriverInfo(ctx context.Context, infoCodes []adbc.InfoCode) error { + if err := c.ConnectionImplBase.DriverInfo.RegisterInfoCode(adbc.InfoVendorSql, true); err != nil { + return err + } + if err := c.ConnectionImplBase.DriverInfo.RegisterInfoCode(adbc.InfoVendorSubstrait, false); err != nil { + return err + } return c.ConnectionImplBase.DriverInfo.RegisterInfoCode(adbc.InfoCode(10_002), "this was fetched dynamically") } diff --git a/go/adbc/driver/snowflake/connection.go b/go/adbc/driver/snowflake/connection.go index 9bc3ef54fc..d992b6516c 100644 --- a/go/adbc/driver/snowflake/connection.go +++ b/go/adbc/driver/snowflake/connection.go @@ -83,6 +83,14 @@ type TableConstraint struct { skipRely bool } +// PrepareDriverInfo implements driverbase.DriverInfoPreparer. +func (c *connectionImpl) PrepareDriverInfo(ctx context.Context, infoCodes []adbc.InfoCode) error { + if err := c.ConnectionImplBase.DriverInfo.RegisterInfoCode(adbc.InfoVendorSql, true); err != nil { + return err + } + return c.ConnectionImplBase.DriverInfo.RegisterInfoCode(adbc.InfoVendorSubstrait, false) +} + // ListTableTypes implements driverbase.TableTypeLister. func (*connectionImpl) ListTableTypes(ctx context.Context) ([]string, error) { return []string{"BASE TABLE", "TEMPORARY TABLE", "VIEW"}, nil diff --git a/go/adbc/driver/snowflake/driver_test.go b/go/adbc/driver/snowflake/driver_test.go index 3f93dbdb58..aa5280437f 100644 --- a/go/adbc/driver/snowflake/driver_test.go +++ b/go/adbc/driver/snowflake/driver_test.go @@ -1794,6 +1794,39 @@ func (suite *SnowflakeTests) TestDescribeOnly() { suite.Truef(arrow.TypeEqual(&arrow.Decimal128Type{Precision: 6, Scale: 2}, schema.Field(0).Type), "expected decimal(6, 2), got %s", schema.Field(0).Type) } +func (suite *SnowflakeTests) TestAdditionalDriverInfo() { + rdr, err := suite.cnxn.GetInfo( + suite.ctx, + []adbc.InfoCode{ + adbc.InfoVendorSql, + adbc.InfoVendorSubstrait, + }, + ) + suite.Require().NoError(err) + + var totalRows int64 + for rdr.Next() { + rec := rdr.Record() + totalRows += rec.NumRows() + code := rec.Column(0).(*array.Uint32) + info := rec.Column(1).(*array.DenseUnion) + + for i := 0; i < int(rec.NumRows()); i++ { + if code.Value(i) == uint32(adbc.InfoVendorSql) { + arr, ok := info.Field(info.ChildID(i)).(*array.Boolean) + suite.Require().True(ok) + suite.Require().Equal(true, arr.Value(i)) + } + if code.Value(i) == uint32(adbc.InfoVendorSubstrait) { + arr, ok := info.Field(info.ChildID(i)).(*array.Boolean) + suite.Require().True(ok) + suite.Require().Equal(false, arr.Value(i)) + } + } + } + suite.Require().Equal(int64(2), totalRows) +} + func TestJwtAuthenticationUnencryptedValue(t *testing.T) { // test doesn't participate in SnowflakeTests because // JWT auth has a different behavior diff --git a/go/adbc/driver/snowflake/snowflake_database.go b/go/adbc/driver/snowflake/snowflake_database.go index 5c5f32b690..581d9733e4 100644 --- a/go/adbc/driver/snowflake/snowflake_database.go +++ b/go/adbc/driver/snowflake/snowflake_database.go @@ -155,7 +155,8 @@ func (d *databaseImpl) SetOptions(cnOptions map[string]string) error { } } - driverVersion, _ := d.DatabaseImplBase.DriverInfo.GetInfoDriverVersion() + dv, _ := d.DatabaseImplBase.DriverInfo.GetInfoForInfoCode(adbc.InfoDriverVersion) + driverVersion := dv.(string) defaultAppName := "[ADBC][Go-" + driverVersion + "]" // set default application name to track // unless user overrides it @@ -459,6 +460,7 @@ func (d *databaseImpl) Open(ctx context.Context) (adbc.Connection, error) { WithAutocommitSetter(conn). WithCurrentNamespacer(conn). WithTableTypeLister(conn). + WithDriverInfoPreparer(conn). Connection(), nil } diff --git a/go/adbc/drivermgr/adbc.h b/go/adbc/drivermgr/adbc.h index 1ec2f05080..26b8da13b8 100644 --- a/go/adbc/drivermgr/adbc.h +++ b/go/adbc/drivermgr/adbc.h @@ -459,6 +459,24 @@ const struct AdbcError* AdbcErrorFromArrayStream(struct ArrowArrayStream* stream /// /// \see AdbcConnectionGetInfo #define ADBC_INFO_VENDOR_ARROW_VERSION 2 +/// \brief Indicates whether SQL queries are supported (type: bool). +/// +/// \see AdbcConnectionGetInfo +#define ADBC_INFO_VENDOR_SQL 3 +/// \brief Indicates whether Substrait queries are supported (type: bool). +/// +/// \see AdbcConnectionGetInfo +#define ADBC_INFO_VENDOR_SUBSTRAIT 4 +/// \brief The minimum supported Substrait version, or null if +/// Substrait is not supported (type: utf8). +/// +/// \see AdbcConnectionGetInfo +#define ADBC_INFO_VENDOR_SUBSTRAIT_MIN_VERSION 5 +/// \brief The maximum supported Substrait version, or null if +/// Substrait is not supported (type: utf8). +/// +/// \see AdbcConnectionGetInfo +#define ADBC_INFO_VENDOR_SUBSTRAIT_MAX_VERSION 6 /// \brief The driver name (type: utf8). /// @@ -754,6 +772,24 @@ const struct AdbcError* AdbcErrorFromArrayStream(struct ArrowArrayStream* stream /// schema of the data to append (ADBC_STATUS_ALREADY_EXISTS). /// \since ADBC API revision 1.1.0 #define ADBC_INGEST_OPTION_MODE_CREATE_APPEND "adbc.ingest.mode.create_append" +/// \brief The catalog of the table for bulk insert. +/// +/// The type is char*. +#define ADBC_INGEST_OPTION_TARGET_CATALOG "adbc.ingest.target_catalog" +/// \brief The schema of the table for bulk insert. +/// +/// The type is char*. +#define ADBC_INGEST_OPTION_TARGET_DB_SCHEMA "adbc.ingest.target_db_schema" +/// \brief Use a temporary table for ingestion. +/// +/// The value should be ADBC_OPTION_VALUE_ENABLED or +/// ADBC_OPTION_VALUE_DISABLED (the default). +/// +/// This is not supported with ADBC_INGEST_OPTION_TARGET_CATALOG and +/// ADBC_INGEST_OPTION_TARGET_DB_SCHEMA. +/// +/// The type is char*. +#define ADBC_INGEST_OPTION_TEMPORARY "adbc.ingest.temporary" /// @} diff --git a/go/adbc/validation/validation.go b/go/adbc/validation/validation.go index ca594cba0f..9d73e6b436 100644 --- a/go/adbc/validation/validation.go +++ b/go/adbc/validation/validation.go @@ -329,6 +329,9 @@ func (c *ConnectionTests) TestMetadataGetInfo() { case 0: // String actual = child.(*array.String).Value(offset) + case 1: + // bool + actual = child.(*array.Boolean).Value(offset) case 2: // int64 actual = child.(*array.Int64).Value(offset) diff --git a/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcInfoCode.java b/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcInfoCode.java index 8d5c73ba9f..03d9abf4f4 100644 --- a/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcInfoCode.java +++ b/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcInfoCode.java @@ -29,6 +29,18 @@ public enum AdbcInfoCode { VENDOR_VERSION(1), /** The database vendor/product Arrow library version (type: utf8). */ VENDOR_ARROW_VERSION(2), + /** Indicates whether SQL queries are supported (type: bool). */ + VENDOR_SQL(3), + /** Indicates whether Substrait queries are supported (type: bool). */ + VENDOR_SUBSTRAIT(4), + /** + * The minimum supported Substrait version, or null if Substrait is not supported (type: utf8). + */ + VENDOR_SUBSTRAIT_MIN_VERSION(5), + /** + * The maximum supported Substrait version, or null if Substrait is not supported (type: utf8). + */ + VENDOR_SUBSTRAIT_MAX_VERSION(6), /** The driver name (type: utf8). */ DRIVER_NAME(100),