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)