This is an automated email from the ASF dual-hosted git repository.
joellubi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-adbc.git
The following commit(s) were added to refs/heads/main by this push:
new aa04aadcc feat(format): add info codes for supported capabilities
(#1649)
aa04aadcc is described below
commit aa04aadccd319e6fa3abb07154fa8d87b58d5c21
Author: Joel Lubinitsky <[email protected]>
AuthorDate: Fri Apr 12 08:38:01 2024 -0400
feat(format): add info codes for supported capabilities (#1649)
Add the following info_codes to the C, Go, and Java ABDC definitions:
- ADBC_INFO_VENDOR_SQL
- ADBC_INFO_VENDOR_SUBSTRAIT
- ADBC_INFO_VENDOR_SUBSTRAIT_MIN_VERSION
- ADBC_INFO_VENDOR_SUBSTRAIT_MAX_VERSION
Also:
- Add support for new info_codes to flightsql and snowflake drivers
- Refactor driverbase to simplify driverinfo and make it more extensible
---
adbc.h | 36 ++++++
c/driver/common/options.h | 21 ----
go/adbc/adbc.go | 12 +-
go/adbc/driver/flightsql/flightsql_connection.go | 50 +++++---
go/adbc/driver/flightsql/flightsql_database.go | 6 +-
go/adbc/driver/internal/driverbase/connection.go | 80 +++++--------
go/adbc/driver/internal/driverbase/driver_info.go | 127 ++++++---------------
.../driver/internal/driverbase/driver_info_test.go | 20 ++--
go/adbc/driver/internal/driverbase/driver_test.go | 14 +++
go/adbc/driver/snowflake/connection.go | 8 ++
go/adbc/driver/snowflake/driver_test.go | 33 ++++++
go/adbc/driver/snowflake/snowflake_database.go | 4 +-
go/adbc/drivermgr/adbc.h | 36 ++++++
go/adbc/validation/validation.go | 3 +
.../org/apache/arrow/adbc/core/AdbcInfoCode.java | 12 ++
15 files changed, 266 insertions(+), 196 deletions(-)
diff --git a/adbc.h b/adbc.h
index 1ec2f0508..26b8da13b 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 f42bb0904..ef4f1cf53 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 6968faacf..9f50935ac 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 83807856e..a17d8f2e3 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 9f0848c3f..21302c2cb 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 fc1fc0d36..1745d3d19 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 e68aa16c2..7f98082b8 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 2bad25d05..b253c1b7a 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 309c529c1..3d0b579bd 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 9bc3ef54f..d992b6516 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 3f93dbdb5..aa5280437 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 5c5f32b69..581d9733e 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 1ec2f0508..26b8da13b 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 ca594cba0..9d73e6b43 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 8d5c73ba9..03d9abf4f 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),