This is an automated email from the ASF dual-hosted git repository.

zeroshade 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 4f7fc8417 feat(go/adbc): add GetDriverInfo helper (#3239)
4f7fc8417 is described below

commit 4f7fc8417e55a355dd9acd3d93fa042d50b60b22
Author: Mandukhai Alimaa <[email protected]>
AuthorDate: Wed Aug 13 03:23:01 2025 +0800

    feat(go/adbc): add GetDriverInfo helper (#3239)
    
    Closes #3238
    
    ---------
    
    Co-authored-by: MANDY Alimaa <[email protected]>
---
 go/adbc/drivermgr/wrapper_sqlite_test.go |   9 +++
 go/adbc/ext.go                           | 130 +++++++++++++++++++++++++++++++
 2 files changed, 139 insertions(+)

diff --git a/go/adbc/drivermgr/wrapper_sqlite_test.go 
b/go/adbc/drivermgr/wrapper_sqlite_test.go
index abc44c819..779371c22 100644
--- a/go/adbc/drivermgr/wrapper_sqlite_test.go
+++ b/go/adbc/drivermgr/wrapper_sqlite_test.go
@@ -683,3 +683,12 @@ func (dm *DriverMgrSuite) TestIngestStream() {
        dm.Equal(int64(5), cntArr.Value(0), "table should contain 5 rows")
        dm.False(rdr2.Next(), "no more rows expected")
 }
+
+func (dm *DriverMgrSuite) TestGetDriverInfo() {
+       driverInfo, err := adbc.GetDriverInfo(dm.ctx, dm.conn)
+       dm.NoError(err)
+
+       // Core driver info should be non-empty
+       dm.NotEmpty(driverInfo.DriverName, "DriverName should not be empty")
+       dm.NotEmpty(driverInfo.DriverVersion, "DriverVersion should not be 
empty")
+}
diff --git a/go/adbc/ext.go b/go/adbc/ext.go
index f2f3f4a2f..bea3d7e24 100644
--- a/go/adbc/ext.go
+++ b/go/adbc/ext.go
@@ -22,6 +22,8 @@ import (
        "errors"
        "fmt"
        "log/slog"
+       "strconv"
+       "strings"
 
        "github.com/apache/arrow-go/v18/arrow/array"
        "go.opentelemetry.io/otel/attribute"
@@ -159,3 +161,131 @@ func IngestStream(ctx context.Context, cnxn Connection, 
reader array.RecordReade
 
        return count, nil
 }
+
+// DriverInfo library info map keys for auxiliary information
+//
+// NOTE: If in the future any of these InfoCodes are promoted to top-level 
fields
+// in the DriverInfo struct, their values should still also be included in the
+// LibraryInfo map to maintain backward compatibility. This ensures older 
clients
+// relying on LibraryInfo won't break when new fields are introduced.
+const (
+       DriverInfoKeyDriverArrowVersion        = "driver_arrow_version"
+       DriverInfoKeyVendorArrowVersion        = "vendor_arrow_version"
+       DriverInfoKeyVendorSQLSupport          = "vendor_sql_support"
+       DriverInfoKeyVendorSubstraitSupport    = "vendor_substrait_support"
+       DriverInfoKeyVendorSubstraitMinVersion = "vendor_substrait_min_version"
+       DriverInfoKeyVendorSubstraitMaxVersion = "vendor_substrait_max_version"
+)
+
+// DriverInfo contains comprehensive driver and library version information.
+type DriverInfo struct {
+       DriverName        string            `json:"driver_name"`         // 
e.g., "ADBC PostgreSQL Driver"
+       DriverVersion     string            `json:"driver_version"`      // 
e.g., "1.7.0"
+       VendorName        string            `json:"vendor_name"`         // 
e.g., "PostgreSQL"
+       VendorVersion     string            `json:"vendor_version"`      // 
e.g., "15.3"
+       DriverADBCVersion int64             `json:"driver_adbc_version"` // 
ADBC API version number
+       LibraryInfo       map[string]string `json:"library_info"`        // 
Additional library versions, protocol info, etc.
+}
+
+// GetDriverInfo retrieves comprehensive driver version information from a 
connection.
+// This helper function encapsulates the complex process of querying driver 
info codes,
+// handling streaming record batches and union arrays, extracting and safely 
cloning
+// string values, and managing errors with fallback defaults.
+//
+// Returns detailed version information including driver name, version, and 
additional
+// library information such as Arrow version, vendor details, and ADBC version.
+//
+// This is not part of the ADBC API specification.
+func GetDriverInfo(ctx context.Context, cnxn Connection) (DriverInfo, error) {
+       stream, err := cnxn.GetInfo(ctx, nil)
+       if err != nil {
+               return DriverInfo{}, fmt.Errorf("error during GetInfo: %w", err)
+       }
+       defer stream.Release()
+
+       driverInfo := DriverInfo{
+               LibraryInfo: make(map[string]string),
+       }
+
+       for stream.Next() {
+               batch := stream.Record()
+               codeArr := batch.Column(0).(*array.Uint32)
+               unionArr := batch.Column(1).(*array.DenseUnion)
+
+               codes, offsets := codeArr.Values(), unionArr.RawValueOffsets()
+               for i := range int(batch.NumRows()) {
+                       code, offset := InfoCode(codes[i]), int(offsets[i])
+                       child := unionArr.Field(unionArr.ChildID(i))
+
+                       if child.IsNull(offset) {
+                               continue
+                       }
+
+                       // Handle different union types based on child ID 
(similar to validation tests)
+                       switch unionArr.ChildID(i) {
+                       case 0: // String values
+                               strArray, ok := child.(*array.String)
+                               if !ok {
+                                       continue
+                               }
+                               // Create a copy of the string to avoid memory 
corruption issues.
+                               // Arrow strings reference memory owned by the 
record batch. When the batch
+                               // is released, this memory becomes invalid, 
leading to potential crashes or
+                               // data corruption when accessing the string 
later. Cloning ensures we have
+                               // an independent copy that remains valid after 
batch cleanup.
+                               val := strings.Clone(strArray.Value(offset))
+
+                               switch code {
+                               case InfoDriverName:
+                                       driverInfo.DriverName = val
+                               case InfoDriverVersion:
+                                       driverInfo.DriverVersion = val
+                               case InfoDriverArrowVersion:
+                                       
driverInfo.LibraryInfo[DriverInfoKeyDriverArrowVersion] = val
+                               case InfoVendorName:
+                                       driverInfo.VendorName = val
+                               case InfoVendorVersion:
+                                       driverInfo.VendorVersion = val
+                               case InfoVendorArrowVersion:
+                                       
driverInfo.LibraryInfo[DriverInfoKeyVendorArrowVersion] = val
+                               case InfoVendorSubstraitMinVersion:
+                                       
driverInfo.LibraryInfo[DriverInfoKeyVendorSubstraitMinVersion] = val
+                               case InfoVendorSubstraitMaxVersion:
+                                       
driverInfo.LibraryInfo[DriverInfoKeyVendorSubstraitMaxVersion] = val
+                               }
+
+                       case 1: // Boolean values
+                               boolArray, ok := child.(*array.Boolean)
+                               if !ok {
+                                       continue
+                               }
+                               val := boolArray.Value(offset)
+
+                               switch code {
+                               case InfoVendorSql:
+                                       
driverInfo.LibraryInfo[DriverInfoKeyVendorSQLSupport] = strconv.FormatBool(val)
+                               case InfoVendorSubstrait:
+                                       
driverInfo.LibraryInfo[DriverInfoKeyVendorSubstraitSupport] = 
strconv.FormatBool(val)
+                               }
+
+                       case 2: // Int64 values
+                               int64Array, ok := child.(*array.Int64)
+                               if !ok {
+                                       continue
+                               }
+                               val := int64Array.Value(offset)
+
+                               switch code {
+                               case InfoDriverADBCVersion:
+                                       driverInfo.DriverADBCVersion = val
+                               }
+                       }
+               }
+       }
+
+       if err := stream.Err(); err != nil {
+               return DriverInfo{}, fmt.Errorf("error reading info stream: 
%w", err)
+       }
+
+       return driverInfo, nil
+}

Reply via email to