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

lidavidm 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 28c6d5ef57 feat(go/adbc/drivermgr): support typed option getters and 
setters (#4957)
28c6d5ef57 is described below

commit 28c6d5ef573e81bba268994794d05fa8a44eef7a
Author: Matt Topol <[email protected]>
AuthorDate: Thu Oct 29 10:12:27 2026 +0000

    feat(go/adbc/drivermgr): support typed option getters and setters (#4957)
    
    The Go driver manager wrapper implemented string `SetOption` on
    Database, Connection and Statement but had no option getters. This
    PR implements `adbc.GetSetOptions` on all three handle types.
---
 go/adbc/context_adapters.go              | 168 +++++++++++++++++++
 go/adbc/context_adapters_test.go         |  22 +++
 go/adbc/drivermgr/wrapper.go             | 277 +++++++++++++++++++++++++++++--
 go/adbc/drivermgr/wrapper_sqlite_test.go | 109 ++++++++++++
 4 files changed, 559 insertions(+), 17 deletions(-)

diff --git a/go/adbc/context_adapters.go b/go/adbc/context_adapters.go
index b0b55ddec6..210385a7e9 100644
--- a/go/adbc/context_adapters.go
+++ b/go/adbc/context_adapters.go
@@ -191,3 +191,171 @@ func (s *statementContextAdapter) Bind(ctx 
context.Context, values arrow.RecordB
 func (s *statementContextAdapter) BindStream(ctx context.Context, stream 
array.RecordReader) error {
        return s.stmt.BindStream(ctx, stream)
 }
+
+var (
+       _ GetSetOptionsWithContext = (*databaseContextAdapter)(nil)
+       _ GetSetOptionsWithContext = (*connectionContextAdapter)(nil)
+       _ GetSetOptionsWithContext = (*statementContextAdapter)(nil)
+)
+
+// The typed option methods below forward to the wrapped value's
+// GetSetOptions (or PostInitOptions for SetOption) when it implements them.
+// The context cannot be propagated since those methods don't accept one.
+// Unsupported getters return StatusNotFound and unsupported setters return
+// StatusNotImplemented, matching the GetSetOptions contract.
+
+func optionNotFound(key string) error {
+       return Error{Code: StatusNotFound, Msg: "option not found: " + key}
+}
+
+func optionNotImplemented(key string) error {
+       return Error{Code: StatusNotImplemented, Msg: "option not supported: " 
+ key}
+}
+
+func setStringOption(target any, key, value string) error {
+       if opts, ok := target.(PostInitOptions); ok {
+               return opts.SetOption(key, value)
+       }
+       return optionNotImplemented(key)
+}
+
+func setBytesOption(target any, key string, value []byte) error {
+       if opts, ok := target.(GetSetOptions); ok {
+               return opts.SetOptionBytes(key, value)
+       }
+       return optionNotImplemented(key)
+}
+
+func setIntOption(target any, key string, value int64) error {
+       if opts, ok := target.(GetSetOptions); ok {
+               return opts.SetOptionInt(key, value)
+       }
+       return optionNotImplemented(key)
+}
+
+func setDoubleOption(target any, key string, value float64) error {
+       if opts, ok := target.(GetSetOptions); ok {
+               return opts.SetOptionDouble(key, value)
+       }
+       return optionNotImplemented(key)
+}
+
+func getStringOption(target any, key string) (string, error) {
+       if opts, ok := target.(GetSetOptions); ok {
+               return opts.GetOption(key)
+       }
+       return "", optionNotFound(key)
+}
+
+func getBytesOption(target any, key string) ([]byte, error) {
+       if opts, ok := target.(GetSetOptions); ok {
+               return opts.GetOptionBytes(key)
+       }
+       return nil, optionNotFound(key)
+}
+
+func getIntOption(target any, key string) (int64, error) {
+       if opts, ok := target.(GetSetOptions); ok {
+               return opts.GetOptionInt(key)
+       }
+       return 0, optionNotFound(key)
+}
+
+func getDoubleOption(target any, key string) (float64, error) {
+       if opts, ok := target.(GetSetOptions); ok {
+               return opts.GetOptionDouble(key)
+       }
+       return 0, optionNotFound(key)
+}
+
+func (d *databaseContextAdapter) SetOption(_ context.Context, key, value 
string) error {
+       return setStringOption(d.db, key, value)
+}
+
+func (d *databaseContextAdapter) SetOptionBytes(_ context.Context, key string, 
value []byte) error {
+       return setBytesOption(d.db, key, value)
+}
+
+func (d *databaseContextAdapter) SetOptionInt(_ context.Context, key string, 
value int64) error {
+       return setIntOption(d.db, key, value)
+}
+
+func (d *databaseContextAdapter) SetOptionDouble(_ context.Context, key 
string, value float64) error {
+       return setDoubleOption(d.db, key, value)
+}
+
+func (d *databaseContextAdapter) GetOption(_ context.Context, key string) 
(string, error) {
+       return getStringOption(d.db, key)
+}
+
+func (d *databaseContextAdapter) GetOptionBytes(_ context.Context, key string) 
([]byte, error) {
+       return getBytesOption(d.db, key)
+}
+
+func (d *databaseContextAdapter) GetOptionInt(_ context.Context, key string) 
(int64, error) {
+       return getIntOption(d.db, key)
+}
+
+func (d *databaseContextAdapter) GetOptionDouble(_ context.Context, key 
string) (float64, error) {
+       return getDoubleOption(d.db, key)
+}
+
+func (c *connectionContextAdapter) SetOption(_ context.Context, key, value 
string) error {
+       return setStringOption(c.conn, key, value)
+}
+
+func (c *connectionContextAdapter) SetOptionBytes(_ context.Context, key 
string, value []byte) error {
+       return setBytesOption(c.conn, key, value)
+}
+
+func (c *connectionContextAdapter) SetOptionInt(_ context.Context, key string, 
value int64) error {
+       return setIntOption(c.conn, key, value)
+}
+
+func (c *connectionContextAdapter) SetOptionDouble(_ context.Context, key 
string, value float64) error {
+       return setDoubleOption(c.conn, key, value)
+}
+
+func (c *connectionContextAdapter) GetOption(_ context.Context, key string) 
(string, error) {
+       return getStringOption(c.conn, key)
+}
+
+func (c *connectionContextAdapter) GetOptionBytes(_ context.Context, key 
string) ([]byte, error) {
+       return getBytesOption(c.conn, key)
+}
+
+func (c *connectionContextAdapter) GetOptionInt(_ context.Context, key string) 
(int64, error) {
+       return getIntOption(c.conn, key)
+}
+
+func (c *connectionContextAdapter) GetOptionDouble(_ context.Context, key 
string) (float64, error) {
+       return getDoubleOption(c.conn, key)
+}
+
+func (s *statementContextAdapter) SetOptionBytes(_ context.Context, key 
string, value []byte) error {
+       return setBytesOption(s.stmt, key, value)
+}
+
+func (s *statementContextAdapter) SetOptionInt(_ context.Context, key string, 
value int64) error {
+       return setIntOption(s.stmt, key, value)
+}
+
+func (s *statementContextAdapter) SetOptionDouble(_ context.Context, key 
string, value float64) error {
+       return setDoubleOption(s.stmt, key, value)
+}
+
+func (s *statementContextAdapter) GetOption(_ context.Context, key string) 
(string, error) {
+       return getStringOption(s.stmt, key)
+}
+
+func (s *statementContextAdapter) GetOptionBytes(_ context.Context, key 
string) ([]byte, error) {
+       return getBytesOption(s.stmt, key)
+}
+
+func (s *statementContextAdapter) GetOptionInt(_ context.Context, key string) 
(int64, error) {
+       return getIntOption(s.stmt, key)
+}
+
+func (s *statementContextAdapter) GetOptionDouble(_ context.Context, key 
string) (float64, error) {
+       return getDoubleOption(s.stmt, key)
+}
diff --git a/go/adbc/context_adapters_test.go b/go/adbc/context_adapters_test.go
index 61362c5e8b..85ce27e109 100644
--- a/go/adbc/context_adapters_test.go
+++ b/go/adbc/context_adapters_test.go
@@ -318,3 +318,25 @@ func TestAsStatementContext_Nil(t *testing.T) {
        stmtCtx := adbc.AsStatementContext(nil)
        assert.Nil(t, stmtCtx)
 }
+
+func TestContextAdaptersWithoutGetSetOptions(t *testing.T) {
+       ctx := context.Background()
+       for name, opts := range map[string]adbc.GetSetOptionsWithContext{
+               "database":   
adbc.AsDatabaseContext(&mockDatabase{}).(adbc.GetSetOptionsWithContext),
+               "connection": 
adbc.AsConnectionContext(&mockConnection{}).(adbc.GetSetOptionsWithContext),
+               "statement":  
adbc.AsStatementContext(&mockStatement{}).(adbc.GetSetOptionsWithContext),
+       } {
+               t.Run(name, func(t *testing.T) {
+                       var adbcErr adbc.Error
+                       _, err := opts.GetOption(ctx, "key")
+                       assert.ErrorAs(t, err, &adbcErr)
+                       assert.Equal(t, adbc.StatusNotFound, adbcErr.Code)
+                       _, err = opts.GetOptionInt(ctx, "key")
+                       assert.ErrorAs(t, err, &adbcErr)
+                       assert.Equal(t, adbc.StatusNotFound, adbcErr.Code)
+                       err = opts.SetOptionDouble(ctx, "key", 1.5)
+                       assert.ErrorAs(t, err, &adbcErr)
+                       assert.Equal(t, adbc.StatusNotImplemented, adbcErr.Code)
+               })
+       }
+}
diff --git a/go/adbc/drivermgr/wrapper.go b/go/adbc/drivermgr/wrapper.go
index 2bbe1eaba7..fc95d2529b 100644
--- a/go/adbc/drivermgr/wrapper.go
+++ b/go/adbc/drivermgr/wrapper.go
@@ -30,6 +30,24 @@ package drivermgr
 // #include <stdlib.h>
 // #include <string.h>
 //
+// /* Dispatch typed option calls without duplicating buffer management for 
each handle.
+//    0 = database, 1 = connection, 2 = statement. */
+// #define OPTION_ARGS(...) __VA_ARGS__
+// #define OPTION_DISPATCH(name, suffix, signature, ...) \
+// static AdbcStatusCode option_##name(int kind, void* handle, OPTION_ARGS 
signature) { \
+//     switch (kind) { \
+//     case 0: return AdbcDatabase##suffix((struct AdbcDatabase*)handle, 
__VA_ARGS__); \
+//     case 1: return AdbcConnection##suffix((struct AdbcConnection*)handle, 
__VA_ARGS__); \
+//     default: return AdbcStatement##suffix((struct AdbcStatement*)handle, 
__VA_ARGS__); \
+//     } \
+// }
+// OPTION_DISPATCH(get_string, GetOption, (const char* key, char* value, 
size_t* length, struct AdbcError* error), key, value, length, error)
+// OPTION_DISPATCH(get_bytes, GetOptionBytes, (const char* key, uint8_t* 
value, size_t* length, struct AdbcError* error), key, value, length, error)
+// OPTION_DISPATCH(get_int, GetOptionInt, (const char* key, int64_t* value, 
struct AdbcError* error), key, value, error)
+// OPTION_DISPATCH(get_double, GetOptionDouble, (const char* key, double* 
value, struct AdbcError* error), key, value, error)
+// OPTION_DISPATCH(set_bytes, SetOptionBytes, (const char* key, const uint8_t* 
value, size_t length, struct AdbcError* error), key, value, length, error)
+// OPTION_DISPATCH(set_int, SetOptionInt, (const char* key, int64_t value, 
struct AdbcError* error), key, value, error)
+// OPTION_DISPATCH(set_double, SetOptionDouble, (const char* key, double 
value, struct AdbcError* error), key, value, error)
 // void releaseErr(struct AdbcError* err) {
 //     if (err->release != NULL) {
 //         err->release(err);
