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),

Reply via email to