@@ -61,6 +79,12 @@ import (
        "github.com/apache/arrow-go/v18/arrow/cdata"
 )
 
+var (
+       _ adbc.GetSetOptions = (*Database)(nil)
+       _ adbc.GetSetOptions = (*cnxn)(nil)
+       _ adbc.GetSetOptions = (*stmt)(nil)
+)
+
 const (
        LoadFlagsSearchEnv = 1 << iota
        LoadFlagsSearchPath
@@ -195,6 +219,126 @@ func toAdbcError(code adbc.Status, e *C.struct_AdbcError) 
error {
        return err
 }
 
+// getBufferOption uses the ADBC 1.1 length protocol: the first call writes
+// nothing if the buffer is too short and reports the required length.
+func getBufferOption(kind C.int, handle unsafe.Pointer, key string, bytes 
bool) ([]byte, error) {
+       ckey := C.CString(key)
+       defer C.free(unsafe.Pointer(ckey))
+
+       value := make([]byte, 128)
+       for attempt := 0; attempt < 2; attempt++ {
+               length := C.size_t(len(value))
+               var err C.struct_AdbcError
+               var code adbc.Status
+               if bytes {
+                       code = adbc.Status(C.option_get_bytes(kind, handle, 
ckey, (*C.uint8_t)(unsafe.Pointer(&value[0])), &length, &err))
+               } else {
+                       code = adbc.Status(C.option_get_string(kind, handle, 
ckey, (*C.char)(unsafe.Pointer(&value[0])), &length, &err))
+               }
+               if code != adbc.StatusOK {
+                       return nil, toAdbcError(code, &err)
+               }
+               if length <= C.size_t(len(value)) {
+                       return value[:int(length)], nil
+               }
+               if attempt == 0 {
+                       value = make([]byte, int(length))
+               }
+       }
+       return nil, adbc.Error{Code: adbc.StatusInvalidState, Msg: "option 
length changed while reading"}
+}
+
+func getStringOption(kind C.int, handle unsafe.Pointer, key string) (string, 
error) {
+       value, err := getBufferOption(kind, handle, key, false)
+       if err != nil {
+               return "", err
+       }
+       if len(value) == 0 || value[len(value)-1] != 0 {
+               return "", adbc.Error{Code: adbc.StatusInvalidData, Msg: 
"option string is not NUL-terminated"}
+       }
+       return string(value[:len(value)-1]), nil
+}
+
+func getBytesOption(kind C.int, handle unsafe.Pointer, key string) ([]byte, 
error) {
+       return getBufferOption(kind, handle, key, true)
+}
+
+func getIntOption(kind C.int, handle unsafe.Pointer, key string) (int64, 
error) {
+       ckey := C.CString(key)
+       defer C.free(unsafe.Pointer(ckey))
+       var value C.int64_t
+       var err C.struct_AdbcError
+       if code := adbc.Status(C.option_get_int(kind, handle, ckey, &value, 
&err)); code != adbc.StatusOK {
+               return 0, toAdbcError(code, &err)
+       }
+       return int64(value), nil
+}
+
+func getDoubleOption(kind C.int, handle unsafe.Pointer, key string) (float64, 
error) {
+       ckey := C.CString(key)
+       defer C.free(unsafe.Pointer(ckey))
+       var value C.double
+       var err C.struct_AdbcError
+       if code := adbc.Status(C.option_get_double(kind, handle, ckey, &value, 
&err)); code != adbc.StatusOK {
+               return 0, toAdbcError(code, &err)
+       }
+       return float64(value), nil
+}
+
+func setStringOption(kind C.int, handle unsafe.Pointer, key, value string) 
error {
+       ckey, cvalue := C.CString(key), C.CString(value)
+       defer C.free(unsafe.Pointer(ckey))
+       defer C.free(unsafe.Pointer(cvalue))
+       var err C.struct_AdbcError
+       var code adbc.Status
+       switch kind {
+       case 0:
+               code = 
adbc.Status(C.AdbcDatabaseSetOption((*C.struct_AdbcDatabase)(handle), ckey, 
cvalue, &err))
+       case 1:
+               code = 
adbc.Status(C.AdbcConnectionSetOption((*C.struct_AdbcConnection)(handle), ckey, 
cvalue, &err))
+       default:
+               code = 
adbc.Status(C.AdbcStatementSetOption((*C.struct_AdbcStatement)(handle), ckey, 
cvalue, &err))
+       }
+       if code != adbc.StatusOK {
+               return toAdbcError(code, &err)
+       }
+       return nil
+}
+
+func setBytesOption(kind C.int, handle unsafe.Pointer, key string, value 
[]byte) error {
+       ckey := C.CString(key)
+       defer C.free(unsafe.Pointer(ckey))
+       var ptr *C.uint8_t
+       if len(value) > 0 {
+               ptr = (*C.uint8_t)(unsafe.Pointer(&value[0]))
+       }
+       var err C.struct_AdbcError
+       if code := adbc.Status(C.option_set_bytes(kind, handle, ckey, ptr, 
C.size_t(len(value)), &err)); code != adbc.StatusOK {
+               return toAdbcError(code, &err)
+       }
+       return nil
+}
+
+func setIntOption(kind C.int, handle unsafe.Pointer, key string, value int64) 
error {
+       ckey := C.CString(key)
+       defer C.free(unsafe.Pointer(ckey))
+       var err C.struct_AdbcError
+       if code := adbc.Status(C.option_set_int(kind, handle, ckey, 
C.int64_t(value), &err)); code != adbc.StatusOK {
+               return toAdbcError(code, &err)
+       }
+       return nil
+}
+
+func setDoubleOption(kind C.int, handle unsafe.Pointer, key string, value 
float64) error {
+       ckey := C.CString(key)
+       defer C.free(unsafe.Pointer(ckey))
+       var err C.struct_AdbcError
+       if code := adbc.Status(C.option_set_double(kind, handle, ckey, 
C.double(value), &err)); code != adbc.StatusOK {
+               return toAdbcError(code, &err)
+       }
+       return nil
+}
+
 func (d *Database) SetOptions(options map[string]string) error {
        if d.options == nil {
                d.options = make(map[string]option)
@@ -216,6 +360,65 @@ func (d *Database) SetOptions(options map[string]string) 
error {
        return nil
 }
 
+func (d *Database) SetOption(key, value string) error {
+       if d.db == nil {
+               return d.SetOptions(map[string]string{key: value})
+       }
+       return setStringOption(0, unsafe.Pointer(d.db), key, value)
+}
+
+func (d *Database) SetOptionBytes(key string, value []byte) error {
+       if d.db == nil {
+               return adbc.Error{Code: adbc.StatusInvalidState, Msg: "database 
not initialized"}
+       }
+       return setBytesOption(0, unsafe.Pointer(d.db), key, value)
+}
+
+func (d *Database) SetOptionInt(key string, value int64) error {
+       if d.db == nil {
+               return d.SetOption(key, strconv.FormatInt(value, 10))
+       }
+       return setIntOption(0, unsafe.Pointer(d.db), key, value)
+}
+
+func (d *Database) SetOptionDouble(key string, value float64) error {
+       if d.db == nil {
+               return d.SetOption(key, strconv.FormatFloat(value, 'g', -1, 64))
+       }
+       return setDoubleOption(0, unsafe.Pointer(d.db), key, value)
+}
+
+func (d *Database) GetOption(key string) (string, error) {
+       if d.db == nil {
+               if option, ok := d.options[key]; ok {
+                       return C.GoString(option.val), nil
+               }
+               return "", adbc.Error{Code: adbc.StatusNotFound, Msg: "option 
not found: " + key}
+       }
+       return getStringOption(0, unsafe.Pointer(d.db), key)
+}
+
+func (d *Database) GetOptionBytes(key string) ([]byte, error) {
+       if d.db == nil {
+               return nil, adbc.Error{Code: adbc.StatusInvalidState, Msg: 
"database not initialized"}
+       }
+       return getBytesOption(0, unsafe.Pointer(d.db), key)
+}
+
+func (d *Database) GetOptionInt(key string) (int64, error) {
+       if d.db == nil {
+               return 0, adbc.Error{Code: adbc.StatusInvalidState, Msg: 
"database not initialized"}
+       }
+       return getIntOption(0, unsafe.Pointer(d.db), key)
+}
+
+func (d *Database) GetOptionDouble(key string) (float64, error) {
+       if d.db == nil {
+               return 0, adbc.Error{Code: adbc.StatusInvalidState, Msg: 
"database not initialized"}
+       }
+       return getDoubleOption(0, unsafe.Pointer(d.db), key)
+}
+
 func (d *Database) Open(context.Context) (adbc.Connection, error) {
        var err C.struct_AdbcError
 
@@ -486,15 +689,35 @@ func (c *cnxn) ReadPartition(_ context.Context, 
serializedPartition []byte) (arr
 }
 
 func (c *cnxn) SetOption(key, value string) error {
-       ckey, cvalue := C.CString(key), C.CString(value)
-       defer C.free(unsafe.Pointer(ckey))
-       defer C.free(unsafe.Pointer(cvalue))
+       return setStringOption(1, unsafe.Pointer(c.conn), key, value)
+}
 
-       var err C.struct_AdbcError
-       if code := adbc.Status(C.AdbcConnectionSetOption(c.conn, ckey, cvalue, 
&err)); code != adbc.StatusOK {
-               return toAdbcError(code, &err)
-       }
-       return nil
+func (c *cnxn) SetOptionBytes(key string, value []byte) error {
+       return setBytesOption(1, unsafe.Pointer(c.conn), key, value)
+}
+
+func (c *cnxn) SetOptionInt(key string, value int64) error {
+       return setIntOption(1, unsafe.Pointer(c.conn), key, value)
+}
+
+func (c *cnxn) SetOptionDouble(key string, value float64) error {
+       return setDoubleOption(1, unsafe.Pointer(c.conn), key, value)
+}
+
+func (c *cnxn) GetOption(key string) (string, error) {
+       return getStringOption(1, unsafe.Pointer(c.conn), key)
+}
+
+func (c *cnxn) GetOptionBytes(key string) ([]byte, error) {
+       return getBytesOption(1, unsafe.Pointer(c.conn), key)
+}
+
+func (c *cnxn) GetOptionInt(key string) (int64, error) {
+       return getIntOption(1, unsafe.Pointer(c.conn), key)
+}
+
+func (c *cnxn) GetOptionDouble(key string) (float64, error) {
+       return getDoubleOption(1, unsafe.Pointer(c.conn), key)
 }
 
 type stmt struct {
@@ -511,16 +734,36 @@ func (s *stmt) Close() error {
        return nil
 }
 
-func (s *stmt) SetOption(key, val string) error {
-       ckey, cvalue := C.CString(key), C.CString(val)
-       defer C.free(unsafe.Pointer(ckey))
-       defer C.free(unsafe.Pointer(cvalue))
+func (s *stmt) SetOption(key, value string) error {
+       return setStringOption(2, unsafe.Pointer(s.st), key, value)
+}
 
-       var err C.struct_AdbcError
-       if code := adbc.Status(C.AdbcStatementSetOption(s.st, ckey, cvalue, 
&err)); code != adbc.StatusOK {
-               return toAdbcError(code, &err)
-       }
-       return nil
+func (s *stmt) SetOptionBytes(key string, value []byte) error {
+       return setBytesOption(2, unsafe.Pointer(s.st), key, value)
+}
+
+func (s *stmt) SetOptionInt(key string, value int64) error {
+       return setIntOption(2, unsafe.Pointer(s.st), key, value)
+}
+
+func (s *stmt) SetOptionDouble(key string, value float64) error {
+       return setDoubleOption(2, unsafe.Pointer(s.st), key, value)
+}
+
+func (s *stmt) GetOption(key string) (string, error) {
+       return getStringOption(2, unsafe.Pointer(s.st), key)
+}
+
+func (s *stmt) GetOptionBytes(key string) ([]byte, error) {
+       return getBytesOption(2, unsafe.Pointer(s.st), key)
+}
+
+func (s *stmt) GetOptionInt(key string) (int64, error) {
+       return getIntOption(2, unsafe.Pointer(s.st), key)
+}
+
+func (s *stmt) GetOptionDouble(key string) (float64, error) {
+       return getDoubleOption(2, unsafe.Pointer(s.st), key)
 }
 
 func (s *stmt) SetSqlQuery(query string) error {
diff --git a/go/adbc/drivermgr/wrapper_sqlite_test.go 
b/go/adbc/drivermgr/wrapper_sqlite_test.go
index 5f926fb8ff..463e0ce52f 100644
--- a/go/adbc/drivermgr/wrapper_sqlite_test.go
+++ b/go/adbc/drivermgr/wrapper_sqlite_test.go
@@ -433,6 +433,115 @@ func (dm *DriverMgrSuite) TestCommit() {
        dm.ErrorContains(err, "No active transaction, cannot commit")
 }
 
+func (dm *DriverMgrSuite) TestGetSetOptions() {
+       opts := dm.conn.(adbc.GetSetOptions)
+       value, err := opts.GetOption(adbc.OptionKeyAutoCommit)
+       dm.Require().NoError(err)
+       dm.Equal(adbc.OptionValueEnabled, value)
+
+       dm.Require().NoError(opts.SetOption(adbc.OptionKeyAutoCommit, 
adbc.OptionValueDisabled))
+       value, err = opts.GetOption(adbc.OptionKeyAutoCommit)
+       dm.Require().NoError(err)
+       dm.Equal(adbc.OptionValueDisabled, value)
+
+       _, err = opts.GetOption("unknown.option")
+       var adbcErr adbc.Error
+       dm.Require().ErrorAs(err, &adbcErr)
+       dm.Equal(adbc.StatusNotFound, adbcErr.Code)
+
+       _, err = opts.GetOptionBytes("unknown.option")
+       dm.Require().ErrorAs(err, &adbcErr)
+       dm.Equal(adbc.StatusNotFound, adbcErr.Code)
+       _, err = opts.GetOptionInt("unknown.option")
+       dm.Require().ErrorAs(err, &adbcErr)
+       dm.Equal(adbc.StatusNotFound, adbcErr.Code)
+
+       _, err = opts.GetOptionDouble("unknown.option")
+       dm.Require().ErrorAs(err, &adbcErr)
+       dm.Equal(adbc.StatusNotFound, adbcErr.Code)
+
+       err = opts.SetOptionBytes("unknown.option", []byte{1, 2})
+       dm.Require().ErrorAs(err, &adbcErr)
+       dm.Equal(adbc.StatusNotImplemented, adbcErr.Code)
+
+       err = opts.SetOptionDouble("unknown.option", 1.5)
+       dm.Require().ErrorAs(err, &adbcErr)
+       dm.Equal(adbc.StatusNotImplemented, adbcErr.Code)
+
+       stmt, err := dm.conn.NewStatement()
+       dm.Require().NoError(err)
+       defer validation.CheckedClose(dm.T(), stmt)
+       stmtOpts := stmt.(adbc.GetSetOptions)
+       
dm.Require().NoError(stmtOpts.SetOptionInt("adbc.sqlite.query.batch_rows", 42))
+       count, err := stmtOpts.GetOptionInt("adbc.sqlite.query.batch_rows")
+       dm.Require().NoError(err)
+       dm.Equal(int64(42), count)
+}
+
+func (dm *DriverMgrSuite) TestGetSetOptionsWithContext() {
+       dbOpts := adbc.AsDatabaseContext(dm.db).(adbc.GetSetOptionsWithContext)
+       count, err := dbOpts.GetOptionInt(dm.ctx, 
"adbc.sqlite.query.batch_rows")
+       dm.Require().NoError(err)
+       dm.Equal(int64(1024), count)
+
+       cnxn, err := dm.db.Open(dm.ctx)
+       dm.Require().NoError(err)
+       defer validation.CheckedClose(dm.T(), cnxn)
+
+       connCtx := adbc.AsConnectionContext(cnxn)
+       connOpts := connCtx.(adbc.GetSetOptionsWithContext)
+       dm.Require().NoError(connOpts.SetOption(dm.ctx, 
adbc.OptionKeyAutoCommit, adbc.OptionValueDisabled))
+       value, err := connOpts.GetOption(dm.ctx, adbc.OptionKeyAutoCommit)
+       dm.Require().NoError(err)
+       dm.Equal(adbc.OptionValueDisabled, value)
+
+       _, err = connOpts.GetOption(dm.ctx, "unknown.option")
+       var adbcErr adbc.Error
+       dm.Require().ErrorAs(err, &adbcErr)
+       dm.Equal(adbc.StatusNotFound, adbcErr.Code)
+
+       stmt, err := connCtx.NewStatement(dm.ctx)
+       dm.Require().NoError(err)
+       defer func() { dm.NoError(stmt.Close(dm.ctx)) }()
+       stmtOpts := stmt.(adbc.GetSetOptionsWithContext)
+       dm.Require().NoError(stmtOpts.SetOptionInt(dm.ctx, 
"adbc.sqlite.query.batch_rows", 7))
+       count, err = stmtOpts.GetOptionInt(dm.ctx, 
"adbc.sqlite.query.batch_rows")
+       dm.Require().NoError(err)
+       dm.Equal(int64(7), count)
+}
+
+func TestGetOptionLongValue(t *testing.T) {
+       uri := "file:" + strings.Repeat("a", 150) + "?mode=memory"
+       db, err := (drivermgr.Driver{}).NewDatabase(map[string]string{
+               "driver": "adbc_driver_sqlite",
+               "uri":    uri,
+       })
+       require.NoError(t, err)
+       defer validation.CheckedClose(t, db)
+
+       value, err := db.(adbc.GetSetOptions).GetOption("uri")
+       require.NoError(t, err)
+       assert.Equal(t, uri, value)
+       count, err := 
db.(adbc.GetSetOptions).GetOptionInt("adbc.sqlite.query.batch_rows")
+       require.NoError(t, err)
+       assert.Equal(t, int64(1024), count)
+}
+
+func TestDatabaseOptionsBeforeInitialization(t *testing.T) {
+       db := &drivermgr.Database{}
+       require.NoError(t, db.SetOptions(map[string]string{"example": "value"}))
+       defer validation.CheckedClose(t, db)
+
+       got, err := db.GetOption("example")
+       require.NoError(t, err)
+       assert.Equal(t, "value", got)
+
+       _, err = db.GetOption("missing")
+       var adbcErr adbc.Error
+       require.ErrorAs(t, err, &adbcErr)
+       assert.Equal(t, adbc.StatusNotFound, adbcErr.Code)
+}
+
 func (dm *DriverMgrSuite) TestCommitAutocommitDisabled() {
        cnxnopt, ok := dm.conn.(adbc.PostInitOptions)
        dm.True(ok)

Reply via email to