From ddbfaeccba2be01fe0e54cacd29c058fdf5359e3 Mon Sep 17 00:00:00 2001
From: Solomon Choe <128758390+ywc88@users.noreply.github.com>
Date: Tue, 22 Aug 2023 11:42:44 -0700
Subject: [PATCH 01/20] fix(go/adbc/driver/flightsql): Have GetTableSchema
check for table name match instead of the first schema it receives (#980)
Fixes #934.
---
go/adbc/driver/flightsql/flightsql_adbc.go | 42 ++++++---
.../flightsql/flightsql_adbc_server_test.go | 92 +++++++++++++++++++
2 files changed, 122 insertions(+), 12 deletions(-)
diff --git a/go/adbc/driver/flightsql/flightsql_adbc.go b/go/adbc/driver/flightsql/flightsql_adbc.go
index 1ae99a6a55..e00310cfc3 100644
--- a/go/adbc/driver/flightsql/flightsql_adbc.go
+++ b/go/adbc/driver/flightsql/flightsql_adbc.go
@@ -1231,24 +1231,42 @@ func (c *cnxn) GetTableSchema(ctx context.Context, catalog *string, dbSchema *st
return nil, adbcFromFlightStatus(err, "GetTableSchema(DoGet)")
}
- if rec.NumRows() == 0 {
+ numRows := rec.NumRows()
+ switch {
+ case numRows == 0:
return nil, adbc.Error{
Code: adbc.StatusNotFound,
}
+ case numRows > math.MaxInt32:
+ return nil, adbc.Error{
+ Msg: "[Flight SQL] GetTableSchema cannot handle tables with number of rows > 2^31 - 1",
+ Code: adbc.StatusNotImplemented,
+ }
}
- // returned schema should be
- // 0: catalog_name: utf8
- // 1: db_schema_name: utf8
- // 2: table_name: utf8 not null
- // 3: table_type: utf8 not null
- // 4: table_schema: bytes not null
- schemaBytes := rec.Column(4).(*array.Binary).Value(0)
- s, err := flight.DeserializeSchema(schemaBytes, c.db.alloc)
- if err != nil {
- return nil, adbcFromFlightStatus(err, "GetTableSchema")
+ var s *arrow.Schema
+ for i := 0; i < int(numRows); i++ {
+ currentTableName := rec.Column(2).(*array.String).Value(i)
+ if currentTableName == tableName {
+ // returned schema should be
+ // 0: catalog_name: utf8
+ // 1: db_schema_name: utf8
+ // 2: table_name: utf8 not null
+ // 3: table_type: utf8 not null
+ // 4: table_schema: bytes not null
+ schemaBytes := rec.Column(4).(*array.Binary).Value(i)
+ s, err = flight.DeserializeSchema(schemaBytes, c.db.alloc)
+ if err != nil {
+ return nil, adbcFromFlightStatus(err, "GetTableSchema")
+ }
+ return s, nil
+ }
+ }
+
+ return s, adbc.Error{
+ Msg: "[Flight SQL] GetTableSchema could not find a table with a matching schema",
+ Code: adbc.StatusNotFound,
}
- return s, nil
}
// GetTableTypes returns a list of the table types in the database.
diff --git a/go/adbc/driver/flightsql/flightsql_adbc_server_test.go b/go/adbc/driver/flightsql/flightsql_adbc_server_test.go
index dd6171c4cd..d8af6a6579 100644
--- a/go/adbc/driver/flightsql/flightsql_adbc_server_test.go
+++ b/go/adbc/driver/flightsql/flightsql_adbc_server_test.go
@@ -35,6 +35,7 @@ import (
"github.com/apache/arrow/go/v13/arrow/array"
"github.com/apache/arrow/go/v13/arrow/flight"
"github.com/apache/arrow/go/v13/arrow/flight/flightsql"
+ "github.com/apache/arrow/go/v13/arrow/flight/flightsql/schema_ref"
"github.com/apache/arrow/go/v13/arrow/memory"
"github.com/stretchr/testify/suite"
"golang.org/x/exp/maps"
@@ -107,6 +108,10 @@ func TestDataType(t *testing.T) {
suite.Run(t, &DataTypeTests{})
}
+func TestMultiTable(t *testing.T) {
+ suite.Run(t, &MultiTableTests{})
+}
+
// ---- AuthN Tests --------------------
type AuthnTestServer struct {
@@ -627,3 +632,90 @@ func (suite *DataTypeTests) TestListInt() {
func (suite *DataTypeTests) TestMapIntInt() {
suite.DoTestCase("map[int]int", SchemaMapIntInt)
}
+
+// ---- Multi Table Tests --------------------
+
+type MultiTableTestServer struct {
+ flightsql.BaseServer
+}
+
+func (server *MultiTableTestServer) GetFlightInfoStatement(ctx context.Context, cmd flightsql.StatementQuery, desc *flight.FlightDescriptor) (*flight.FlightInfo, error) {
+ query := cmd.GetQuery()
+ tkt, err := flightsql.CreateStatementQueryTicket([]byte(query))
+ if err != nil {
+ return nil, err
+ }
+
+ return &flight.FlightInfo{
+ Endpoint: []*flight.FlightEndpoint{{Ticket: &flight.Ticket{Ticket: tkt}}},
+ FlightDescriptor: desc,
+ TotalRecords: -1,
+ TotalBytes: -1,
+ }, nil
+}
+
+func (server *MultiTableTestServer) GetFlightInfoTables(ctx context.Context, cmd flightsql.GetTables, desc *flight.FlightDescriptor) (*flight.FlightInfo, error) {
+ schema := schema_ref.Tables
+ if cmd.GetIncludeSchema() {
+ schema = schema_ref.TablesWithIncludedSchema
+ }
+ server.Alloc = memory.NewCheckedAllocator(memory.DefaultAllocator)
+ info := &flight.FlightInfo{
+ Endpoint: []*flight.FlightEndpoint{
+ {Ticket: &flight.Ticket{Ticket: desc.Cmd}},
+ },
+ FlightDescriptor: desc,
+ Schema: flight.SerializeSchema(schema, server.Alloc),
+ TotalRecords: -1,
+ TotalBytes: -1,
+ }
+
+ return info, nil
+}
+
+func (server *MultiTableTestServer) DoGetTables(ctx context.Context, cmd flightsql.GetTables) (*arrow.Schema, <-chan flight.StreamChunk, error) {
+ bldr := array.NewRecordBuilder(server.Alloc, adbc.GetTableSchemaSchema)
+
+ bldr.Field(0).(*array.StringBuilder).AppendValues([]string{"", ""}, nil)
+ bldr.Field(1).(*array.StringBuilder).AppendValues([]string{"", ""}, nil)
+ bldr.Field(2).(*array.StringBuilder).AppendValues([]string{"tbl1", "tbl2"}, nil)
+ bldr.Field(3).(*array.StringBuilder).AppendValues([]string{"", ""}, nil)
+
+ sc1 := arrow.NewSchema([]arrow.Field{{Name: "a", Type: arrow.PrimitiveTypes.Int32, Nullable: true}}, nil)
+ sc2 := arrow.NewSchema([]arrow.Field{{Name: "b", Type: arrow.PrimitiveTypes.Int32, Nullable: true}}, nil)
+ buf1 := flight.SerializeSchema(sc1, server.Alloc)
+ buf2 := flight.SerializeSchema(sc2, server.Alloc)
+
+ bldr.Field(4).(*array.BinaryBuilder).AppendValues([][]byte{buf1, buf2}, nil)
+ defer bldr.Release()
+
+ rec := bldr.NewRecord()
+
+ ch := make(chan flight.StreamChunk)
+ go func() {
+ defer close(ch)
+ ch <- flight.StreamChunk{
+ Data: rec,
+ Desc: nil,
+ Err: nil,
+ }
+ }()
+ return adbc.GetTableSchemaSchema, ch, nil
+}
+
+type MultiTableTests struct {
+ ServerBasedTests
+}
+
+func (suite *MultiTableTests) SetupSuite() {
+ suite.DoSetupSuite(&MultiTableTestServer{}, nil, map[string]string{})
+}
+
+// Regression test for https://github.com/apache/arrow-adbc/issues/934
+func (suite *MultiTableTests) TestGetTableSchema() {
+ actualSchema, err := suite.cnxn.GetTableSchema(context.Background(), nil, nil, "tbl2")
+ suite.NoError(err)
+
+ expectedSchema := arrow.NewSchema([]arrow.Field{{Name: "b", Type: arrow.PrimitiveTypes.Int32, Nullable: true}}, nil)
+ suite.Equal(expectedSchema, actualSchema)
+}
From d901b87688ff2f34f169584daccf8a44300d8c92 Mon Sep 17 00:00:00 2001
From: David Li
Date: Wed, 23 Aug 2023 09:25:18 -0400
Subject: [PATCH 02/20] docs: pin furo version (#988)
Fixes #987.
---
ci/conda_env_docs.txt | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/ci/conda_env_docs.txt b/ci/conda_env_docs.txt
index 008b61d17a..ad1c9939f8 100644
--- a/ci/conda_env_docs.txt
+++ b/ci/conda_env_docs.txt
@@ -17,7 +17,8 @@
breathe
doxygen
-furo
+# XXX(https://github.com/apache/arrow-adbc/issues/987)
+furo=2023.07.26
make
numpydoc
pytest
From 19a30aea784dfdce1fed506604f0272a222a3294 Mon Sep 17 00:00:00 2001
From: Solomon Choe <128758390+ywc88@users.noreply.github.com>
Date: Wed, 23 Aug 2023 11:25:57 -0700
Subject: [PATCH 03/20] feat(python/adbc_driver_manager): add
fetch_record_batch (#989)
Fixes #968
---------
Co-authored-by: David Li
---
.../adbc_driver_manager/dbapi.py | 26 ++++++++++++++++---
.../adbc_driver_manager/tests/test_dbapi.py | 20 ++++++++++++++
2 files changed, 42 insertions(+), 4 deletions(-)
diff --git a/python/adbc_driver_manager/adbc_driver_manager/dbapi.py b/python/adbc_driver_manager/adbc_driver_manager/dbapi.py
index 31e4392ae5..60bc2d1bce 100644
--- a/python/adbc_driver_manager/adbc_driver_manager/dbapi.py
+++ b/python/adbc_driver_manager/adbc_driver_manager/dbapi.py
@@ -926,6 +926,24 @@ def fetch_df(self) -> "pandas.DataFrame":
)
return self._results.fetch_df()
+ def fetch_record_batch(self) -> pyarrow.RecordBatchReader:
+ """
+ Fetch the result as a PyArrow RecordBatchReader.
+
+ This implements a similar API as DuckDB:
+ https://duckdb.org/docs/guides/python/export_arrow.html#export-as-a-recordbatchreader
+
+ Notes
+ -----
+ This is an extension and not part of the DBAPI standard.
+ """
+ if self._results is None:
+ raise ProgrammingError(
+ "Cannot fetch_record_batch() before execute()",
+ status_code=_lib.AdbcStatusCode.INVALID_STATE,
+ )
+ return self._results._reader
+
# ----------------------------------------------------------
# Utilities
@@ -973,7 +991,7 @@ def fetchone(self) -> Optional[tuple]:
self.rownumber += 1
return row
- def fetchmany(self, size: int):
+ def fetchmany(self, size: int) -> List[tuple]:
rows = []
for _ in range(size):
row = self.fetchone()
@@ -982,7 +1000,7 @@ def fetchmany(self, size: int):
rows.append(row)
return rows
- def fetchall(self):
+ def fetchall(self) -> List[tuple]:
rows = []
while True:
row = self.fetchone()
@@ -991,10 +1009,10 @@ def fetchall(self):
rows.append(row)
return rows
- def fetch_arrow_table(self):
+ def fetch_arrow_table(self) -> pyarrow.Table:
return self._reader.read_all()
- def fetch_df(self):
+ def fetch_df(self) -> "pandas.DataFrame":
return self._reader.read_pandas()
diff --git a/python/adbc_driver_manager/tests/test_dbapi.py b/python/adbc_driver_manager/tests/test_dbapi.py
index 1eba12fda3..a29a661a23 100644
--- a/python/adbc_driver_manager/tests/test_dbapi.py
+++ b/python/adbc_driver_manager/tests/test_dbapi.py
@@ -294,6 +294,26 @@ def test_executemany(sqlite):
assert next(cur) == (5, 6)
+@pytest.mark.sqlite
+def test_fetch_record_batch(sqlite):
+ dataset = [
+ [1, 2],
+ [3, 4],
+ [5, 6],
+ [7, 8],
+ [9, 10],
+ ]
+ with sqlite.cursor() as cur:
+ cur.execute("CREATE TABLE foo (a, b)")
+ cur.executemany(
+ "INSERT INTO foo VALUES (?, ?)",
+ dataset,
+ )
+ cur.execute("SELECT * FROM foo")
+ rbr = cur.fetch_record_batch()
+ assert rbr.read_pandas().values.tolist() == dataset
+
+
@pytest.mark.sqlite
def test_fetch_empty(sqlite):
with sqlite.cursor() as cur:
From 60997c1b0bfc25883851a5870b4a0ac30e728f3a Mon Sep 17 00:00:00 2001
From: David Li
Date: Wed, 23 Aug 2023 14:57:04 -0400
Subject: [PATCH 04/20] chore: bump to arrow-go v13 (#990)
Fixes #927.
---
go/adbc/driver/flightsql/flightsql_adbc_test.go | 2 +-
go/adbc/go.mod | 2 +-
go/adbc/go.sum | 4 ++--
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/go/adbc/driver/flightsql/flightsql_adbc_test.go b/go/adbc/driver/flightsql/flightsql_adbc_test.go
index 2f96093408..ee7f92802e 100644
--- a/go/adbc/driver/flightsql/flightsql_adbc_test.go
+++ b/go/adbc/driver/flightsql/flightsql_adbc_test.go
@@ -252,7 +252,7 @@ func (s *FlightSQLQuirks) GetMetadata(code adbc.InfoCode) interface{} {
case adbc.InfoVendorVersion:
return "sqlite 3"
case adbc.InfoVendorArrowVersion:
- return "13.0.0-SNAPSHOT"
+ return "13.0.0"
}
return nil
diff --git a/go/adbc/go.mod b/go/adbc/go.mod
index 999a8d47f1..b1bb5234c0 100644
--- a/go/adbc/go.mod
+++ b/go/adbc/go.mod
@@ -20,7 +20,7 @@ module github.com/apache/arrow-adbc/go/adbc
go 1.18
require (
- github.com/apache/arrow/go/v13 v13.0.0-20230713180941-b97597765355
+ github.com/apache/arrow/go/v13 v13.0.0
github.com/bluele/gcache v0.0.2
github.com/google/uuid v1.3.0
github.com/snowflakedb/gosnowflake v1.6.22
diff --git a/go/adbc/go.sum b/go/adbc/go.sum
index 7e47f67f5c..83f49cc229 100644
--- a/go/adbc/go.sum
+++ b/go/adbc/go.sum
@@ -17,8 +17,8 @@ github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/apache/arrow/go/v12 v12.0.1 h1:JsR2+hzYYjgSUkBSaahpqCetqZMr76djX80fF/DiJbg=
github.com/apache/arrow/go/v12 v12.0.1/go.mod h1:weuTY7JvTG/HDPtMQxEUp7pU73vkLWMLpY67QwZ/WWw=
-github.com/apache/arrow/go/v13 v13.0.0-20230713180941-b97597765355 h1:QuXqLb2HzL5EjY99fFp+iG9NagAruvQIbU/2++x+2VY=
-github.com/apache/arrow/go/v13 v13.0.0-20230713180941-b97597765355/go.mod h1:W69eByFNO0ZR30q1/7Sr9d83zcVZmF2MiP3fFYAWJOc=
+github.com/apache/arrow/go/v13 v13.0.0 h1:kELrvDQuKZo8csdWYqBQfyi431x6Zs/YJTEgUuSVcWk=
+github.com/apache/arrow/go/v13 v13.0.0/go.mod h1:W69eByFNO0ZR30q1/7Sr9d83zcVZmF2MiP3fFYAWJOc=
github.com/apache/thrift v0.17.0 h1:cMd2aj52n+8VoAtvSvLn4kDC3aZ6IAkBuqWQ2IDu7wo=
github.com/apache/thrift v0.17.0/go.mod h1:OLxhMRJxomX+1I/KUw03qoV3mMz16BwaKI+d4fPBx7Q=
github.com/aws/aws-sdk-go-v2 v1.19.0 h1:klAT+y3pGFBU/qVf1uzwttpBbiuozJYWzNLHioyDJ+k=
From db0b9c111ed8721db512ff2ac493b3f050610284 Mon Sep 17 00:00:00 2001
From: Sutou Kouhei
Date: Sun, 27 Aug 2023 10:07:44 +0900
Subject: [PATCH 05/20] docs: add APT/Yum repositories to installation pages
(#992)
Fixes #991.
---
docs/source/cpp/driver_manager.rst | 64 ++++++++++++++++++++++++++++-
docs/source/driver/installation.rst | 64 ++++++++++++++++++++++++++++-
2 files changed, 125 insertions(+), 3 deletions(-)
diff --git a/docs/source/cpp/driver_manager.rst b/docs/source/cpp/driver_manager.rst
index 120e5dd5f0..d8db791d1f 100644
--- a/docs/source/cpp/driver_manager.rst
+++ b/docs/source/cpp/driver_manager.rst
@@ -27,7 +27,69 @@ specific driver.
Installation
============
-TODO
+Install the appropriate driver package. You can use conda-forge_, ``apt`` or ``dnf``.
+
+conda-forge:
+
+- ``mamba install adbc-driver-manager``
+
+You can use ``apt`` on the following platforms:
+
+- Debian GNU/Linux bookworm
+- Ubuntu 22.04
+
+Prepare the Apache Arrow APT repository:
+
+.. code-block:: bash
+
+ sudo apt update
+ sudo apt install -y -V ca-certificates lsb-release wget
+ sudo wget https://apache.jfrog.io/artifactory/arrow/$(lsb_release --id --short | tr 'A-Z' 'a-z')/apache-arrow-apt-source-latest-$(lsb_release --codename --short).deb
+ sudo apt install -y -V ./apache-arrow-apt-source-latest-$(lsb_release --codename --short).deb
+ rm ./apache-arrow-apt-source-latest-$(lsb_release --codename --short).deb
+ sudo apt update
+
+Install:
+
+- ``sudo apt install libadbc-driver-manager-dev``
+
+You can use ``dnf`` on the following platforms:
+
+- AlmaLinux 8
+- Oracle Linux 8
+- Red Hat Enterprise Linux 8
+- AlmaLinux 9
+- Oracle Linux 9
+- Red Hat Enterprise Linux 9
+
+Prepare the Apache Arrow Yum repository:
+
+.. code-block:: bash
+
+ sudo dnf install -y epel-release || sudo dnf install -y oracle-epel-release-el$(cut -d: -f5 /etc/system-release-cpe | cut -d. -f1) || sudo dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-$(cut -d: -f5 /etc/system-release-cpe | cut -d. -f1).noarch.rpm
+ sudo dnf install -y https://apache.jfrog.io/artifactory/arrow/almalinux/$(cut -d: -f5 /etc/system-release-cpe | cut -d. -f1)/apache-arrow-release-latest.rpm
+ sudo dnf config-manager --set-enabled epel || :
+ sudo dnf config-manager --set-enabled powertools || :
+ sudo dnf config-manager --set-enabled crb || :
+ sudo dnf config-manager --set-enabled ol$(cut -d: -f5 /etc/system-release-cpe | cut -d. -f1)_codeready_builder || :
+ sudo dnf config-manager --set-enabled codeready-builder-for-rhel-$(cut -d: -f5 /etc/system-release-cpe | cut -d. -f1)-rhui-rpms || :
+ sudo subscription-manager repos --enable codeready-builder-for-rhel-$(cut -d: -f5 /etc/system-release-cpe | cut -d. -f1)-$(arch)-rpms || :
+
+Install:
+
+- ``sudo dnf install adbc-driver-manager-devel``
+
+Then they can be used via CMake, e.g.:
+
+.. code-block:: cmake
+
+ find_package(AdbcDriverPostgreSQL)
+
+ # ...
+
+ target_link_libraries(myapp PRIVATE AdbcDriverPostgreSQL::adbc_driver_postgresql_shared)
+
+.. _conda-forge: https://conda-forge.org/
Usage
=====
diff --git a/docs/source/driver/installation.rst b/docs/source/driver/installation.rst
index ffd3a8c9a5..7540747774 100644
--- a/docs/source/driver/installation.rst
+++ b/docs/source/driver/installation.rst
@@ -26,12 +26,66 @@ Installation
C/C++
=====
-Install the appropriate driver package. These are currently only available from conda-forge_:
+Install the appropriate driver package. You can use conda-forge_, ``apt`` or ``dnf``.
+
+conda-forge:
- ``mamba install libadbc-driver-flightsql``
- ``mamba install libadbc-driver-postgresql``
- ``mamba install libadbc-driver-sqlite``
+You can use ``apt`` on the following platforms:
+
+- Debian GNU/Linux bookworm
+- Ubuntu 22.04
+
+Prepare the Apache Arrow APT repository:
+
+.. code-block:: bash
+
+ sudo apt update
+ sudo apt install -y -V ca-certificates lsb-release wget
+ sudo wget https://apache.jfrog.io/artifactory/arrow/$(lsb_release --id --short | tr 'A-Z' 'a-z')/apache-arrow-apt-source-latest-$(lsb_release --codename --short).deb
+ sudo apt install -y -V ./apache-arrow-apt-source-latest-$(lsb_release --codename --short).deb
+ rm ./apache-arrow-apt-source-latest-$(lsb_release --codename --short).deb
+ sudo apt update
+
+Install:
+
+- ``sudo apt install libadbc-driver-flightsql-dev``
+- ``sudo apt install libadbc-driver-postgresql-dev``
+- ``sudo apt install libadbc-driver-sqlite-dev``
+- ``sudo apt install libadbc-driver-snowflake-dev``
+
+You can use ``dnf`` on the following platforms:
+
+- AlmaLinux 8
+- Oracle Linux 8
+- Red Hat Enterprise Linux 8
+- AlmaLinux 9
+- Oracle Linux 9
+- Red Hat Enterprise Linux 9
+
+Prepare the Apache Arrow Yum repository:
+
+.. code-block:: bash
+
+ sudo dnf install -y epel-release || sudo dnf install -y oracle-epel-release-el$(cut -d: -f5 /etc/system-release-cpe | cut -d. -f1) || sudo dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-$(cut -d: -f5 /etc/system-release-cpe | cut -d. -f1).noarch.rpm
+ sudo dnf install -y https://apache.jfrog.io/artifactory/arrow/almalinux/$(cut -d: -f5 /etc/system-release-cpe | cut -d. -f1)/apache-arrow-release-latest.rpm
+ sudo dnf config-manager --set-enabled epel || :
+ sudo dnf config-manager --set-enabled powertools || :
+ sudo dnf config-manager --set-enabled crb || :
+ sudo dnf config-manager --set-enabled ol$(cut -d: -f5 /etc/system-release-cpe | cut -d. -f1)_codeready_builder || :
+ sudo dnf config-manager --set-enabled codeready-builder-for-rhel-$(cut -d: -f5 /etc/system-release-cpe | cut -d. -f1)-rhui-rpms || :
+ sudo subscription-manager repos --enable codeready-builder-for-rhel-$(cut -d: -f5 /etc/system-release-cpe | cut -d. -f1)-$(arch)-rpms || :
+
+Install:
+
+- ``sudo dnf install adbc-driver-flightsql-devel``
+- ``sudo dnf install adbc-driver-postgresql-devel``
+- ``sudo dnf install adbc-driver-sqlite-devel``
+- ``sudo dnf install adbc-driver-snowflake-devel``
+
Then they can be used via CMake, e.g.:
.. code-block:: cmake
@@ -80,7 +134,7 @@ From conda-forge_:
- ``mamba install adbc-driver-sqlite``
R
-======
+=
Install the appropriate driver package from GitHub:
@@ -94,3 +148,9 @@ Install the appropriate driver package from GitHub:
Installation of stable releases from CRAN is anticipated following the
release of ADBC Libraries 0.6.0.
+
+Ruby
+====
+
+Install the appropriate driver package for C/C++. You can use it from
+Ruby.
From 370aac6622df5b30c8fa8fa6c0c7d3a61e2520d7 Mon Sep 17 00:00:00 2001
From: David Li
Date: Mon, 28 Aug 2023 08:58:19 -0400
Subject: [PATCH 06/20] chore: bump versions to 0.7.0 (#993)
---
CHANGELOG.md | 50 +++++++++++++++++++
c/cmake_modules/AdbcVersion.cmake | 2 +-
ci/conda/meta.yaml | 2 +-
ci/linux-packages/debian/control | 22 ++++----
...ll => libadbc-driver-flightsql007.install} | 0
...tall => libadbc-driver-manager007.install} | 0
...l => libadbc-driver-postgresql007.install} | 0
...ll => libadbc-driver-snowflake007.install} | 0
...stall => libadbc-driver-sqlite007.install} | 0
docs/source/conf.py | 2 +-
glib/meson.build | 2 +-
java/core/pom.xml | 2 +-
java/driver-manager/pom.xml | 2 +-
java/driver/flight-sql-validation/pom.xml | 2 +-
java/driver/flight-sql/pom.xml | 2 +-
java/driver/jdbc-validation-derby/pom.xml | 2 +-
.../jdbc-validation-mssqlserver/pom.xml | 2 +-
.../driver/jdbc-validation-postgresql/pom.xml | 2 +-
java/driver/jdbc/pom.xml | 2 +-
java/driver/validation/pom.xml | 2 +-
java/pom.xml | 4 +-
java/sql/pom.xml | 2 +-
r/adbcdrivermanager/DESCRIPTION | 2 +-
r/adbcflightsql/DESCRIPTION | 2 +-
r/adbcpostgresql/DESCRIPTION | 2 +-
r/adbcsnowflake/DESCRIPTION | 2 +-
r/adbcsqlite/DESCRIPTION | 2 +-
ruby/lib/adbc/version.rb | 2 +-
rust/Cargo.toml | 2 +-
29 files changed, 84 insertions(+), 34 deletions(-)
rename ci/linux-packages/debian/{libadbc-driver-flightsql006.install => libadbc-driver-flightsql007.install} (100%)
rename ci/linux-packages/debian/{libadbc-driver-manager006.install => libadbc-driver-manager007.install} (100%)
rename ci/linux-packages/debian/{libadbc-driver-postgresql006.install => libadbc-driver-postgresql007.install} (100%)
rename ci/linux-packages/debian/{libadbc-driver-snowflake006.install => libadbc-driver-snowflake007.install} (100%)
rename ci/linux-packages/debian/{libadbc-driver-sqlite006.install => libadbc-driver-sqlite007.install} (100%)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0acdedfa81..190da9867b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -237,3 +237,53 @@
### Perf
- **go/adbc/driver/flightsql**: filter by schema in getObjectsTables (#726)
+
+## ADBC Libraries 0.6.0 (2023-08-23)
+
+### Feat
+
+- **python/adbc_driver_manager**: add fetch_record_batch (#989)
+- **c/driver**: Date32 support (#948)
+- **c/driver/postgresql**: Interval support (#908)
+- **go/adbc/driver/flightsql**: add context to gRPC errors (#921)
+- **c/driver/sqlite**: SQLite timestamp write support (#897)
+- **c/driver/postgresql**: Handle NUMERIC type by converting to string (#883)
+- **python/adbc_driver_postgresql**: add PostgreSQL options enum (#886)
+- **c/driver/postgresql**: TimestampTz write (#868)
+- **c/driver/postgresql**: Implement streaming/chunked output (#870)
+- **c/driver/postgresql**: Timestamp write support (#861)
+- **c/driver_manager,go/adbc,python**: trim down error messages (#866)
+- **c/driver/postgresql**: Int8 support (#858)
+- **c/driver/postgresql**: Better type error messages (#860)
+
+### Fix
+
+- **go/adbc/driver/flightsql**: Have GetTableSchema check for table name match instead of the first schema it receives (#980)
+- **r**: Ensure that info_codes are coerced to integer (#986)
+- **go/adbc/sqldriver**: fix handling of decimal types (#970)
+- **c/driver/postgresql**: Fix segfault associated with uninitialized copy_reader_ (#964)
+- **c/driver/sqlite**: add table types by default from arrow types (#955)
+- **csharp**: include GetTableTypes and GetTableSchema call for .NET 4.7.2 (#950)
+- **csharp**: include GetInfo and GetObjects call for .NET 4.7.2 (#945)
+- **c/driver/sqlite**: Wrap bulk ingests in a single begin/commit txn (#910)
+- **csharp**: fix C api to work under .NET 4.7.2 (#931)
+- **python/adbc_driver_snowflake**: allow connecting without URI (#923)
+- **go/adbc/pkg**: export Adbc* symbols on Windows (#916)
+- **go/adbc/driver/snowflake**: handle non-arrow result sets (#909)
+- **c/driver/sqlite**: fix escaping of sqlite TABLE CREATE columns (#906)
+- **go/adbc/pkg**: follow CGO rules properly (#902)
+- **go/adbc/driver/snowflake**: Fix integration tests by fixing timestamp handling (#889)
+- **go/adbc/driver/snowflake**: fix failing integration tests (#888)
+- **c/validation**: Fix ASAN-detected leak (#879)
+- **go/adbc**: fix crash on map type (#854)
+- **go/adbc/driver/snowflake**: handle result sets without Arrow data (#864)
+
+### Perf
+
+- **go/adbc/driver/snowflake**: Implement concurrency limit (#974)
+
+### Refactor
+
+- **c**: Vendor portable-snippets for overflow checks (#951)
+- **c/driver/postgresql**: Use ArrowArrayViewGetIntervalUnsafe from nanoarrow (#957)
+- **c/driver/postgresql**: Simplify current database querying (#880)
diff --git a/c/cmake_modules/AdbcVersion.cmake b/c/cmake_modules/AdbcVersion.cmake
index 8a27457cd9..a39565cde8 100644
--- a/c/cmake_modules/AdbcVersion.cmake
+++ b/c/cmake_modules/AdbcVersion.cmake
@@ -21,7 +21,7 @@
# ------------------------------------------------------------
# Version definitions
-set(ADBC_VERSION "0.6.0-SNAPSHOT")
+set(ADBC_VERSION "0.7.0-SNAPSHOT")
string(REGEX MATCH "^[0-9]+\\.[0-9]+\\.[0-9]+" ADBC_BASE_VERSION "${ADBC_VERSION}")
string(REPLACE "." ";" _adbc_version_list "${ADBC_BASE_VERSION}")
list(GET _adbc_version_list 0 ADBC_VERSION_MAJOR)
diff --git a/ci/conda/meta.yaml b/ci/conda/meta.yaml
index dfa02ee0cb..66d66cbf50 100644
--- a/ci/conda/meta.yaml
+++ b/ci/conda/meta.yaml
@@ -18,7 +18,7 @@
package:
name: arrow-adbc-split
# TODO: this needs to get bumped by the release process
- version: 0.6.0
+ version: 0.7.0
source:
path: ../../
diff --git a/ci/linux-packages/debian/control b/ci/linux-packages/debian/control
index dbf6b6e3d1..2b4a06a55f 100644
--- a/ci/linux-packages/debian/control
+++ b/ci/linux-packages/debian/control
@@ -33,7 +33,7 @@ Build-Depends:
Standards-Version: 4.5.0
Homepage: https://arrow.apache.org/adbc/
-Package: libadbc-driver-manager006
+Package: libadbc-driver-manager007
Section: libs
Architecture: any
Multi-Arch: same
@@ -51,12 +51,12 @@ Architecture: any
Multi-Arch: same
Depends:
${misc:Depends},
- libadbc-driver-manager006 (= ${binary:Version})
+ libadbc-driver-manager007 (= ${binary:Version})
Description: Apache Arrow Database Connectivity (ADBC) driver manager
.
This package provides C++ header files.
-Package: libadbc-driver-postgresql006
+Package: libadbc-driver-postgresql007
Section: libs
Architecture: any
Multi-Arch: same
@@ -74,12 +74,12 @@ Architecture: any
Multi-Arch: same
Depends:
${misc:Depends},
- libadbc-driver-postgresql006 (= ${binary:Version})
+ libadbc-driver-postgresql007 (= ${binary:Version})
Description: Apache Arrow Database Connectivity (ADBC) PostgreSQL driver
.
This package provides CMake package, pkg-config package and so on.
-Package: libadbc-driver-sqlite006
+Package: libadbc-driver-sqlite007
Section: libs
Architecture: any
Multi-Arch: same
@@ -97,12 +97,12 @@ Architecture: any
Multi-Arch: same
Depends:
${misc:Depends},
- libadbc-driver-sqlite006 (= ${binary:Version})
+ libadbc-driver-sqlite007 (= ${binary:Version})
Description: Apache Arrow Database Connectivity (ADBC) SQLite driver
.
This package provides CMake package, pkg-config package and so on.
-Package: libadbc-driver-flightsql006
+Package: libadbc-driver-flightsql007
Section: libs
Architecture: any
Multi-Arch: same
@@ -120,12 +120,12 @@ Architecture: any
Multi-Arch: same
Depends:
${misc:Depends},
- libadbc-driver-flightsql006 (= ${binary:Version})
+ libadbc-driver-flightsql007 (= ${binary:Version})
Description: Apache Arrow Database Connectivity (ADBC) Flight SQL driver
.
This package provides CMake package, pkg-config package and so on.
-Package: libadbc-driver-snowflake006
+Package: libadbc-driver-snowflake007
Section: libs
Architecture: any
Multi-Arch: same
@@ -143,7 +143,7 @@ Architecture: any
Multi-Arch: same
Depends:
${misc:Depends},
- libadbc-driver-snowflake006 (= ${binary:Version})
+ libadbc-driver-snowflake007 (= ${binary:Version})
Description: Apache Arrow Database Connectivity (ADBC) Snowflake driver
.
This package provides CMake package, pkg-config package and so on.
@@ -157,7 +157,7 @@ Pre-Depends: ${misc:Pre-Depends}
Depends:
${misc:Depends},
${shlibs:Depends},
- libadbc-driver-manager006 (= ${binary:Version})
+ libadbc-driver-manager007 (= ${binary:Version})
Description: Apache Arrow Database Connectivity (ADBC) driver manager
.
This package provides GLib based library files.
diff --git a/ci/linux-packages/debian/libadbc-driver-flightsql006.install b/ci/linux-packages/debian/libadbc-driver-flightsql007.install
similarity index 100%
rename from ci/linux-packages/debian/libadbc-driver-flightsql006.install
rename to ci/linux-packages/debian/libadbc-driver-flightsql007.install
diff --git a/ci/linux-packages/debian/libadbc-driver-manager006.install b/ci/linux-packages/debian/libadbc-driver-manager007.install
similarity index 100%
rename from ci/linux-packages/debian/libadbc-driver-manager006.install
rename to ci/linux-packages/debian/libadbc-driver-manager007.install
diff --git a/ci/linux-packages/debian/libadbc-driver-postgresql006.install b/ci/linux-packages/debian/libadbc-driver-postgresql007.install
similarity index 100%
rename from ci/linux-packages/debian/libadbc-driver-postgresql006.install
rename to ci/linux-packages/debian/libadbc-driver-postgresql007.install
diff --git a/ci/linux-packages/debian/libadbc-driver-snowflake006.install b/ci/linux-packages/debian/libadbc-driver-snowflake007.install
similarity index 100%
rename from ci/linux-packages/debian/libadbc-driver-snowflake006.install
rename to ci/linux-packages/debian/libadbc-driver-snowflake007.install
diff --git a/ci/linux-packages/debian/libadbc-driver-sqlite006.install b/ci/linux-packages/debian/libadbc-driver-sqlite007.install
similarity index 100%
rename from ci/linux-packages/debian/libadbc-driver-sqlite006.install
rename to ci/linux-packages/debian/libadbc-driver-sqlite007.install
diff --git a/docs/source/conf.py b/docs/source/conf.py
index b1c2dfa356..5c370f225b 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -26,7 +26,7 @@
project = "ADBC"
copyright = "2022, Apache Arrow Developers"
author = "the Apache Arrow Developers"
-release = "0.6.0 (dev)"
+release = "0.7.0 (dev)"
# Needed to generate version switcher
version = release
diff --git a/glib/meson.build b/glib/meson.build
index 55d9a13a3c..7516c433dc 100644
--- a/glib/meson.build
+++ b/glib/meson.build
@@ -23,7 +23,7 @@ project('adbc-glib',
'c_std=c99',
],
license: 'Apache-2.0',
- version: '0.6.0-SNAPSHOT')
+ version: '0.7.0-SNAPSHOT')
version_numbers = meson.project_version().split('-')[0].split('.')
version_major = version_numbers[0].to_int()
diff --git a/java/core/pom.xml b/java/core/pom.xml
index 837119d527..742651be12 100644
--- a/java/core/pom.xml
+++ b/java/core/pom.xml
@@ -14,7 +14,7 @@
arrow-adbc-java-root
org.apache.arrow.adbc
- 0.6.0-SNAPSHOT
+ 0.7.0-SNAPSHOT
adbc-core
diff --git a/java/driver-manager/pom.xml b/java/driver-manager/pom.xml
index 12d7a3f3c0..bfaba9ba7d 100644
--- a/java/driver-manager/pom.xml
+++ b/java/driver-manager/pom.xml
@@ -14,7 +14,7 @@
arrow-adbc-java-root
org.apache.arrow.adbc
- 0.6.0-SNAPSHOT
+ 0.7.0-SNAPSHOT
adbc-driver-manager
diff --git a/java/driver/flight-sql-validation/pom.xml b/java/driver/flight-sql-validation/pom.xml
index 987108c2f3..57e685f2a1 100644
--- a/java/driver/flight-sql-validation/pom.xml
+++ b/java/driver/flight-sql-validation/pom.xml
@@ -14,7 +14,7 @@
arrow-adbc-java-root
org.apache.arrow.adbc
- 0.6.0-SNAPSHOT
+ 0.7.0-SNAPSHOT
../../pom.xml
diff --git a/java/driver/flight-sql/pom.xml b/java/driver/flight-sql/pom.xml
index 432967963b..db7aef9900 100644
--- a/java/driver/flight-sql/pom.xml
+++ b/java/driver/flight-sql/pom.xml
@@ -14,7 +14,7 @@
arrow-adbc-java-root
org.apache.arrow.adbc
- 0.6.0-SNAPSHOT
+ 0.7.0-SNAPSHOT
../../pom.xml
diff --git a/java/driver/jdbc-validation-derby/pom.xml b/java/driver/jdbc-validation-derby/pom.xml
index f273218c65..a97f8c21fa 100644
--- a/java/driver/jdbc-validation-derby/pom.xml
+++ b/java/driver/jdbc-validation-derby/pom.xml
@@ -14,7 +14,7 @@
arrow-adbc-java-root
org.apache.arrow.adbc
- 0.6.0-SNAPSHOT
+ 0.7.0-SNAPSHOT
../../pom.xml
diff --git a/java/driver/jdbc-validation-mssqlserver/pom.xml b/java/driver/jdbc-validation-mssqlserver/pom.xml
index 5dd6b54acd..e0ba64cc4a 100644
--- a/java/driver/jdbc-validation-mssqlserver/pom.xml
+++ b/java/driver/jdbc-validation-mssqlserver/pom.xml
@@ -14,7 +14,7 @@
arrow-adbc-java-root
org.apache.arrow.adbc
- 0.6.0-SNAPSHOT
+ 0.7.0-SNAPSHOT
../../pom.xml
diff --git a/java/driver/jdbc-validation-postgresql/pom.xml b/java/driver/jdbc-validation-postgresql/pom.xml
index 295ca5d4f0..1e0e5407c9 100644
--- a/java/driver/jdbc-validation-postgresql/pom.xml
+++ b/java/driver/jdbc-validation-postgresql/pom.xml
@@ -14,7 +14,7 @@
arrow-adbc-java-root
org.apache.arrow.adbc
- 0.6.0-SNAPSHOT
+ 0.7.0-SNAPSHOT
../../pom.xml
diff --git a/java/driver/jdbc/pom.xml b/java/driver/jdbc/pom.xml
index 0c78818b1b..73ebad38e4 100644
--- a/java/driver/jdbc/pom.xml
+++ b/java/driver/jdbc/pom.xml
@@ -14,7 +14,7 @@
arrow-adbc-java-root
org.apache.arrow.adbc
- 0.6.0-SNAPSHOT
+ 0.7.0-SNAPSHOT
../../pom.xml
diff --git a/java/driver/validation/pom.xml b/java/driver/validation/pom.xml
index 20c2169762..c70235002f 100644
--- a/java/driver/validation/pom.xml
+++ b/java/driver/validation/pom.xml
@@ -14,7 +14,7 @@
arrow-adbc-java-root
org.apache.arrow.adbc
- 0.6.0-SNAPSHOT
+ 0.7.0-SNAPSHOT
../../pom.xml
diff --git a/java/pom.xml b/java/pom.xml
index 9b2b1d7d47..fd244227fb 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -20,7 +20,7 @@
org.apache.arrow.adbc
arrow-adbc-java-root
- 0.6.0-SNAPSHOT
+ 0.7.0-SNAPSHOT
pom
Apache Arrow ADBC Java Root POM
@@ -29,7 +29,7 @@
12.0.0
- 0.6.0-SNAPSHOT
+ 0.7.0-SNAPSHOT
diff --git a/java/sql/pom.xml b/java/sql/pom.xml
index 2b13654ce9..4703f1df76 100644
--- a/java/sql/pom.xml
+++ b/java/sql/pom.xml
@@ -14,7 +14,7 @@
arrow-adbc-java-root
org.apache.arrow.adbc
- 0.6.0-SNAPSHOT
+ 0.7.0-SNAPSHOT
adbc-sql
diff --git a/r/adbcdrivermanager/DESCRIPTION b/r/adbcdrivermanager/DESCRIPTION
index 3bb8509d3f..816aa909d8 100644
--- a/r/adbcdrivermanager/DESCRIPTION
+++ b/r/adbcdrivermanager/DESCRIPTION
@@ -1,6 +1,6 @@
Package: adbcdrivermanager
Title: 'Arrow' Database Connectivity ('ADBC') Driver Manager
-Version: 0.5.0.9000
+Version: 0.6.0.9000
Authors@R: c(
person("Dewey", "Dunnington", , "dewey@dunnington.ca", role = c("aut", "cre"),
comment = c(ORCID = "0000-0002-9415-4582")),
diff --git a/r/adbcflightsql/DESCRIPTION b/r/adbcflightsql/DESCRIPTION
index e4bbcda297..c041186a52 100644
--- a/r/adbcflightsql/DESCRIPTION
+++ b/r/adbcflightsql/DESCRIPTION
@@ -1,6 +1,6 @@
Package: adbcflightsql
Title: 'Arrow' Database Connectivity ('ADBC') 'FlightSQL' Driver
-Version: 0.5.0.9000
+Version: 0.6.0.9000
Authors@R: c(
person("Dewey", "Dunnington", , "dewey@dunnington.ca", role = c("aut", "cre"),
comment = c(ORCID = "0000-0002-9415-4582")),
diff --git a/r/adbcpostgresql/DESCRIPTION b/r/adbcpostgresql/DESCRIPTION
index 7a729f1b7b..86a1812cb2 100644
--- a/r/adbcpostgresql/DESCRIPTION
+++ b/r/adbcpostgresql/DESCRIPTION
@@ -1,6 +1,6 @@
Package: adbcpostgresql
Title: 'Arrow' Database Connectivity ('ADBC') 'PostgreSQL' Driver
-Version: 0.5.0.9000
+Version: 0.6.0.9000
Authors@R: c(
person("Dewey", "Dunnington", , "dewey@dunnington.ca", role = c("aut", "cre"),
comment = c(ORCID = "0000-0002-9415-4582")),
diff --git a/r/adbcsnowflake/DESCRIPTION b/r/adbcsnowflake/DESCRIPTION
index c21694d510..5351956a9f 100644
--- a/r/adbcsnowflake/DESCRIPTION
+++ b/r/adbcsnowflake/DESCRIPTION
@@ -1,6 +1,6 @@
Package: adbcsnowflake
Title: Arrow Database Connectivity ('ADBC') 'Snowflake' Driver
-Version: 0.5.0.9000
+Version: 0.6.0.9000
Authors@R: c(
person("Dewey", "Dunnington", , "dewey@dunnington.ca", role = c("aut", "cre"),
comment = c(ORCID = "0000-0002-9415-4582")),
diff --git a/r/adbcsqlite/DESCRIPTION b/r/adbcsqlite/DESCRIPTION
index 53d589a3e5..83bbf8f97a 100644
--- a/r/adbcsqlite/DESCRIPTION
+++ b/r/adbcsqlite/DESCRIPTION
@@ -1,6 +1,6 @@
Package: adbcsqlite
Title: 'Arrow' Database Connectivity ('ADBC') 'SQLite' Driver
-Version: 0.5.0.9000
+Version: 0.6.0.9000
Authors@R: c(
person("Dewey", "Dunnington", , "dewey@dunnington.ca", role = c("aut", "cre"),
comment = c(ORCID = "0000-0002-9415-4582")),
diff --git a/ruby/lib/adbc/version.rb b/ruby/lib/adbc/version.rb
index 3843fb710a..e853463bfe 100644
--- a/ruby/lib/adbc/version.rb
+++ b/ruby/lib/adbc/version.rb
@@ -16,7 +16,7 @@
# under the License.
module ADBC
- VERSION = "0.6.0-SNAPSHOT"
+ VERSION = "0.7.0-SNAPSHOT"
module Version
MAJOR, MINOR, MICRO, TAG = VERSION.split(".").collect(&:to_i)
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index dc7a37dde5..4daec0cc35 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -17,7 +17,7 @@
[package]
name = "arrow-adbc"
-version = "0.6.0-SNAPSHOT"
+version = "0.7.0-SNAPSHOT"
edition = "2021"
rust-version = "1.62"
description = "Rust implementation of Arrow Database Connectivity (ADBC)"
From e6319052af945fe910ee0265d02fc9048caf72ed Mon Sep 17 00:00:00 2001
From: David Li
Date: Mon, 28 Aug 2023 09:45:27 -0400
Subject: [PATCH 07/20] chore(dev/release): fix typos in release scripts (#994)
---
dev/release/02-sign.sh | 4 ++--
dev/release/06-binary-verify.sh | 2 +-
docs/source/development/releasing.rst | 6 +++---
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/dev/release/02-sign.sh b/dev/release/02-sign.sh
index 6267ce8407..2eb8dce5c0 100755
--- a/dev/release/02-sign.sh
+++ b/dev/release/02-sign.sh
@@ -23,8 +23,8 @@ main() {
local -r source_top_dir="$( cd "${source_dir}/../../" && pwd )"
pushd "${source_top_dir}"
- if [ "$#" -ne 2 ]; then
- echo "Usage: $0 "
+ if [ "$#" -ne 3 ]; then
+ echo "Usage: $0 "
exit 1
fi
diff --git a/dev/release/06-binary-verify.sh b/dev/release/06-binary-verify.sh
index 8f1a118249..2a9aad46e4 100755
--- a/dev/release/06-binary-verify.sh
+++ b/dev/release/06-binary-verify.sh
@@ -89,7 +89,7 @@ The vote will be open for at least 72 hours.
[ ] +0
[ ] -1 Do not release this as Apache Arrow ADBC ${version} because...
-Note: to verify APT/YUM packages on macOS/AArch64, you must \`export DOCKER_DEFAULT_ARCHITECTURE=linux/amd64\`. (Or skip this step by \`export TEST_APT=0 TEST_YUM=0\`.)
+Note: to verify APT/YUM packages on macOS/AArch64, you must \`export DOCKER_DEFAULT_PLATFORM=linux/amd64\`. (Or skip this step by \`export TEST_APT=0 TEST_YUM=0\`.)
[1]: https://github.com/apache/arrow-adbc/issues?q=is%3Aissue+milestone%3A%22ADBC+Libraries+${version}%22+is%3Aclosed
[2]: https://github.com/apache/arrow-adbc/commit/${commit}
diff --git a/docs/source/development/releasing.rst b/docs/source/development/releasing.rst
index cef9fab8e4..6408b10bb4 100644
--- a/docs/source/development/releasing.rst
+++ b/docs/source/development/releasing.rst
@@ -275,7 +275,7 @@ Be sure to go through on the following checklist:
# dev/release/post-01-upload.sh 0.1.0 0
dev/release/post-01-upload.sh
- git push --tag apache apache-arrow-adbc-
+ git push apache apache-arrow-adbc-
.. dropdown:: Create the final GitHub release
:class-title: sd-fs-5
@@ -303,8 +303,8 @@ Be sure to go through on the following checklist:
.. code-block:: Bash
- # dev/release/post-03-python.sh 10.0.0
- dev/release/post-03-python.sh
+ # dev/release/post-03-python.sh 0.1.0 0
+ dev/release/post-03-python.sh
.. dropdown:: Publish Maven packages
:class-title: sd-fs-5
From ba7032da87bd138e4b6310ad5004eb04aa85a481 Mon Sep 17 00:00:00 2001
From: David Li
Date: Mon, 28 Aug 2023 10:56:23 -0400
Subject: [PATCH 08/20] feat: ADBC API revision 1.1.0 (#971)
Fixes #55.
Fixes #317.
Fixes #318.
Fixes #319.
Fixes #442.
Fixes #458.
Fixes #459.
Fixes #541.
Fixes #620.
Fixes #685.
Fixes #736.
Fixes #755.
Fixes #939.
Fixes #940.
Fixes #942.
Fixes #962.
---------
Co-authored-by: Matt Topol
Co-authored-by: Sutou Kouhei
Co-authored-by: Will Jones
---
.env | 2 +-
.gitattributes | 5 +
.github/workflows/native-unix.yml | 1 +
.pre-commit-config.yaml | 4 +-
adbc.h | 1108 ++++++++++++++-
c/driver/common/utils.c | 202 ++-
c/driver/common/utils.h | 18 +-
c/driver/common/utils_test.cc | 95 ++
c/driver/flightsql/dremio_flightsql_test.cc | 3 +-
c/driver/flightsql/sqlite_flightsql_test.cc | 177 ++-
c/driver/postgresql/CMakeLists.txt | 1 +
c/driver/postgresql/connection.cc | 511 ++++++-
c/driver/postgresql/connection.h | 23 +-
c/driver/postgresql/database.cc | 45 +-
c/driver/postgresql/database.h | 19 +
c/driver/postgresql/error.cc | 97 ++
c/driver/postgresql/error.h | 42 +
c/driver/postgresql/postgres_copy_reader.h | 8 +-
c/driver/postgresql/postgresql.cc | 472 ++++++-
c/driver/postgresql/postgresql_test.cc | 385 +++++-
c/driver/postgresql/statement.cc | 392 ++++--
c/driver/postgresql/statement.h | 41 +-
c/driver/snowflake/snowflake_test.cc | 8 +-
c/driver/sqlite/sqlite.c | 312 ++++-
c/driver/sqlite/sqlite_test.cc | 20 +
c/driver_manager/CMakeLists.txt | 19 +-
c/driver_manager/adbc_driver_manager.cc | 1200 ++++++++++++++---
c/driver_manager/adbc_driver_manager_test.cc | 61 +-
c/driver_manager/adbc_version_100.c | 117 ++
c/driver_manager/adbc_version_100.h | 94 ++
.../adbc_version_100_compatibility_test.cc | 111 ++
c/integration/duckdb/CMakeLists.txt | 1 +
c/integration/duckdb/duckdb_test.cc | 8 +-
c/symbols.map | 10 +
c/validation/CMakeLists.txt | 19 +-
c/validation/adbc_validation.cc | 611 +++++++--
c/validation/adbc_validation.h | 102 +-
c/validation/adbc_validation_util.h | 1 +
ci/docker/python-wheel-manylinux.dockerfile | 4 +-
ci/scripts/python_wheel_unix_build.sh | 2 +-
dev/release/verify-release-candidate.sh | 8 +-
docker-compose.yml | 26 +-
docs/source/format/specification.rst | 111 ++
docs/source/format/versioning.rst | 28 +-
.../source/python/api/adbc_driver_manager.rst | 2 +
.../recipe/postgresql_create_append_table.py | 2 +-
go/adbc/adbc.go | 265 +++-
go/adbc/driver/flightsql/flightsql_adbc.go | 487 +++++--
.../flightsql/flightsql_adbc_server_test.go | 268 ++++
.../driver/flightsql/flightsql_adbc_test.go | 17 +-
.../driver/flightsql/flightsql_statement.go | 219 ++-
go/adbc/driver/flightsql/record_reader.go | 20 +-
go/adbc/driver/flightsql/utils.go | 63 +-
go/adbc/driver/snowflake/connection.go | 115 +-
go/adbc/driver/snowflake/driver.go | 128 ++
go/adbc/driver/snowflake/driver_test.go | 25 +-
go/adbc/driver/snowflake/statement.go | 116 +-
go/adbc/drivermgr/adbc.h | 1108 ++++++++++++++-
go/adbc/drivermgr/adbc_driver_manager.cc | 1200 ++++++++++++++---
go/adbc/infocode_string.go | 7 +-
go/adbc/pkg/_tmpl/driver.go.tmpl | 1077 +++++++++++++--
go/adbc/pkg/_tmpl/utils.c.tmpl | 353 ++++-
go/adbc/pkg/_tmpl/utils.h.tmpl | 76 +-
go/adbc/pkg/flightsql/driver.go | 1077 +++++++++++++--
go/adbc/pkg/flightsql/utils.c | 356 ++++-
go/adbc/pkg/flightsql/utils.h | 143 +-
go/adbc/pkg/panicdummy/driver.go | 1077 +++++++++++++--
go/adbc/pkg/panicdummy/utils.c | 356 ++++-
go/adbc/pkg/panicdummy/utils.h | 146 +-
go/adbc/pkg/snowflake/driver.go | 1077 +++++++++++++--
go/adbc/pkg/snowflake/utils.c | 356 ++++-
go/adbc/pkg/snowflake/utils.h | 143 +-
go/adbc/standard_schemas.go | 28 +
go/adbc/validation/validation.go | 341 ++++-
.../arrow/adbc/core/AdbcConnection.java | 152 ++-
.../apache/arrow/adbc/core/AdbcDatabase.java | 2 +-
.../apache/arrow/adbc/core/AdbcDriver.java | 35 +-
.../apache/arrow/adbc/core/AdbcException.java | 38 +-
.../apache/arrow/adbc/core/AdbcInfoCode.java | 17 +-
.../apache/arrow/adbc/core/AdbcOptions.java | 45 +
.../apache/arrow/adbc/core/AdbcStatement.java | 62 +-
.../arrow/adbc/core/BulkIngestMode.java | 15 +-
.../apache/arrow/adbc/core/ErrorDetail.java | 60 +
.../arrow/adbc/core/StandardSchemas.java | 79 +-
.../arrow/adbc/core/StandardStatistics.java | 81 ++
.../org/apache/arrow/adbc/core/TypedKey.java | 87 ++
.../driver/flightsql/FlightSqlQuirks.java | 2 +-
.../flightsql/FlightSqlStatementTest.java | 12 +
java/driver/flight-sql/pom.xml | 12 +
.../driver/flightsql/FlightSqlDriver.java | 17 +-
.../driver/flightsql/FlightSqlDriverUtil.java | 22 +-
.../driver/flightsql/FlightSqlStatement.java | 12 +
.../adbc/driver/flightsql/DetailsTest.java | 381 ++++++
.../driver/jdbc/derby/DerbyStatementTest.java | 5 +
.../jdbc/postgresql/PostgresqlQuirks.java | 27 +-
.../jdbc/postgresql/StatisticsTest.java | 121 ++
.../adbc/driver/jdbc/InfoMetadataBuilder.java | 22 +-
.../adbc/driver/jdbc/JdbcArrowReader.java | 15 +-
.../adbc/driver/jdbc/JdbcConnection.java | 206 +++
.../arrow/adbc/driver/jdbc/JdbcStatement.java | 36 +
.../testsuite/AbstractConnectionTest.java | 14 +
.../testsuite/AbstractStatementTest.java | 57 +
.../driver/testsuite/SqlValidationQuirks.java | 9 +
python/adbc_driver_manager/MANIFEST.in | 3 +
.../adbc_driver_manager/__init__.py | 10 +
.../adbc_driver_manager/_lib.pxd | 287 ++++
.../adbc_driver_manager/_lib.pyi | 48 +-
.../adbc_driver_manager/_lib.pyx | 690 ++++++----
.../adbc_driver_manager/_reader.pyi | 34 +
.../adbc_driver_manager/_reader.pyx | 113 ++
.../adbc_driver_manager/dbapi.py | 92 +-
python/adbc_driver_manager/setup.py | 16 +-
.../adbc_driver_manager/tests/test_reader.py | 80 ++
.../tests/test_dbapi.py | 144 ++
.../tests/test_lowlevel.py | 12 +-
r/adbcdrivermanager/src/driver_log.c | 3 +-
r/adbcdrivermanager/src/driver_monkey.c | 2 +-
r/adbcdrivermanager/src/driver_void.c | 2 +-
r/adbcflightsql/tools/download-go.R | 2 +-
r/adbcpostgresql/bootstrap.R | 2 +
r/adbcpostgresql/src/.gitignore | 2 +
r/adbcpostgresql/src/Makevars.in | 1 +
r/adbcpostgresql/src/Makevars.ucrt | 1 +
r/adbcpostgresql/src/Makevars.win | 1 +
r/adbcsnowflake/configure | 2 +-
r/adbcsnowflake/tools/download-go.R | 2 +-
126 files changed, 18453 insertions(+), 1911 deletions(-)
create mode 100644 c/driver/postgresql/error.cc
create mode 100644 c/driver/postgresql/error.h
create mode 100644 c/driver_manager/adbc_version_100.c
create mode 100644 c/driver_manager/adbc_version_100.h
create mode 100644 c/driver_manager/adbc_version_100_compatibility_test.cc
create mode 100644 java/core/src/main/java/org/apache/arrow/adbc/core/AdbcOptions.java
create mode 100644 java/core/src/main/java/org/apache/arrow/adbc/core/ErrorDetail.java
create mode 100644 java/core/src/main/java/org/apache/arrow/adbc/core/StandardStatistics.java
create mode 100644 java/core/src/main/java/org/apache/arrow/adbc/core/TypedKey.java
create mode 100644 java/driver/flight-sql/src/test/java/org/apache/arrow/adbc/driver/flightsql/DetailsTest.java
create mode 100644 java/driver/jdbc-validation-postgresql/src/test/java/org/apache/arrow/adbc/driver/jdbc/postgresql/StatisticsTest.java
create mode 100644 python/adbc_driver_manager/adbc_driver_manager/_lib.pxd
create mode 100644 python/adbc_driver_manager/adbc_driver_manager/_reader.pyi
create mode 100644 python/adbc_driver_manager/adbc_driver_manager/_reader.pyx
create mode 100644 python/adbc_driver_manager/tests/test_reader.py
diff --git a/.env b/.env
index 2b73edb57e..0ff75396fd 100644
--- a/.env
+++ b/.env
@@ -33,7 +33,7 @@ MANYLINUX=2014
MAVEN=3.5.4
PYTHON=3.10
GO=1.19.5
-ARROW_MAJOR_VERSION=12
+ARROW_MAJOR_VERSION=14
# Used through docker-compose.yml and serves as the default version for the
# ci/scripts/install_vcpkg.sh script.
diff --git a/.gitattributes b/.gitattributes
index 7f39f6a39c..f1efc73113 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -16,6 +16,11 @@
# under the License.
c/vendor/* linguist-vendored
+go/adbc/drivermgr/adbc.h linguist-vendored
+go/adbc/drivermgr/adbc_driver_manager.cc linguist-vendored
+go/adbc/pkg/flightsql/* linguist-generated
+go/adbc/pkg/panicdummy/* linguist-generated
+go/adbc/pkg/snowflake/* linguist-generated
python/adbc_driver_flightsql/adbc_driver_flightsql/_static_version.py export-subst
python/adbc_driver_manager/adbc_driver_manager/_static_version.py export-subst
python/adbc_driver_postgresql/adbc_driver_postgresql/_static_version.py export-subst
diff --git a/.github/workflows/native-unix.yml b/.github/workflows/native-unix.yml
index dffdab1933..b12aa46d51 100644
--- a/.github/workflows/native-unix.yml
+++ b/.github/workflows/native-unix.yml
@@ -318,6 +318,7 @@ jobs:
popd
- name: Go Test
env:
+ SNOWFLAKE_DATABASE: ADBC_TESTING
SNOWFLAKE_URI: ${{ secrets.SNOWFLAKE_URI }}
run: |
./ci/scripts/go_test.sh "$(pwd)" "$(pwd)/build" "$HOME/local"
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 82df862c70..188b069b56 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -43,12 +43,12 @@ repos:
- id: cmake-format
args: [--in-place]
- repo: https://github.com/cpplint/cpplint
- rev: 1.6.0
+ rev: 1.6.1
hooks:
- id: cpplint
args:
# From Arrow's config
- - "--filter=-whitespace/comments,-readability/casting,-readability/todo,-readability/alt_tokens,-build/header_guard,-build/c++11,-build/include_order,-build/include_subdir"
+ - "--filter=-whitespace/comments,-whitespace/indent,-readability/braces,-readability/casting,-readability/todo,-readability/alt_tokens,-build/header_guard,-build/c++11,-build/include_order,-build/include_subdir"
- "--linelength=90"
- "--verbose=2"
- repo: https://github.com/golangci/golangci-lint
diff --git a/adbc.h b/adbc.h
index 154e881255..1ec2f05080 100644
--- a/adbc.h
+++ b/adbc.h
@@ -35,7 +35,7 @@
/// but not concurrent access. Specific implementations may permit
/// multiple threads.
///
-/// \version 1.0.0
+/// \version 1.1.0
#pragma once
@@ -248,7 +248,24 @@ typedef uint8_t AdbcStatusCode;
/// May indicate a database-side error only.
#define ADBC_STATUS_UNAUTHORIZED 14
+/// \brief Inform the driver/driver manager that we are using the extended
+/// AdbcError struct from ADBC 1.1.0.
+///
+/// See the AdbcError documentation for usage.
+///
+/// \since ADBC API revision 1.1.0
+#define ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA INT32_MIN
+
/// \brief A detailed error message for an operation.
+///
+/// The caller must zero-initialize this struct (clarified in ADBC 1.1.0).
+///
+/// The structure was extended in ADBC 1.1.0. Drivers and clients using ADBC
+/// 1.0.0 will not have the private_data or private_driver fields. Drivers
+/// should read/write these fields if and only if vendor_code is equal to
+/// ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA. Clients are required to initialize
+/// this struct to avoid the possibility of uninitialized values confusing the
+/// driver.
struct ADBC_EXPORT AdbcError {
/// \brief The error message.
char* message;
@@ -266,8 +283,112 @@ struct ADBC_EXPORT AdbcError {
/// Unlike other structures, this is an embedded callback to make it
/// easier for the driver manager and driver to cooperate.
void (*release)(struct AdbcError* error);
+
+ /// \brief Opaque implementation-defined state.
+ ///
+ /// This field may not be used unless vendor_code is
+ /// ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA. If present, this field is NULLPTR
+ /// iff the error is unintialized/freed.
+ ///
+ /// \since ADBC API revision 1.1.0
+ void* private_data;
+
+ /// \brief The associated driver (used by the driver manager to help
+ /// track state).
+ ///
+ /// This field may not be used unless vendor_code is
+ /// ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA.
+ ///
+ /// \since ADBC API revision 1.1.0
+ struct AdbcDriver* private_driver;
};
+#ifdef __cplusplus
+/// \brief A helper to initialize the full AdbcError structure.
+///
+/// \since ADBC API revision 1.1.0
+#define ADBC_ERROR_INIT \
+ (AdbcError{nullptr, \
+ ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA, \
+ {0, 0, 0, 0, 0}, \
+ nullptr, \
+ nullptr, \
+ nullptr})
+#else
+/// \brief A helper to initialize the full AdbcError structure.
+///
+/// \since ADBC API revision 1.1.0
+#define ADBC_ERROR_INIT \
+ ((struct AdbcError){ \
+ NULL, ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA, {0, 0, 0, 0, 0}, NULL, NULL, NULL})
+#endif
+
+/// \brief The size of the AdbcError structure in ADBC 1.0.0.
+///
+/// Drivers written for ADBC 1.1.0 and later should never touch more than this
+/// portion of an AdbcDriver struct when vendor_code is not
+/// ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA.
+///
+/// \since ADBC API revision 1.1.0
+#define ADBC_ERROR_1_0_0_SIZE (offsetof(struct AdbcError, private_data))
+/// \brief The size of the AdbcError structure in ADBC 1.1.0.
+///
+/// Drivers written for ADBC 1.1.0 and later should never touch more than this
+/// portion of an AdbcDriver struct when vendor_code is
+/// ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA.
+///
+/// \since ADBC API revision 1.1.0
+#define ADBC_ERROR_1_1_0_SIZE (sizeof(struct AdbcError))
+
+/// \brief Extra key-value metadata for an error.
+///
+/// The fields here are owned by the driver and should not be freed. The
+/// fields here are invalidated when the release callback in AdbcError is
+/// called.
+///
+/// \since ADBC API revision 1.1.0
+struct ADBC_EXPORT AdbcErrorDetail {
+ /// \brief The metadata key.
+ const char* key;
+ /// \brief The binary metadata value.
+ const uint8_t* value;
+ /// \brief The length of the metadata value.
+ size_t value_length;
+};
+
+/// \brief Get the number of metadata values available in an error.
+///
+/// \since ADBC API revision 1.1.0
+ADBC_EXPORT
+int AdbcErrorGetDetailCount(const struct AdbcError* error);
+
+/// \brief Get a metadata value in an error by index.
+///
+/// If index is invalid, returns an AdbcErrorDetail initialized with NULL/0
+/// fields.
+///
+/// \since ADBC API revision 1.1.0
+ADBC_EXPORT
+struct AdbcErrorDetail AdbcErrorGetDetail(const struct AdbcError* error, int index);
+
+/// \brief Get an ADBC error from an ArrowArrayStream created by a driver.
+///
+/// This allows retrieving error details and other metadata that would
+/// normally be suppressed by the Arrow C Stream Interface.
+///
+/// The caller MUST NOT release the error; it is managed by the release
+/// callback in the stream itself.
+///
+/// \param[in] stream The stream to query.
+/// \param[out] status The ADBC status code, or ADBC_STATUS_OK if there is no
+/// error. Not written to if the stream does not contain an ADBC error or
+/// if the pointer is NULL.
+/// \return NULL if not supported.
+/// \since ADBC API revision 1.1.0
+ADBC_EXPORT
+const struct AdbcError* AdbcErrorFromArrayStream(struct ArrowArrayStream* stream,
+ AdbcStatusCode* status);
+
/// @}
/// \defgroup adbc-constants Constants
@@ -279,6 +400,14 @@ struct ADBC_EXPORT AdbcError {
/// point to an AdbcDriver.
#define ADBC_VERSION_1_0_0 1000000
+/// \brief ADBC revision 1.1.0.
+///
+/// When passed to an AdbcDriverInitFunc(), the driver parameter must
+/// point to an AdbcDriver.
+///
+/// \since ADBC API revision 1.1.0
+#define ADBC_VERSION_1_1_0 1001000
+
/// \brief Canonical option value for enabling an option.
///
/// For use as the value in SetOption calls.
@@ -288,6 +417,34 @@ struct ADBC_EXPORT AdbcError {
/// For use as the value in SetOption calls.
#define ADBC_OPTION_VALUE_DISABLED "false"
+/// \brief Canonical option name for URIs.
+///
+/// Should be used as the expected option name to specify a URI for
+/// any ADBC driver.
+///
+/// The type is char*.
+///
+/// \since ADBC API revision 1.1.0
+#define ADBC_OPTION_URI "uri"
+/// \brief Canonical option name for usernames.
+///
+/// Should be used as the expected option name to specify a username
+/// to a driver for authentication.
+///
+/// The type is char*.
+///
+/// \since ADBC API revision 1.1.0
+#define ADBC_OPTION_USERNAME "username"
+/// \brief Canonical option name for passwords.
+///
+/// Should be used as the expected option name to specify a password
+/// for authentication to a driver.
+///
+/// The type is char*.
+///
+/// \since ADBC API revision 1.1.0
+#define ADBC_OPTION_PASSWORD "password"
+
/// \brief The database vendor/product name (e.g. the server name).
/// (type: utf8).
///
@@ -315,6 +472,15 @@ struct ADBC_EXPORT AdbcError {
///
/// \see AdbcConnectionGetInfo
#define ADBC_INFO_DRIVER_ARROW_VERSION 102
+/// \brief The driver ADBC API version (type: int64).
+///
+/// The value should be one of the ADBC_VERSION constants.
+///
+/// \since ADBC API revision 1.1.0
+/// \see AdbcConnectionGetInfo
+/// \see ADBC_VERSION_1_0_0
+/// \see ADBC_VERSION_1_1_0
+#define ADBC_INFO_DRIVER_ADBC_VERSION 103
/// \brief Return metadata on catalogs, schemas, tables, and columns.
///
@@ -337,18 +503,133 @@ struct ADBC_EXPORT AdbcError {
/// \see AdbcConnectionGetObjects
#define ADBC_OBJECT_DEPTH_COLUMNS ADBC_OBJECT_DEPTH_ALL
+/// \defgroup adbc-table-statistics ADBC Statistic Types
+/// Standard statistic names for AdbcConnectionGetStatistics.
+/// @{
+
+/// \brief The dictionary-encoded name of the average byte width statistic.
+#define ADBC_STATISTIC_AVERAGE_BYTE_WIDTH_KEY 0
+/// \brief The average byte width statistic. The average size in bytes of a
+/// row in the column. Value type is float64.
+///
+/// For example, this is roughly the average length of a string for a string
+/// column.
+#define ADBC_STATISTIC_AVERAGE_BYTE_WIDTH_NAME "adbc.statistic.byte_width"
+/// \brief The dictionary-encoded name of the distinct value count statistic.
+#define ADBC_STATISTIC_DISTINCT_COUNT_KEY 1
+/// \brief The distinct value count (NDV) statistic. The number of distinct
+/// values in the column. Value type is int64 (when not approximate) or
+/// float64 (when approximate).
+#define ADBC_STATISTIC_DISTINCT_COUNT_NAME "adbc.statistic.distinct_count"
+/// \brief The dictionary-encoded name of the max byte width statistic.
+#define ADBC_STATISTIC_MAX_BYTE_WIDTH_KEY 2
+/// \brief The max byte width statistic. The maximum size in bytes of a row
+/// in the column. Value type is int64 (when not approximate) or float64
+/// (when approximate).
+///
+/// For example, this is the maximum length of a string for a string column.
+#define ADBC_STATISTIC_MAX_BYTE_WIDTH_NAME "adbc.statistic.byte_width"
+/// \brief The dictionary-encoded name of the max value statistic.
+#define ADBC_STATISTIC_MAX_VALUE_KEY 3
+/// \brief The max value statistic. Value type is column-dependent.
+#define ADBC_STATISTIC_MAX_VALUE_NAME "adbc.statistic.byte_width"
+/// \brief The dictionary-encoded name of the min value statistic.
+#define ADBC_STATISTIC_MIN_VALUE_KEY 4
+/// \brief The min value statistic. Value type is column-dependent.
+#define ADBC_STATISTIC_MIN_VALUE_NAME "adbc.statistic.byte_width"
+/// \brief The dictionary-encoded name of the null count statistic.
+#define ADBC_STATISTIC_NULL_COUNT_KEY 5
+/// \brief The null count statistic. The number of values that are null in
+/// the column. Value type is int64 (when not approximate) or float64
+/// (when approximate).
+#define ADBC_STATISTIC_NULL_COUNT_NAME "adbc.statistic.null_count"
+/// \brief The dictionary-encoded name of the row count statistic.
+#define ADBC_STATISTIC_ROW_COUNT_KEY 6
+/// \brief The row count statistic. The number of rows in the column or
+/// table. Value type is int64 (when not approximate) or float64 (when
+/// approximate).
+#define ADBC_STATISTIC_ROW_COUNT_NAME "adbc.statistic.row_count"
+/// @}
+
/// \brief The name of the canonical option for whether autocommit is
/// enabled.
///
+/// The type is char*.
+///
/// \see AdbcConnectionSetOption
#define ADBC_CONNECTION_OPTION_AUTOCOMMIT "adbc.connection.autocommit"
/// \brief The name of the canonical option for whether the current
/// connection should be restricted to being read-only.
///
+/// The type is char*.
+///
/// \see AdbcConnectionSetOption
#define ADBC_CONNECTION_OPTION_READ_ONLY "adbc.connection.readonly"
+/// \brief The name of the canonical option for the current catalog.
+///
+/// The type is char*.
+///
+/// \see AdbcConnectionGetOption
+/// \see AdbcConnectionSetOption
+/// \since ADBC API revision 1.1.0
+#define ADBC_CONNECTION_OPTION_CURRENT_CATALOG "adbc.connection.catalog"
+
+/// \brief The name of the canonical option for the current schema.
+///
+/// The type is char*.
+///
+/// \see AdbcConnectionGetOption
+/// \see AdbcConnectionSetOption
+/// \since ADBC API revision 1.1.0
+#define ADBC_CONNECTION_OPTION_CURRENT_DB_SCHEMA "adbc.connection.db_schema"
+
+/// \brief The name of the canonical option for making query execution
+/// nonblocking.
+///
+/// When enabled, AdbcStatementExecutePartitions will return
+/// partitions as soon as they are available, instead of returning
+/// them all at the end. When there are no more to return, it will
+/// return an empty set of partitions. AdbcStatementExecuteQuery and
+/// AdbcStatementExecuteSchema are not affected.
+///
+/// The default is ADBC_OPTION_VALUE_DISABLED.
+///
+/// The type is char*.
+///
+/// \see AdbcStatementSetOption
+/// \since ADBC API revision 1.1.0
+#define ADBC_STATEMENT_OPTION_INCREMENTAL "adbc.statement.exec.incremental"
+
+/// \brief The name of the option for getting the progress of a query.
+///
+/// The value is not necessarily in any particular range or have any
+/// particular units. (For example, it might be a percentage, bytes of data,
+/// rows of data, number of workers, etc.) The max value can be retrieved via
+/// ADBC_STATEMENT_OPTION_MAX_PROGRESS. This represents the progress of
+/// execution, not of consumption (i.e., it is independent of how much of the
+/// result set has been read by the client via ArrowArrayStream.get_next().)
+///
+/// The type is double.
+///
+/// \see AdbcStatementGetOptionDouble
+/// \since ADBC API revision 1.1.0
+#define ADBC_STATEMENT_OPTION_PROGRESS "adbc.statement.exec.progress"
+
+/// \brief The name of the option for getting the maximum progress of a query.
+///
+/// This is the value of ADBC_STATEMENT_OPTION_PROGRESS for a completed query.
+/// If not supported, or if the value is nonpositive, then the maximum is not
+/// known. (For instance, the query may be fully streaming and the driver
+/// does not know when the result set will end.)
+///
+/// The type is double.
+///
+/// \see AdbcStatementGetOptionDouble
+/// \since ADBC API revision 1.1.0
+#define ADBC_STATEMENT_OPTION_MAX_PROGRESS "adbc.statement.exec.max_progress"
+
/// \brief The name of the canonical option for setting the isolation
/// level of a transaction.
///
@@ -357,6 +638,8 @@ struct ADBC_EXPORT AdbcError {
/// isolation level is not supported by a driver, it should return an
/// appropriate error.
///
+/// The type is char*.
+///
/// \see AdbcConnectionSetOption
#define ADBC_CONNECTION_OPTION_ISOLATION_LEVEL \
"adbc.connection.transaction.isolation_level"
@@ -449,8 +732,12 @@ struct ADBC_EXPORT AdbcError {
/// exist. If the table exists but has a different schema,
/// ADBC_STATUS_ALREADY_EXISTS should be raised. Else, data should be
/// appended to the target table.
+///
+/// The type is char*.
#define ADBC_INGEST_OPTION_TARGET_TABLE "adbc.ingest.target_table"
/// \brief Whether to create (the default) or append.
+///
+/// The type is char*.
#define ADBC_INGEST_OPTION_MODE "adbc.ingest.mode"
/// \brief Create the table and insert data; error if the table exists.
#define ADBC_INGEST_OPTION_MODE_CREATE "adbc.ingest.mode.create"
@@ -458,6 +745,15 @@ struct ADBC_EXPORT AdbcError {
/// table does not exist (ADBC_STATUS_NOT_FOUND) or does not match
/// the schema of the data to append (ADBC_STATUS_ALREADY_EXISTS).
#define ADBC_INGEST_OPTION_MODE_APPEND "adbc.ingest.mode.append"
+/// \brief Create the table and insert data; drop the original table
+/// if it already exists.
+/// \since ADBC API revision 1.1.0
+#define ADBC_INGEST_OPTION_MODE_REPLACE "adbc.ingest.mode.replace"
+/// \brief Insert data; create the table if it does not exist, or
+/// error if the table exists, but the schema does not match the
+/// 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"
/// @}
@@ -624,7 +920,7 @@ struct ADBC_EXPORT AdbcDriver {
AdbcStatusCode (*DatabaseRelease)(struct AdbcDatabase*, struct AdbcError*);
AdbcStatusCode (*ConnectionCommit)(struct AdbcConnection*, struct AdbcError*);
- AdbcStatusCode (*ConnectionGetInfo)(struct AdbcConnection*, uint32_t*, size_t,
+ AdbcStatusCode (*ConnectionGetInfo)(struct AdbcConnection*, const uint32_t*, size_t,
struct ArrowArrayStream*, struct AdbcError*);
AdbcStatusCode (*ConnectionGetObjects)(struct AdbcConnection*, int, const char*,
const char*, const char*, const char**,
@@ -667,8 +963,108 @@ struct ADBC_EXPORT AdbcDriver {
struct AdbcError*);
AdbcStatusCode (*StatementSetSubstraitPlan)(struct AdbcStatement*, const uint8_t*,
size_t, struct AdbcError*);
+
+ /// \defgroup adbc-1.1.0 ADBC API Revision 1.1.0
+ ///
+ /// Functions added in ADBC 1.1.0. For backwards compatibility,
+ /// these members must not be accessed unless the version passed to
+ /// the AdbcDriverInitFunc is greater than or equal to
+ /// ADBC_VERSION_1_1_0.
+ ///
+ /// For a 1.0.0 driver being loaded by a 1.1.0 driver manager: the
+ /// 1.1.0 manager will allocate the new, expanded AdbcDriver struct
+ /// and attempt to have the driver initialize it with
+ /// ADBC_VERSION_1_1_0. This must return an error, after which the
+ /// driver will try again with ADBC_VERSION_1_0_0. The driver must
+ /// not access the new fields, which will carry undefined values.
+ ///
+ /// For a 1.1.0 driver being loaded by a 1.0.0 driver manager: the
+ /// 1.0.0 manager will allocate the old AdbcDriver struct and
+ /// attempt to have the driver initialize it with
+ /// ADBC_VERSION_1_0_0. The driver must not access the new fields,
+ /// and should initialize the old fields.
+ ///
+ /// @{
+
+ int (*ErrorGetDetailCount)(const struct AdbcError* error);
+ struct AdbcErrorDetail (*ErrorGetDetail)(const struct AdbcError* error, int index);
+ const struct AdbcError* (*ErrorFromArrayStream)(struct ArrowArrayStream* stream,
+ AdbcStatusCode* status);
+
+ AdbcStatusCode (*DatabaseGetOption)(struct AdbcDatabase*, const char*, char*, size_t*,
+ struct AdbcError*);
+ AdbcStatusCode (*DatabaseGetOptionBytes)(struct AdbcDatabase*, const char*, uint8_t*,
+ size_t*, struct AdbcError*);
+ AdbcStatusCode (*DatabaseGetOptionDouble)(struct AdbcDatabase*, const char*, double*,
+ struct AdbcError*);
+ AdbcStatusCode (*DatabaseGetOptionInt)(struct AdbcDatabase*, const char*, int64_t*,
+ struct AdbcError*);
+ AdbcStatusCode (*DatabaseSetOptionBytes)(struct AdbcDatabase*, const char*,
+ const uint8_t*, size_t, struct AdbcError*);
+ AdbcStatusCode (*DatabaseSetOptionDouble)(struct AdbcDatabase*, const char*, double,
+ struct AdbcError*);
+ AdbcStatusCode (*DatabaseSetOptionInt)(struct AdbcDatabase*, const char*, int64_t,
+ struct AdbcError*);
+
+ AdbcStatusCode (*ConnectionCancel)(struct AdbcConnection*, struct AdbcError*);
+ AdbcStatusCode (*ConnectionGetOption)(struct AdbcConnection*, const char*, char*,
+ size_t*, struct AdbcError*);
+ AdbcStatusCode (*ConnectionGetOptionBytes)(struct AdbcConnection*, const char*,
+ uint8_t*, size_t*, struct AdbcError*);
+ AdbcStatusCode (*ConnectionGetOptionDouble)(struct AdbcConnection*, const char*,
+ double*, struct AdbcError*);
+ AdbcStatusCode (*ConnectionGetOptionInt)(struct AdbcConnection*, const char*, int64_t*,
+ struct AdbcError*);
+ AdbcStatusCode (*ConnectionGetStatistics)(struct AdbcConnection*, const char*,
+ const char*, const char*, char,
+ struct ArrowArrayStream*, struct AdbcError*);
+ AdbcStatusCode (*ConnectionGetStatisticNames)(struct AdbcConnection*,
+ struct ArrowArrayStream*,
+ struct AdbcError*);
+ AdbcStatusCode (*ConnectionSetOptionBytes)(struct AdbcConnection*, const char*,
+ const uint8_t*, size_t, struct AdbcError*);
+ AdbcStatusCode (*ConnectionSetOptionDouble)(struct AdbcConnection*, const char*, double,
+ struct AdbcError*);
+ AdbcStatusCode (*ConnectionSetOptionInt)(struct AdbcConnection*, const char*, int64_t,
+ struct AdbcError*);
+
+ AdbcStatusCode (*StatementCancel)(struct AdbcStatement*, struct AdbcError*);
+ AdbcStatusCode (*StatementExecuteSchema)(struct AdbcStatement*, struct ArrowSchema*,
+ struct AdbcError*);
+ AdbcStatusCode (*StatementGetOption)(struct AdbcStatement*, const char*, char*, size_t*,
+ struct AdbcError*);
+ AdbcStatusCode (*StatementGetOptionBytes)(struct AdbcStatement*, const char*, uint8_t*,
+ size_t*, struct AdbcError*);
+ AdbcStatusCode (*StatementGetOptionDouble)(struct AdbcStatement*, const char*, double*,
+ struct AdbcError*);
+ AdbcStatusCode (*StatementGetOptionInt)(struct AdbcStatement*, const char*, int64_t*,
+ struct AdbcError*);
+ AdbcStatusCode (*StatementSetOptionBytes)(struct AdbcStatement*, const char*,
+ const uint8_t*, size_t, struct AdbcError*);
+ AdbcStatusCode (*StatementSetOptionDouble)(struct AdbcStatement*, const char*, double,
+ struct AdbcError*);
+ AdbcStatusCode (*StatementSetOptionInt)(struct AdbcStatement*, const char*, int64_t,
+ struct AdbcError*);
+
+ /// @}
};
+/// \brief The size of the AdbcDriver structure in ADBC 1.0.0.
+/// Drivers written for ADBC 1.1.0 and later should never touch more
+/// than this portion of an AdbcDriver struct when given
+/// ADBC_VERSION_1_0_0.
+///
+/// \since ADBC API revision 1.1.0
+#define ADBC_DRIVER_1_0_0_SIZE (offsetof(struct AdbcDriver, ErrorGetDetailCount))
+
+/// \brief The size of the AdbcDriver structure in ADBC 1.1.0.
+/// Drivers written for ADBC 1.1.0 and later should never touch more
+/// than this portion of an AdbcDriver struct when given
+/// ADBC_VERSION_1_1_0.
+///
+/// \since ADBC API revision 1.1.0
+#define ADBC_DRIVER_1_1_0_SIZE (sizeof(struct AdbcDriver))
+
/// @}
/// \addtogroup adbc-database
@@ -684,16 +1080,189 @@ struct ADBC_EXPORT AdbcDriver {
ADBC_EXPORT
AdbcStatusCode AdbcDatabaseNew(struct AdbcDatabase* database, struct AdbcError* error);
+/// \brief Get a string option of the database.
+///
+/// This must always be thread-safe (other operations are not), though
+/// given the semantics here, it is not recommended to call GetOption
+/// concurrently with itself.
+///
+/// length must be provided and must be the size of the buffer pointed
+/// to by value. If there is sufficient space, the driver will copy
+/// the option value (including the null terminator) to buffer and set
+/// length to the size of the actual value. If the buffer is too
+/// small, no data will be written and length will be set to the
+/// required length.
+///
+/// In other words:
+///
+/// - If output length <= input length, value will contain a value
+/// with length bytes.
+/// - If output length > input length, nothing has been written to
+/// value.
+///
+/// For standard options, drivers must always support getting the
+/// option value (if they support getting option values at all) via
+/// the type specified in the option. (For example, an option set via
+/// SetOptionDouble must be retrievable via GetOptionDouble.) Drivers
+/// may also support getting a converted option value via other
+/// getters if needed. (For example, getting the string
+/// representation of a double option.)
+///
+/// \since ADBC API revision 1.1.0
+/// \param[in] database The database.
+/// \param[in] key The option to get.
+/// \param[out] value The option value.
+/// \param[in,out] length The length of value.
+/// \param[out] error An optional location to return an error
+/// message if necessary.
+/// \return ADBC_STATUS_NOT_FOUND if the option is not recognized.
+ADBC_EXPORT
+AdbcStatusCode AdbcDatabaseGetOption(struct AdbcDatabase* database, const char* key,
+ char* value, size_t* length,
+ struct AdbcError* error);
+
+/// \brief Get a bytestring option of the database.
+///
+/// This must always be thread-safe (other operations are not), though
+/// given the semantics here, it is not recommended to call
+/// GetOptionBytes concurrently with itself.
+///
+/// length must be provided and must be the size of the buffer pointed
+/// to by value. If there is sufficient space, the driver will copy
+/// the option value to buffer and set length to the size of the
+/// actual value. If the buffer is too small, no data will be written
+/// and length will be set to the required length.
+///
+/// In other words:
+///
+/// - If output length <= input length, value will contain a value
+/// with length bytes.
+/// - If output length > input length, nothing has been written to
+/// value.
+///
+/// For standard options, drivers must always support getting the
+/// option value (if they support getting option values at all) via
+/// the type specified in the option. (For example, an option set via
+/// SetOptionDouble must be retrievable via GetOptionDouble.) Drivers
+/// may also support getting a converted option value via other
+/// getters if needed. (For example, getting the string
+/// representation of a double option.)
+///
+/// \since ADBC API revision 1.1.0
+/// \param[in] database The database.
+/// \param[in] key The option to get.
+/// \param[out] value The option value.
+/// \param[in,out] length The option value length.
+/// \param[out] error An optional location to return an error
+/// message if necessary.
+/// \return ADBC_STATUS_NOT_FOUND if the option is not recognized.
+ADBC_EXPORT
+AdbcStatusCode AdbcDatabaseGetOptionBytes(struct AdbcDatabase* database, const char* key,
+ uint8_t* value, size_t* length,
+ struct AdbcError* error);
+
+/// \brief Get a double option of the database.
+///
+/// This must always be thread-safe (other operations are not).
+///
+/// For standard options, drivers must always support getting the
+/// option value (if they support getting option values at all) via
+/// the type specified in the option. (For example, an option set via
+/// SetOptionDouble must be retrievable via GetOptionDouble.) Drivers
+/// may also support getting a converted option value via other
+/// getters if needed. (For example, getting the double
+/// representation of an integer option.)
+///
+/// \since ADBC API revision 1.1.0
+/// \param[in] database The database.
+/// \param[in] key The option to get.
+/// \param[out] value The option value.
+/// \param[out] error An optional location to return an error
+/// message if necessary.
+/// \return ADBC_STATUS_NOT_FOUND if the option is not recognized.
+ADBC_EXPORT
+AdbcStatusCode AdbcDatabaseGetOptionDouble(struct AdbcDatabase* database, const char* key,
+ double* value, struct AdbcError* error);
+
+/// \brief Get an integer option of the database.
+///
+/// This must always be thread-safe (other operations are not).
+///
+/// For standard options, drivers must always support getting the
+/// option value (if they support getting option values at all) via
+/// the type specified in the option. (For example, an option set via
+/// SetOptionDouble must be retrievable via GetOptionDouble.) Drivers
+/// may also support getting a converted option value via other
+/// getters if needed. (For example, getting the integer
+/// representation of a double option.)
+///
+/// \since ADBC API revision 1.1.0
+/// \param[in] database The database.
+/// \param[in] key The option to get.
+/// \param[out] value The option value.
+/// \param[out] error An optional location to return an error
+/// message if necessary.
+/// \return ADBC_STATUS_NOT_FOUND if the option is not recognized.
+ADBC_EXPORT
+AdbcStatusCode AdbcDatabaseGetOptionInt(struct AdbcDatabase* database, const char* key,
+ int64_t* value, struct AdbcError* error);
+
/// \brief Set a char* option.
///
/// Options may be set before AdbcDatabaseInit. Some drivers may
/// support setting options after initialization as well.
///
+/// \param[in] database The database.
+/// \param[in] key The option to set.
+/// \param[in] value The option value.
+/// \param[out] error An optional location to return an error
+/// message if necessary.
/// \return ADBC_STATUS_NOT_IMPLEMENTED if the option is not recognized
ADBC_EXPORT
AdbcStatusCode AdbcDatabaseSetOption(struct AdbcDatabase* database, const char* key,
const char* value, struct AdbcError* error);
+/// \brief Set a bytestring option on a database.
+///
+/// \since ADBC API revision 1.1.0
+/// \param[in] database The database.
+/// \param[in] key The option to set.
+/// \param[in] value The option value.
+/// \param[in] length The option value length.
+/// \param[out] error An optional location to return an error
+/// message if necessary.
+/// \return ADBC_STATUS_NOT_IMPLEMENTED if the option is not recognized
+ADBC_EXPORT
+AdbcStatusCode AdbcDatabaseSetOptionBytes(struct AdbcDatabase* database, const char* key,
+ const uint8_t* value, size_t length,
+ struct AdbcError* error);
+
+/// \brief Set a double option on a database.
+///
+/// \since ADBC API revision 1.1.0
+/// \param[in] database The database.
+/// \param[in] key The option to set.
+/// \param[in] value The option value.
+/// \param[out] error An optional location to return an error
+/// message if necessary.
+/// \return ADBC_STATUS_NOT_IMPLEMENTED if the option is not recognized
+ADBC_EXPORT
+AdbcStatusCode AdbcDatabaseSetOptionDouble(struct AdbcDatabase* database, const char* key,
+ double value, struct AdbcError* error);
+
+/// \brief Set an integer option on a database.
+///
+/// \since ADBC API revision 1.1.0
+/// \param[in] database The database.
+/// \param[in] key The option to set.
+/// \param[in] value The option value.
+/// \param[out] error An optional location to return an error
+/// message if necessary.
+/// \return ADBC_STATUS_NOT_IMPLEMENTED if the option is not recognized
+ADBC_EXPORT
+AdbcStatusCode AdbcDatabaseSetOptionInt(struct AdbcDatabase* database, const char* key,
+ int64_t value, struct AdbcError* error);
+
/// \brief Finish setting options and initialize the database.
///
/// Some drivers may support setting options after initialization
@@ -730,11 +1299,65 @@ AdbcStatusCode AdbcConnectionNew(struct AdbcConnection* connection,
/// Options may be set before AdbcConnectionInit. Some drivers may
/// support setting options after initialization as well.
///
+/// \param[in] connection The database connection.
+/// \param[in] key The option to set.
+/// \param[in] value The option value.
+/// \param[out] error An optional location to return an error
+/// message if necessary.
/// \return ADBC_STATUS_NOT_IMPLEMENTED if the option is not recognized
ADBC_EXPORT
AdbcStatusCode AdbcConnectionSetOption(struct AdbcConnection* connection, const char* key,
const char* value, struct AdbcError* error);
+/// \brief Set a bytestring option on a connection.
+///
+/// \since ADBC API revision 1.1.0
+/// \param[in] connection The connection.
+/// \param[in] key The option to set.
+/// \param[in] value The option value.
+/// \param[in] length The option value length.
+/// \param[out] error An optional location to return an error
+/// message if necessary.
+/// \return ADBC_STATUS_NOT_IMPLEMENTED if the option is not recognized
+ADBC_EXPORT
+AdbcStatusCode AdbcConnectionSetOptionBytes(struct AdbcConnection* connection,
+ const char* key, const uint8_t* value,
+ size_t length, struct AdbcError* error);
+
+/// \brief Set an integer option.
+///
+/// Options may be set before AdbcConnectionInit. Some drivers may
+/// support setting options after initialization as well.
+///
+/// \since ADBC API revision 1.1.0
+/// \param[in] connection The database connection.
+/// \param[in] key The option to set.
+/// \param[in] value The option value.
+/// \param[out] error An optional location to return an error
+/// message if necessary.
+/// \return ADBC_STATUS_NOT_IMPLEMENTED if the option is not recognized
+ADBC_EXPORT
+AdbcStatusCode AdbcConnectionSetOptionInt(struct AdbcConnection* connection,
+ const char* key, int64_t value,
+ struct AdbcError* error);
+
+/// \brief Set a double option.
+///
+/// Options may be set before AdbcConnectionInit. Some drivers may
+/// support setting options after initialization as well.
+///
+/// \since ADBC API revision 1.1.0
+/// \param[in] connection The database connection.
+/// \param[in] key The option to set.
+/// \param[in] value The option value.
+/// \param[out] error An optional location to return an error
+/// message if necessary.
+/// \return ADBC_STATUS_NOT_IMPLEMENTED if the option is not recognized
+ADBC_EXPORT
+AdbcStatusCode AdbcConnectionSetOptionDouble(struct AdbcConnection* connection,
+ const char* key, double value,
+ struct AdbcError* error);
+
/// \brief Finish setting options and initialize the connection.
///
/// Some drivers may support setting options after initialization
@@ -752,6 +1375,30 @@ ADBC_EXPORT
AdbcStatusCode AdbcConnectionRelease(struct AdbcConnection* connection,
struct AdbcError* error);
+/// \brief Cancel the in-progress operation on a connection.
+///
+/// This can be called during AdbcConnectionGetObjects (or similar),
+/// or while consuming an ArrowArrayStream returned from such.
+/// Calling this function should make the other functions return
+/// ADBC_STATUS_CANCELLED (from ADBC functions) or ECANCELED (from
+/// methods of ArrowArrayStream). (It is not guaranteed to, for
+/// instance, the result set may be buffered in memory already.)
+///
+/// This must always be thread-safe (other operations are not). It is
+/// not necessarily signal-safe.
+///
+/// \since ADBC API revision 1.1.0
+///
+/// \param[in] connection The connection to cancel.
+/// \param[out] error An optional location to return an error
+/// message if necessary.
+///
+/// \return ADBC_STATUS_INVALID_STATE if there is no operation to cancel.
+/// \return ADBC_STATUS_UNKNOWN if the operation could not be cancelled.
+ADBC_EXPORT
+AdbcStatusCode AdbcConnectionCancel(struct AdbcConnection* connection,
+ struct AdbcError* error);
+
/// \defgroup adbc-connection-metadata Metadata
/// Functions for retrieving metadata about the database.
///
@@ -765,6 +1412,8 @@ AdbcStatusCode AdbcConnectionRelease(struct AdbcConnection* connection,
/// concurrent active statements and it must execute a SQL query
/// internally in order to implement the metadata function).
///
+/// This AdbcConnection must outlive the returned ArrowArrayStream.
+///
/// Some functions accept "search pattern" arguments, which are
/// strings that can contain the special character "%" to match zero
/// or more characters, or "_" to match exactly one character. (See
@@ -799,6 +1448,10 @@ AdbcStatusCode AdbcConnectionRelease(struct AdbcConnection* connection,
/// for ADBC usage. Drivers/vendors will ignore requests for
/// unrecognized codes (the row will be omitted from the result).
///
+/// Since ADBC 1.1.0: the range [500, 1_000) is reserved for "XDBC"
+/// information, which is the same metadata provided by the same info
+/// code range in the Arrow Flight SQL GetSqlInfo RPC.
+///
/// \param[in] connection The connection to query.
/// \param[in] info_codes A list of metadata codes to fetch, or NULL
/// to fetch all.
@@ -808,7 +1461,7 @@ AdbcStatusCode AdbcConnectionRelease(struct AdbcConnection* connection,
/// \param[out] error Error details, if an error occurs.
ADBC_EXPORT
AdbcStatusCode AdbcConnectionGetInfo(struct AdbcConnection* connection,
- uint32_t* info_codes, size_t info_codes_length,
+ const uint32_t* info_codes, size_t info_codes_length,
struct ArrowArrayStream* out,
struct AdbcError* error);
@@ -891,6 +1544,8 @@ AdbcStatusCode AdbcConnectionGetInfo(struct AdbcConnection* connection,
/// | fk_table | utf8 not null |
/// | fk_column_name | utf8 not null |
///
+/// This AdbcConnection must outlive the returned ArrowArrayStream.
+///
/// \param[in] connection The database connection.
/// \param[in] depth The level of nesting to display. If 0, display
/// all levels. If 1, display only catalogs (i.e. catalog_schemas
@@ -922,6 +1577,212 @@ AdbcStatusCode AdbcConnectionGetObjects(struct AdbcConnection* connection, int d
struct ArrowArrayStream* out,
struct AdbcError* error);
+/// \brief Get a string option of the connection.
+///
+/// This must always be thread-safe (other operations are not), though
+/// given the semantics here, it is not recommended to call GetOption
+/// concurrently with itself.
+///
+/// length must be provided and must be the size of the buffer pointed
+/// to by value. If there is sufficient space, the driver will copy
+/// the option value (including the null terminator) to buffer and set
+/// length to the size of the actual value. If the buffer is too
+/// small, no data will be written and length will be set to the
+/// required length.
+///
+/// In other words:
+///
+/// - If output length <= input length, value will contain a value
+/// with length bytes.
+/// - If output length > input length, nothing has been written to
+/// value.
+///
+/// \since ADBC API revision 1.1.0
+/// \param[in] connection The database connection.
+/// \param[in] key The option to get.
+/// \param[out] value The option value.
+/// \param[in,out] length The length of value.
+/// \param[out] error An optional location to return an error
+/// message if necessary.
+/// \return ADBC_STATUS_NOT_FOUND if the option is not recognized.
+ADBC_EXPORT
+AdbcStatusCode AdbcConnectionGetOption(struct AdbcConnection* connection, const char* key,
+ char* value, size_t* length,
+ struct AdbcError* error);
+
+/// \brief Get a bytestring option of the connection.
+///
+/// This must always be thread-safe (other operations are not), though
+/// given the semantics here, it is not recommended to call
+/// GetOptionBytes concurrently with itself.
+///
+/// length must be provided and must be the size of the buffer pointed
+/// to by value. If there is sufficient space, the driver will copy
+/// the option value to buffer and set length to the size of the
+/// actual value. If the buffer is too small, no data will be written
+/// and length will be set to the required length.
+///
+/// In other words:
+///
+/// - If output length <= input length, value will contain a value
+/// with length bytes.
+/// - If output length > input length, nothing has been written to
+/// value.
+///
+/// For standard options, drivers must always support getting the
+/// option value (if they support getting option values at all) via
+/// the type specified in the option. (For example, an option set via
+/// SetOptionDouble must be retrievable via GetOptionDouble.) Drivers
+/// may also support getting a converted option value via other
+/// getters if needed. (For example, getting the string
+/// representation of a double option.)
+///
+/// \since ADBC API revision 1.1.0
+/// \param[in] connection The connection.
+/// \param[in] key The option to get.
+/// \param[out] value The option value.
+/// \param[in,out] length The option value length.
+/// \param[out] error An optional location to return an error
+/// message if necessary.
+/// \return ADBC_STATUS_NOT_FOUND if the option is not recognized.
+ADBC_EXPORT
+AdbcStatusCode AdbcConnectionGetOptionBytes(struct AdbcConnection* connection,
+ const char* key, uint8_t* value,
+ size_t* length, struct AdbcError* error);
+
+/// \brief Get an integer option of the connection.
+///
+/// This must always be thread-safe (other operations are not).
+///
+/// For standard options, drivers must always support getting the
+/// option value (if they support getting option values at all) via
+/// the type specified in the option. (For example, an option set via
+/// SetOptionDouble must be retrievable via GetOptionDouble.) Drivers
+/// may also support getting a converted option value via other
+/// getters if needed. (For example, getting the string
+/// representation of a double option.)
+///
+/// \since ADBC API revision 1.1.0
+/// \param[in] connection The database connection.
+/// \param[in] key The option to get.
+/// \param[out] value The option value.
+/// \param[out] error An optional location to return an error
+/// message if necessary.
+/// \return ADBC_STATUS_NOT_FOUND if the option is not recognized.
+ADBC_EXPORT
+AdbcStatusCode AdbcConnectionGetOptionInt(struct AdbcConnection* connection,
+ const char* key, int64_t* value,
+ struct AdbcError* error);
+
+/// \brief Get a double option of the connection.
+///
+/// This must always be thread-safe (other operations are not).
+///
+/// For standard options, drivers must always support getting the
+/// option value (if they support getting option values at all) via
+/// the type specified in the option. (For example, an option set via
+/// SetOptionDouble must be retrievable via GetOptionDouble.) Drivers
+/// may also support getting a converted option value via other
+/// getters if needed. (For example, getting the string
+/// representation of a double option.)
+///
+/// \since ADBC API revision 1.1.0
+/// \param[in] connection The database connection.
+/// \param[in] key The option to get.
+/// \param[out] value The option value.
+/// \param[out] error An optional location to return an error
+/// message if necessary.
+/// \return ADBC_STATUS_NOT_FOUND if the option is not recognized.
+ADBC_EXPORT
+AdbcStatusCode AdbcConnectionGetOptionDouble(struct AdbcConnection* connection,
+ const char* key, double* value,
+ struct AdbcError* error);
+
+/// \brief Get statistics about the data distribution of table(s).
+///
+/// The result is an Arrow dataset with the following schema:
+///
+/// | Field Name | Field Type |
+/// |--------------------------|----------------------------------|
+/// | catalog_name | utf8 |
+/// | catalog_db_schemas | list not null |
+///
+/// DB_SCHEMA_SCHEMA is a Struct with fields:
+///
+/// | Field Name | Field Type |
+/// |--------------------------|----------------------------------|
+/// | db_schema_name | utf8 |
+/// | db_schema_statistics | list not null |
+///
+/// STATISTICS_SCHEMA is a Struct with fields:
+///
+/// | Field Name | Field Type | Comments |
+/// |--------------------------|----------------------------------| -------- |
+/// | table_name | utf8 not null | |
+/// | column_name | utf8 | (1) |
+/// | statistic_key | int16 not null | (2) |
+/// | statistic_value | VALUE_SCHEMA not null | |
+/// | statistic_is_approximate | bool not null | (3) |
+///
+/// 1. If null, then the statistic applies to the entire table.
+/// 2. A dictionary-encoded statistic name (although we do not use the Arrow
+/// dictionary type). Values in [0, 1024) are reserved for ADBC. Other
+/// values are for implementation-specific statistics. For the definitions
+/// of predefined statistic types, see \ref adbc-table-statistics. To get
+/// driver-specific statistic names, use AdbcConnectionGetStatisticNames.
+/// 3. If true, then the value is approximate or best-effort.
+///
+/// VALUE_SCHEMA is a dense union with members:
+///
+/// | Field Name | Field Type |
+/// |--------------------------|----------------------------------|
+/// | int64 | int64 |
+/// | uint64 | uint64 |
+/// | float64 | float64 |
+/// | binary | binary |
+///
+/// This AdbcConnection must outlive the returned ArrowArrayStream.
+///
+/// \since ADBC API revision 1.1.0
+/// \param[in] connection The database connection.
+/// \param[in] catalog The catalog (or nullptr). May be a search
+/// pattern (see section documentation).
+/// \param[in] db_schema The database schema (or nullptr). May be a
+/// search pattern (see section documentation).
+/// \param[in] table_name The table name (or nullptr). May be a
+/// search pattern (see section documentation).
+/// \param[in] approximate If zero, request exact values of
+/// statistics, else allow for best-effort, approximate, or cached
+/// values. The database may return approximate values regardless,
+/// as indicated in the result. Requesting exact values may be
+/// expensive or unsupported.
+/// \param[out] out The result set.
+/// \param[out] error Error details, if an error occurs.
+ADBC_EXPORT
+AdbcStatusCode AdbcConnectionGetStatistics(struct AdbcConnection* connection,
+ const char* catalog, const char* db_schema,
+ const char* table_name, char approximate,
+ struct ArrowArrayStream* out,
+ struct AdbcError* error);
+
+/// \brief Get the names of statistics specific to this driver.
+///
+/// The result is an Arrow dataset with the following schema:
+///
+/// Field Name | Field Type
+/// ---------------|----------------
+/// statistic_name | utf8 not null
+/// statistic_key | int16 not null
+///
+/// \since ADBC API revision 1.1.0
+/// \param[in] connection The database connection.
+/// \param[out] out The result set.
+/// \param[out] error Error details, if an error occurs.
+ADBC_EXPORT
+AdbcStatusCode AdbcConnectionGetStatisticNames(struct AdbcConnection* connection,
+ struct ArrowArrayStream* out,
+ struct AdbcError* error);
+
/// \brief Get the Arrow schema of a table.
///
/// \param[in] connection The database connection.
@@ -945,6 +1806,8 @@ AdbcStatusCode AdbcConnectionGetTableSchema(struct AdbcConnection* connection,
/// ---------------|--------------
/// table_type | utf8 not null
///
+/// This AdbcConnection must outlive the returned ArrowArrayStream.
+///
/// \param[in] connection The database connection.
/// \param[out] out The result set.
/// \param[out] error Error details, if an error occurs.
@@ -973,6 +1836,8 @@ AdbcStatusCode AdbcConnectionGetTableTypes(struct AdbcConnection* connection,
///
/// A partition can be retrieved from AdbcPartitions.
///
+/// This AdbcConnection must outlive the returned ArrowArrayStream.
+///
/// \param[in] connection The connection to use. This does not have
/// to be the same connection that the partition was created on.
/// \param[in] serialized_partition The partition descriptor.
@@ -1042,7 +1907,11 @@ AdbcStatusCode AdbcStatementRelease(struct AdbcStatement* statement,
/// \brief Execute a statement and get the results.
///
-/// This invalidates any prior result sets.
+/// This invalidates any prior result sets. This AdbcStatement must
+/// outlive the returned ArrowArrayStream.
+///
+/// Since ADBC 1.1.0: releasing the returned ArrowArrayStream without
+/// consuming it fully is equivalent to calling AdbcStatementCancel.
///
/// \param[in] statement The statement to execute.
/// \param[out] out The results. Pass NULL if the client does not
@@ -1056,6 +1925,27 @@ AdbcStatusCode AdbcStatementExecuteQuery(struct AdbcStatement* statement,
struct ArrowArrayStream* out,
int64_t* rows_affected, struct AdbcError* error);
+/// \brief Get the schema of the result set of a query without
+/// executing it.
+///
+/// This invalidates any prior result sets.
+///
+/// Depending on the driver, this may require first executing
+/// AdbcStatementPrepare.
+///
+/// \since ADBC API revision 1.1.0
+///
+/// \param[in] statement The statement to execute.
+/// \param[out] out The result schema.
+/// \param[out] error An optional location to return an error
+/// message if necessary.
+///
+/// \return ADBC_STATUS_NOT_IMPLEMENTED if the driver does not support this.
+ADBC_EXPORT
+AdbcStatusCode AdbcStatementExecuteSchema(struct AdbcStatement* statement,
+ struct ArrowSchema* schema,
+ struct AdbcError* error);
+
/// \brief Turn this statement into a prepared statement to be
/// executed multiple times.
///
@@ -1138,6 +2028,158 @@ AdbcStatusCode AdbcStatementBindStream(struct AdbcStatement* statement,
struct ArrowArrayStream* stream,
struct AdbcError* error);
+/// \brief Cancel execution of an in-progress query.
+///
+/// This can be called during AdbcStatementExecuteQuery (or similar),
+/// or while consuming an ArrowArrayStream returned from such.
+/// Calling this function should make the other functions return
+/// ADBC_STATUS_CANCELLED (from ADBC functions) or ECANCELED (from
+/// methods of ArrowArrayStream). (It is not guaranteed to, for
+/// instance, the result set may be buffered in memory already.)
+///
+/// This must always be thread-safe (other operations are not). It is
+/// not necessarily signal-safe.
+///
+/// \since ADBC API revision 1.1.0
+///
+/// \param[in] statement The statement to cancel.
+/// \param[out] error An optional location to return an error
+/// message if necessary.
+///
+/// \return ADBC_STATUS_INVALID_STATE if there is no query to cancel.
+/// \return ADBC_STATUS_UNKNOWN if the query could not be cancelled.
+ADBC_EXPORT
+AdbcStatusCode AdbcStatementCancel(struct AdbcStatement* statement,
+ struct AdbcError* error);
+
+/// \brief Get a string option of the statement.
+///
+/// This must always be thread-safe (other operations are not), though
+/// given the semantics here, it is not recommended to call GetOption
+/// concurrently with itself.
+///
+/// length must be provided and must be the size of the buffer pointed
+/// to by value. If there is sufficient space, the driver will copy
+/// the option value (including the null terminator) to buffer and set
+/// length to the size of the actual value. If the buffer is too
+/// small, no data will be written and length will be set to the
+/// required length.
+///
+/// In other words:
+///
+/// - If output length <= input length, value will contain a value
+/// with length bytes.
+/// - If output length > input length, nothing has been written to
+/// value.
+///
+/// For standard options, drivers must always support getting the
+/// option value (if they support getting option values at all) via
+/// the type specified in the option. (For example, an option set via
+/// SetOptionDouble must be retrievable via GetOptionDouble.) Drivers
+/// may also support getting a converted option value via other
+/// getters if needed. (For example, getting the string
+/// representation of a double option.)
+///
+/// \since ADBC API revision 1.1.0
+/// \param[in] statement The statement.
+/// \param[in] key The option to get.
+/// \param[out] value The option value.
+/// \param[in,out] length The length of value.
+/// \param[out] error An optional location to return an error
+/// message if necessary.
+/// \return ADBC_STATUS_NOT_FOUND if the option is not recognized.
+ADBC_EXPORT
+AdbcStatusCode AdbcStatementGetOption(struct AdbcStatement* statement, const char* key,
+ char* value, size_t* length,
+ struct AdbcError* error);
+
+/// \brief Get a bytestring option of the statement.
+///
+/// This must always be thread-safe (other operations are not), though
+/// given the semantics here, it is not recommended to call
+/// GetOptionBytes concurrently with itself.
+///
+/// length must be provided and must be the size of the buffer pointed
+/// to by value. If there is sufficient space, the driver will copy
+/// the option value to buffer and set length to the size of the
+/// actual value. If the buffer is too small, no data will be written
+/// and length will be set to the required length.
+///
+/// In other words:
+///
+/// - If output length <= input length, value will contain a value
+/// with length bytes.
+/// - If output length > input length, nothing has been written to
+/// value.
+///
+/// For standard options, drivers must always support getting the
+/// option value (if they support getting option values at all) via
+/// the type specified in the option. (For example, an option set via
+/// SetOptionDouble must be retrievable via GetOptionDouble.) Drivers
+/// may also support getting a converted option value via other
+/// getters if needed. (For example, getting the string
+/// representation of a double option.)
+///
+/// \since ADBC API revision 1.1.0
+/// \param[in] statement The statement.
+/// \param[in] key The option to get.
+/// \param[out] value The option value.
+/// \param[in,out] length The option value length.
+/// \param[out] error An optional location to return an error
+/// message if necessary.
+/// \return ADBC_STATUS_NOT_FOUND if the option is not recognized.
+ADBC_EXPORT
+AdbcStatusCode AdbcStatementGetOptionBytes(struct AdbcStatement* statement,
+ const char* key, uint8_t* value,
+ size_t* length, struct AdbcError* error);
+
+/// \brief Get an integer option of the statement.
+///
+/// This must always be thread-safe (other operations are not).
+///
+/// For standard options, drivers must always support getting the
+/// option value (if they support getting option values at all) via
+/// the type specified in the option. (For example, an option set via
+/// SetOptionDouble must be retrievable via GetOptionDouble.) Drivers
+/// may also support getting a converted option value via other
+/// getters if needed. (For example, getting the string
+/// representation of a double option.)
+///
+/// \since ADBC API revision 1.1.0
+/// \param[in] statement The statement.
+/// \param[in] key The option to get.
+/// \param[out] value The option value.
+/// \param[out] error An optional location to return an error
+/// message if necessary.
+/// \return ADBC_STATUS_NOT_FOUND if the option is not recognized.
+ADBC_EXPORT
+AdbcStatusCode AdbcStatementGetOptionInt(struct AdbcStatement* statement, const char* key,
+ int64_t* value, struct AdbcError* error);
+
+/// \brief Get a double option of the statement.
+///
+/// This must always be thread-safe (other operations are not).
+///
+/// For standard options, drivers must always support getting the
+/// option value (if they support getting option values at all) via
+/// the type specified in the option. (For example, an option set via
+/// SetOptionDouble must be retrievable via GetOptionDouble.) Drivers
+/// may also support getting a converted option value via other
+/// getters if needed. (For example, getting the string
+/// representation of a double option.)
+///
+/// \since ADBC API revision 1.1.0
+/// \param[in] statement The statement.
+/// \param[in] key The option to get.
+/// \param[out] value The option value.
+/// \param[out] error An optional location to return an error
+/// message if necessary.
+/// \return ADBC_STATUS_NOT_FOUND if the option is not recognized.
+ADBC_EXPORT
+AdbcStatusCode AdbcStatementGetOptionDouble(struct AdbcStatement* statement,
+ const char* key, double* value,
+ struct AdbcError* error);
+
/// \brief Get the schema for bound parameters.
///
/// This retrieves an Arrow schema describing the number, names, and
@@ -1159,10 +2201,58 @@ AdbcStatusCode AdbcStatementGetParameterSchema(struct AdbcStatement* statement,
struct AdbcError* error);
/// \brief Set a string option on a statement.
+/// \param[in] statement The statement.
+/// \param[in] key The option to set.
+/// \param[in] value The option value.
+/// \param[out] error An optional location to return an error
+/// message if necessary.
+/// \return ADBC_STATUS_NOT_IMPLEMENTED if the option is not recognized.
ADBC_EXPORT
AdbcStatusCode AdbcStatementSetOption(struct AdbcStatement* statement, const char* key,
const char* value, struct AdbcError* error);
+/// \brief Set a bytestring option on a statement.
+///
+/// \since ADBC API revision 1.1.0
+/// \param[in] statement The statement.
+/// \param[in] key The option to set.
+/// \param[in] value The option value.
+/// \param[in] length The option value length.
+/// \param[out] error An optional location to return an error
+/// message if necessary.
+/// \return ADBC_STATUS_NOT_IMPLEMENTED if the option is not recognized
+ADBC_EXPORT
+AdbcStatusCode AdbcStatementSetOptionBytes(struct AdbcStatement* statement,
+ const char* key, const uint8_t* value,
+ size_t length, struct AdbcError* error);
+
+/// \brief Set an integer option on a statement.
+///
+/// \since ADBC API revision 1.1.0
+/// \param[in] statement The statement.
+/// \param[in] key The option to set.
+/// \param[in] value The option value.
+/// \param[out] error An optional location to return an error
+/// message if necessary.
+/// \return ADBC_STATUS_NOT_IMPLEMENTED if the option is not recognized
+ADBC_EXPORT
+AdbcStatusCode AdbcStatementSetOptionInt(struct AdbcStatement* statement, const char* key,
+ int64_t value, struct AdbcError* error);
+
+/// \brief Set a double option on a statement.
+///
+/// \since ADBC API revision 1.1.0
+/// \param[in] statement The statement.
+/// \param[in] key The option to set.
+/// \param[in] value The option value.
+/// \param[out] error An optional location to return an error
+/// message if necessary.
+/// \return ADBC_STATUS_NOT_IMPLEMENTED if the option is not recognized
+ADBC_EXPORT
+AdbcStatusCode AdbcStatementSetOptionDouble(struct AdbcStatement* statement,
+ const char* key, double value,
+ struct AdbcError* error);
+
/// \addtogroup adbc-statement-partition
/// @{
@@ -1198,7 +2288,15 @@ AdbcStatusCode AdbcStatementExecutePartitions(struct AdbcStatement* statement,
/// driver.
///
/// Although drivers may choose any name for this function, the
-/// recommended name is "AdbcDriverInit".
+/// recommended name is "AdbcDriverInit", or a name derived from the
+/// name of the driver's shared library as follows: remove the 'lib'
+/// prefix (on Unix systems) and all file extensions, then PascalCase
+/// the driver name, append Init, and prepend Adbc (if not already
+/// there). For example:
+///
+/// - libadbc_driver_sqlite.so.2.0.0 -> AdbcDriverSqliteInit
+/// - adbc_driver_sqlite.dll -> AdbcDriverSqliteInit
+/// - proprietary_driver.dll -> AdbcProprietaryDriverInit
///
/// \param[in] version The ADBC revision to attempt to initialize (see
/// ADBC_VERSION_1_0_0).
diff --git a/c/driver/common/utils.c b/c/driver/common/utils.c
index dfac14f5e4..71d9e7ef01 100644
--- a/c/driver/common/utils.c
+++ b/c/driver/common/utils.c
@@ -17,15 +17,80 @@
#include "utils.h"
+#include
#include
-#include
#include
#include
#include
-#include
+#include
+
+static size_t kErrorBufferSize = 1024;
+
+int AdbcStatusCodeToErrno(AdbcStatusCode code) {
+ switch (code) {
+ case ADBC_STATUS_OK:
+ return 0;
+ case ADBC_STATUS_UNKNOWN:
+ return EIO;
+ case ADBC_STATUS_NOT_IMPLEMENTED:
+ return ENOTSUP;
+ case ADBC_STATUS_NOT_FOUND:
+ return ENOENT;
+ case ADBC_STATUS_ALREADY_EXISTS:
+ return EEXIST;
+ case ADBC_STATUS_INVALID_ARGUMENT:
+ case ADBC_STATUS_INVALID_STATE:
+ return EINVAL;
+ case ADBC_STATUS_INVALID_DATA:
+ case ADBC_STATUS_INTEGRITY:
+ case ADBC_STATUS_INTERNAL:
+ case ADBC_STATUS_IO:
+ return EIO;
+ case ADBC_STATUS_CANCELLED:
+ return ECANCELED;
+ case ADBC_STATUS_TIMEOUT:
+ return ETIMEDOUT;
+ case ADBC_STATUS_UNAUTHENTICATED:
+ // FreeBSD/macOS have EAUTH, but not other platforms
+ case ADBC_STATUS_UNAUTHORIZED:
+ return EACCES;
+ default:
+ return EIO;
+ }
+}
+
+/// For ADBC 1.1.0, the structure held in private_data.
+struct AdbcErrorDetails {
+ char* message;
+
+ // The metadata keys (may be NULL).
+ char** keys;
+ // The metadata values (may be NULL).
+ uint8_t** values;
+ // The metadata value lengths (may be NULL).
+ size_t* lengths;
+ // The number of initialized metadata.
+ int count;
+ // The length of the keys/values/lengths arrays above.
+ int capacity;
+};
+
+static void ReleaseErrorWithDetails(struct AdbcError* error) {
+ struct AdbcErrorDetails* details = (struct AdbcErrorDetails*)error->private_data;
+ free(details->message);
-static size_t kErrorBufferSize = 256;
+ for (int i = 0; i < details->count; i++) {
+ free(details->keys[i]);
+ free(details->values[i]);
+ }
+
+ free(details->keys);
+ free(details->values);
+ free(details->lengths);
+ free(error->private_data);
+ *error = ADBC_ERROR_INIT;
+}
static void ReleaseError(struct AdbcError* error) {
free(error->message);
@@ -34,20 +99,126 @@ static void ReleaseError(struct AdbcError* error) {
}
void SetError(struct AdbcError* error, const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ SetErrorVariadic(error, format, args);
+ va_end(args);
+}
+
+void SetErrorVariadic(struct AdbcError* error, const char* format, va_list args) {
if (!error) return;
if (error->release) {
// TODO: combine the errors if possible
error->release(error);
}
- error->message = malloc(kErrorBufferSize);
- if (!error->message) return;
- error->release = &ReleaseError;
+ if (error->vendor_code == ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA) {
+ error->private_data = malloc(sizeof(struct AdbcErrorDetails));
+ if (!error->private_data) return;
+
+ struct AdbcErrorDetails* details = (struct AdbcErrorDetails*)error->private_data;
+
+ details->message = malloc(kErrorBufferSize);
+ if (!details->message) {
+ free(details);
+ return;
+ }
+ details->keys = NULL;
+ details->values = NULL;
+ details->lengths = NULL;
+ details->count = 0;
+ details->capacity = 0;
+
+ error->message = details->message;
+ error->release = &ReleaseErrorWithDetails;
+ } else {
+ error->message = malloc(kErrorBufferSize);
+ if (!error->message) return;
+
+ error->release = &ReleaseError;
+ }
- va_list args;
- va_start(args, format);
vsnprintf(error->message, kErrorBufferSize, format, args);
- va_end(args);
+}
+
+void AppendErrorDetail(struct AdbcError* error, const char* key, const uint8_t* detail,
+ size_t detail_length) {
+ if (error->release != ReleaseErrorWithDetails) return;
+
+ struct AdbcErrorDetails* details = (struct AdbcErrorDetails*)error->private_data;
+ if (details->count >= details->capacity) {
+ int new_capacity = (details->capacity == 0) ? 4 : (2 * details->capacity);
+ char** new_keys = calloc(new_capacity, sizeof(char*));
+ if (!new_keys) {
+ return;
+ }
+
+ uint8_t** new_values = calloc(new_capacity, sizeof(uint8_t*));
+ if (!new_values) {
+ free(new_keys);
+ return;
+ }
+
+ size_t* new_lengths = calloc(new_capacity, sizeof(size_t*));
+ if (!new_lengths) {
+ free(new_keys);
+ free(new_values);
+ return;
+ }
+
+ memcpy(new_keys, details->keys, sizeof(char*) * details->count);
+ free(details->keys);
+ details->keys = new_keys;
+
+ memcpy(new_values, details->values, sizeof(uint8_t*) * details->count);
+ free(details->values);
+ details->values = new_values;
+
+ memcpy(new_lengths, details->lengths, sizeof(size_t) * details->count);
+ free(details->lengths);
+ details->lengths = new_lengths;
+
+ details->capacity = new_capacity;
+ }
+
+ char* key_data = strdup(key);
+ if (!key_data) return;
+ uint8_t* value_data = malloc(detail_length);
+ if (!value_data) {
+ free(key_data);
+ return;
+ }
+ memcpy(value_data, detail, detail_length);
+
+ int index = details->count;
+ details->keys[index] = key_data;
+ details->values[index] = value_data;
+ details->lengths[index] = detail_length;
+
+ details->count++;
+}
+
+int CommonErrorGetDetailCount(const struct AdbcError* error) {
+ if (error->release != ReleaseErrorWithDetails) {
+ return 0;
+ }
+ struct AdbcErrorDetails* details = (struct AdbcErrorDetails*)error->private_data;
+ return details->count;
+}
+
+struct AdbcErrorDetail CommonErrorGetDetail(const struct AdbcError* error, int index) {
+ if (error->release != ReleaseErrorWithDetails) {
+ return (struct AdbcErrorDetail){NULL, NULL, 0};
+ }
+ struct AdbcErrorDetails* details = (struct AdbcErrorDetails*)error->private_data;
+ if (index < 0 || index >= details->count) {
+ return (struct AdbcErrorDetail){NULL, NULL, 0};
+ }
+ return (struct AdbcErrorDetail){
+ .key = details->keys[index],
+ .value = details->values[index],
+ .value_length = details->lengths[index],
+ };
}
struct SingleBatchArrayStream {
@@ -244,6 +415,19 @@ AdbcStatusCode AdbcConnectionGetInfoAppendString(struct ArrowArray* array,
return ADBC_STATUS_OK;
}
+AdbcStatusCode AdbcConnectionGetInfoAppendInt(struct ArrowArray* array,
+ uint32_t info_code, int64_t info_value,
+ struct AdbcError* error) {
+ CHECK_NA(INTERNAL, ArrowArrayAppendUInt(array->children[0], info_code), error);
+ // Append to type variant
+ CHECK_NA(INTERNAL, ArrowArrayAppendInt(array->children[1]->children[2], info_value),
+ error);
+ // Append type code/offset
+ CHECK_NA(INTERNAL, ArrowArrayFinishUnionElement(array->children[1], /*type_id=*/2),
+ error);
+ return ADBC_STATUS_OK;
+}
+
AdbcStatusCode AdbcInitConnectionObjectsSchema(struct ArrowSchema* schema,
struct AdbcError* error) {
ArrowSchemaInit(schema);
diff --git a/c/driver/common/utils.h b/c/driver/common/utils.h
index 5735bb945f..e3d81cb0f6 100644
--- a/c/driver/common/utils.h
+++ b/c/driver/common/utils.h
@@ -17,6 +17,7 @@
#pragma once
+#include
#include
#include
@@ -27,6 +28,8 @@
extern "C" {
#endif
+int AdbcStatusCodeToErrno(AdbcStatusCode code);
+
// The printf checking attribute doesn't work properly on gcc 4.8
// and results in spurious compiler warnings
#if defined(__clang__) || (defined(__GNUC__) && __GNUC__ >= 5)
@@ -35,10 +38,20 @@ extern "C" {
#define ADBC_CHECK_PRINTF_ATTRIBUTE
#endif
-/// Set error details using a format string.
+/// Set error message using a format string.
void SetError(struct AdbcError* error, const char* format,
...) ADBC_CHECK_PRINTF_ATTRIBUTE;
+/// Set error message using a format string.
+void SetErrorVariadic(struct AdbcError* error, const char* format, va_list args);
+
+/// Add an error detail.
+void AppendErrorDetail(struct AdbcError* error, const char* key, const uint8_t* detail,
+ size_t detail_length);
+
+int CommonErrorGetDetailCount(const struct AdbcError* error);
+struct AdbcErrorDetail CommonErrorGetDetail(const struct AdbcError* error, int index);
+
struct StringBuilder {
char* buffer;
// Not including null terminator
@@ -117,6 +130,9 @@ AdbcStatusCode AdbcConnectionGetInfoAppendString(struct ArrowArray* array,
uint32_t info_code,
const char* info_value,
struct AdbcError* error);
+AdbcStatusCode AdbcConnectionGetInfoAppendInt(struct ArrowArray* array,
+ uint32_t info_code, int64_t info_value,
+ struct AdbcError* error);
AdbcStatusCode AdbcInitConnectionObjectsSchema(struct ArrowSchema* schema,
struct AdbcError* error);
diff --git a/c/driver/common/utils_test.cc b/c/driver/common/utils_test.cc
index 6fa7e254df..d5c202bf2e 100644
--- a/c/driver/common/utils_test.cc
+++ b/c/driver/common/utils_test.cc
@@ -15,6 +15,12 @@
// specific language governing permissions and limitations
// under the License.
+#include
+#include
+#include
+#include
+
+#include
#include
#include "utils.h"
@@ -72,3 +78,92 @@ TEST(TestStringBuilder, TestMultipleAppends) {
StringBuilderReset(&str);
}
+
+TEST(ErrorDetails, Adbc100) {
+ struct AdbcError error;
+ std::memset(&error, 0, ADBC_ERROR_1_1_0_SIZE);
+
+ SetError(&error, "My message");
+
+ ASSERT_EQ(nullptr, error.private_data);
+ ASSERT_EQ(nullptr, error.private_driver);
+
+ {
+ std::string detail = "detail";
+ AppendErrorDetail(&error, "key", reinterpret_cast(detail.data()),
+ detail.size());
+ }
+
+ ASSERT_EQ(0, CommonErrorGetDetailCount(&error));
+ struct AdbcErrorDetail detail = CommonErrorGetDetail(&error, 0);
+ ASSERT_EQ(nullptr, detail.key);
+ ASSERT_EQ(nullptr, detail.value);
+ ASSERT_EQ(0, detail.value_length);
+
+ error.release(&error);
+}
+
+TEST(ErrorDetails, Adbc110) {
+ struct AdbcError error = ADBC_ERROR_INIT;
+ SetError(&error, "My message");
+
+ ASSERT_NE(nullptr, error.private_data);
+ ASSERT_EQ(nullptr, error.private_driver);
+
+ {
+ std::string detail = "detail";
+ AppendErrorDetail(&error, "key", reinterpret_cast(detail.data()),
+ detail.size());
+ }
+
+ ASSERT_EQ(1, CommonErrorGetDetailCount(&error));
+ struct AdbcErrorDetail detail = CommonErrorGetDetail(&error, 0);
+ ASSERT_STREQ("key", detail.key);
+ ASSERT_EQ("detail", std::string_view(reinterpret_cast(detail.value),
+ detail.value_length));
+
+ detail = CommonErrorGetDetail(&error, -1);
+ ASSERT_EQ(nullptr, detail.key);
+ ASSERT_EQ(nullptr, detail.value);
+ ASSERT_EQ(0, detail.value_length);
+
+ detail = CommonErrorGetDetail(&error, 2);
+ ASSERT_EQ(nullptr, detail.key);
+ ASSERT_EQ(nullptr, detail.value);
+ ASSERT_EQ(0, detail.value_length);
+
+ error.release(&error);
+ ASSERT_EQ(nullptr, error.private_data);
+ ASSERT_EQ(nullptr, error.private_driver);
+}
+
+TEST(ErrorDetails, RoundTripValues) {
+ struct AdbcError error = ADBC_ERROR_INIT;
+ SetError(&error, "My message");
+
+ struct Detail {
+ std::string key;
+ std::vector value;
+ };
+
+ std::vector details = {
+ {"x-key-1", {0, 1, 2, 3}}, {"x-key-2", {1, 1}}, {"x-key-3", {128, 129, 200, 0, 1}},
+ {"x-key-4", {97, 98, 99}}, {"x-key-5", {42}},
+ };
+
+ for (const auto& detail : details) {
+ AppendErrorDetail(&error, detail.key.c_str(), detail.value.data(),
+ detail.value.size());
+ }
+
+ ASSERT_EQ(details.size(), CommonErrorGetDetailCount(&error));
+ for (int i = 0; i < static_cast(details.size()); i++) {
+ struct AdbcErrorDetail detail = CommonErrorGetDetail(&error, i);
+ ASSERT_EQ(details[i].key, detail.key);
+ ASSERT_EQ(details[i].value.size(), detail.value_length);
+ ASSERT_THAT(std::vector(detail.value, detail.value + detail.value_length),
+ ::testing::ElementsAreArray(details[i].value));
+ }
+
+ error.release(&error);
+}
diff --git a/c/driver/flightsql/dremio_flightsql_test.cc b/c/driver/flightsql/dremio_flightsql_test.cc
index c128bd49f3..416b8aeaf5 100644
--- a/c/driver/flightsql/dremio_flightsql_test.cc
+++ b/c/driver/flightsql/dremio_flightsql_test.cc
@@ -42,11 +42,11 @@ class DremioFlightSqlQuirks : public adbc_validation::DriverQuirks {
}
std::string BindParameter(int index) const override { return "?"; }
+ bool supports_bulk_ingest(const char* /*mode*/) const override { return false; }
bool supports_concurrent_statements() const override { return true; }
bool supports_transactions() const override { return false; }
bool supports_get_sql_info() const override { return false; }
bool supports_get_objects() const override { return true; }
- bool supports_bulk_ingest() const override { return false; }
bool supports_partitioned_data() const override { return true; }
bool supports_dynamic_parameter_binding() const override { return false; }
};
@@ -87,6 +87,7 @@ class DremioFlightSqlStatementTest : public ::testing::Test,
void SetUp() override { ASSERT_NO_FATAL_FAILURE(SetUpTest()); }
void TearDown() override { ASSERT_NO_FATAL_FAILURE(TearDownTest()); }
+ void TestResultInvalidation() { GTEST_SKIP() << "Dremio generates a CANCELLED"; }
void TestSqlIngestTableEscaping() { GTEST_SKIP() << "Table escaping not implemented"; }
protected:
diff --git a/c/driver/flightsql/sqlite_flightsql_test.cc b/c/driver/flightsql/sqlite_flightsql_test.cc
index b61b47bc6f..46ca69be4a 100644
--- a/c/driver/flightsql/sqlite_flightsql_test.cc
+++ b/c/driver/flightsql/sqlite_flightsql_test.cc
@@ -16,6 +16,7 @@
// under the License.
#include
+#include
#include
#include
@@ -32,6 +33,10 @@
using adbc_validation::IsOkErrno;
using adbc_validation::IsOkStatus;
+extern "C" {
+AdbcStatusCode FlightSQLDriverInit(int, void*, struct AdbcError*);
+}
+
#define CHECK_OK(EXPR) \
do { \
if (auto adbc_status = (EXPR); adbc_status != ADBC_STATUS_OK) { \
@@ -89,11 +94,31 @@ class SqliteFlightSqlQuirks : public adbc_validation::DriverQuirks {
}
std::string BindParameter(int index) const override { return "?"; }
+
+ bool supports_bulk_ingest(const char* /*mode*/) const override { return false; }
bool supports_concurrent_statements() const override { return true; }
bool supports_transactions() const override { return false; }
bool supports_get_sql_info() const override { return true; }
+ std::optional supports_get_sql_info(
+ uint32_t info_code) const override {
+ switch (info_code) {
+ case ADBC_INFO_DRIVER_NAME:
+ return "ADBC Flight SQL Driver - Go";
+ case ADBC_INFO_DRIVER_VERSION:
+ return "(unknown or development build)";
+ case ADBC_INFO_DRIVER_ADBC_VERSION:
+ return ADBC_VERSION_1_1_0;
+ case ADBC_INFO_VENDOR_NAME:
+ return "db_name";
+ case ADBC_INFO_VENDOR_VERSION:
+ return "sqlite 3";
+ case ADBC_INFO_VENDOR_ARROW_VERSION:
+ return "12.0.0";
+ default:
+ return std::nullopt;
+ }
+ }
bool supports_get_objects() const override { return true; }
- bool supports_bulk_ingest() const override { return false; }
bool supports_partitioned_data() const override { return true; }
bool supports_dynamic_parameter_binding() const override { return true; }
};
@@ -209,6 +234,20 @@ TEST_F(SqliteFlightSqlTest, TestGarbageInput) {
ASSERT_THAT(AdbcDatabaseRelease(&database, &error), IsOkStatus(&error));
}
+TEST_F(SqliteFlightSqlTest, AdbcDriverBackwardsCompatibility) {
+ // XXX: sketchy cast
+ auto* driver = static_cast(malloc(ADBC_DRIVER_1_0_0_SIZE));
+ std::memset(driver, 0, ADBC_DRIVER_1_0_0_SIZE);
+
+ ASSERT_THAT(::FlightSQLDriverInit(ADBC_VERSION_1_0_0, driver, &error),
+ IsOkStatus(&error));
+
+ ASSERT_THAT(::FlightSQLDriverInit(424242, driver, &error),
+ adbc_validation::IsStatus(ADBC_STATUS_NOT_IMPLEMENTED, &error));
+
+ free(driver);
+}
+
class SqliteFlightSqlConnectionTest : public ::testing::Test,
public adbc_validation::ConnectionTest {
public:
@@ -237,3 +276,139 @@ class SqliteFlightSqlStatementTest : public ::testing::Test,
SqliteFlightSqlQuirks quirks_;
};
ADBCV_TEST_STATEMENT(SqliteFlightSqlStatementTest)
+
+// Test what happens when using the ADBC 1.1.0 error structure
+TEST_F(SqliteFlightSqlStatementTest, NonexistentTable) {
+ adbc_validation::Handle statement;
+ ASSERT_THAT(AdbcStatementNew(&connection, &statement.value, &error),
+ IsOkStatus(&error));
+
+ ASSERT_THAT(AdbcStatementSetSqlQuery(&statement.value,
+ "SELECT * FROM tabledoesnotexist", &error),
+ IsOkStatus(&error));
+
+ for (auto vendor_code : {0, ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA}) {
+ error.vendor_code = vendor_code;
+ ASSERT_THAT(AdbcStatementExecuteQuery(&statement.value, nullptr, nullptr, &error),
+ adbc_validation::IsStatus(ADBC_STATUS_UNKNOWN, &error));
+ ASSERT_EQ(0, AdbcErrorGetDetailCount(&error));
+ error.release(&error);
+ }
+}
+
+TEST_F(SqliteFlightSqlStatementTest, CancelError) {
+ // Ensure cancellation propagates properly through the Go FFI boundary
+ adbc_validation::Handle statement;
+ ASSERT_THAT(AdbcStatementNew(&connection, &statement.value, &error),
+ IsOkStatus(&error));
+
+ ASSERT_THAT(AdbcStatementSetSqlQuery(&statement.value, "SELECT 1", &error),
+ IsOkStatus(&error));
+
+ adbc_validation::StreamReader reader;
+ ASSERT_THAT(AdbcStatementExecuteQuery(&statement.value, &reader.stream.value,
+ &reader.rows_affected, &error),
+ adbc_validation::IsOkStatus(&error));
+
+ ASSERT_THAT(AdbcStatementCancel(&statement.value, &error),
+ adbc_validation::IsOkStatus(&error));
+
+ ASSERT_NO_FATAL_FAILURE(reader.GetSchema());
+
+ int retcode = 0;
+ while (true) {
+ retcode = reader.MaybeNext();
+ if (retcode != 0 || !reader.array->release) break;
+ }
+
+ ASSERT_EQ(ECANCELED, retcode);
+ AdbcStatusCode status = ADBC_STATUS_OK;
+ const struct AdbcError* adbc_error =
+ AdbcErrorFromArrayStream(&reader.stream.value, &status);
+ ASSERT_NE(nullptr, adbc_error);
+ ASSERT_EQ(ADBC_STATUS_CANCELLED, status);
+}
+
+TEST_F(SqliteFlightSqlStatementTest, RpcError) {
+ // Ensure errors that happen at the start of the stream propagate properly
+ // through the Go FFI boundary
+ adbc_validation::Handle statement;
+ ASSERT_THAT(AdbcStatementNew(&connection, &statement.value, &error),
+ IsOkStatus(&error));
+
+ ASSERT_THAT(AdbcStatementSetSqlQuery(&statement.value, "SELECT", &error),
+ IsOkStatus(&error));
+
+ adbc_validation::StreamReader reader;
+ error.vendor_code = ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA;
+ ASSERT_THAT(AdbcStatementExecuteQuery(&statement.value, &reader.stream.value,
+ &reader.rows_affected, &error),
+ adbc_validation::IsStatus(ADBC_STATUS_UNKNOWN, &error));
+
+ int count = AdbcErrorGetDetailCount(&error);
+ ASSERT_NE(0, count);
+ for (int i = 0; i < count; i++) {
+ struct AdbcErrorDetail detail = AdbcErrorGetDetail(&error, i);
+ ASSERT_NE(nullptr, detail.key);
+ ASSERT_NE(nullptr, detail.value);
+ ASSERT_NE(0, detail.value_length);
+ EXPECT_STREQ("afsql-sqlite-query", detail.key);
+ EXPECT_EQ("SELECT", std::string_view(reinterpret_cast(detail.value),
+ detail.value_length));
+ }
+}
+
+TEST_F(SqliteFlightSqlStatementTest, StreamError) {
+ // Ensure errors that happen during the stream propagate properly through
+ // the Go FFI boundary
+ adbc_validation::Handle statement;
+ ASSERT_THAT(AdbcStatementNew(&connection, &statement.value, &error),
+ IsOkStatus(&error));
+
+ ASSERT_THAT(AdbcStatementSetSqlQuery(&statement.value,
+ R"(
+DROP TABLE IF EXISTS foo;
+CREATE TABLE foo (a INT);
+WITH RECURSIVE sequence(x) AS
+ (SELECT 1 UNION ALL SELECT x+1 FROM sequence LIMIT 1024)
+INSERT INTO foo(a)
+SELECT x FROM sequence;
+INSERT INTO foo(a) VALUES ('foo');)",
+ &error),
+ IsOkStatus(&error));
+ ASSERT_THAT(AdbcStatementExecuteQuery(&statement.value, nullptr, nullptr, &error),
+ adbc_validation::IsOkStatus(&error));
+
+ ASSERT_THAT(AdbcStatementSetSqlQuery(&statement.value, "SELECT * FROM foo", &error),
+ IsOkStatus(&error));
+
+ adbc_validation::StreamReader reader;
+ ASSERT_THAT(AdbcStatementExecuteQuery(&statement.value, &reader.stream.value,
+ &reader.rows_affected, &error),
+ adbc_validation::IsOkStatus(&error));
+
+ ASSERT_NO_FATAL_FAILURE(reader.GetSchema());
+
+ int retcode = 0;
+ while (true) {
+ retcode = reader.MaybeNext();
+ if (retcode != 0 || !reader.array->release) break;
+ }
+
+ ASSERT_NE(0, retcode);
+ AdbcStatusCode status = ADBC_STATUS_OK;
+ const struct AdbcError* adbc_error =
+ AdbcErrorFromArrayStream(&reader.stream.value, &status);
+ ASSERT_NE(nullptr, adbc_error);
+ ASSERT_EQ(ADBC_STATUS_UNKNOWN, status);
+
+ int count = AdbcErrorGetDetailCount(adbc_error);
+ ASSERT_NE(0, count);
+ for (int i = 0; i < count; i++) {
+ struct AdbcErrorDetail detail = AdbcErrorGetDetail(adbc_error, i);
+ ASSERT_NE(nullptr, detail.key);
+ ASSERT_NE(nullptr, detail.value);
+ ASSERT_NE(0, detail.value_length);
+ EXPECT_STREQ("grpc-status-details-bin", detail.key);
+ }
+}
diff --git a/c/driver/postgresql/CMakeLists.txt b/c/driver/postgresql/CMakeLists.txt
index d16979419a..9cf595a386 100644
--- a/c/driver/postgresql/CMakeLists.txt
+++ b/c/driver/postgresql/CMakeLists.txt
@@ -29,6 +29,7 @@ endif()
add_arrow_lib(adbc_driver_postgresql
SOURCES
connection.cc
+ error.cc
database.cc
postgresql.cc
statement.cc
diff --git a/c/driver/postgresql/connection.cc b/c/driver/postgresql/connection.cc
index 08ff9027c3..c106ffca78 100644
--- a/c/driver/postgresql/connection.cc
+++ b/c/driver/postgresql/connection.cc
@@ -19,6 +19,7 @@
#include
#include
+#include
#include
#include
#include
@@ -32,29 +33,43 @@
#include "common/utils.h"
#include "database.h"
+#include "error.h"
+namespace adbcpq {
namespace {
static const uint32_t kSupportedInfoCodes[] = {
- ADBC_INFO_VENDOR_NAME, ADBC_INFO_VENDOR_VERSION, ADBC_INFO_DRIVER_NAME,
- ADBC_INFO_DRIVER_VERSION, ADBC_INFO_DRIVER_ARROW_VERSION,
+ ADBC_INFO_VENDOR_NAME, ADBC_INFO_VENDOR_VERSION,
+ ADBC_INFO_DRIVER_NAME, ADBC_INFO_DRIVER_VERSION,
+ ADBC_INFO_DRIVER_ARROW_VERSION, ADBC_INFO_DRIVER_ADBC_VERSION,
};
static const std::unordered_map kPgTableTypes = {
{"table", "r"}, {"view", "v"}, {"materialized_view", "m"},
{"toast_table", "t"}, {"foreign_table", "f"}, {"partitioned_table", "p"}};
+/// \brief A single column in a single row of a result set.
struct PqRecord {
const char* data;
const int len;
const bool is_null;
+
+ // XXX: can't use optional due to R
+ std::pair ParseDouble() const {
+ char* end;
+ double result = std::strtod(data, &end);
+ if (errno != 0 || end == data) {
+ return std::make_pair(false, 0.0);
+ }
+ return std::make_pair(true, result);
+ }
};
// Used by PqResultHelper to provide index-based access to the records within each
-// row of a pg_result
+// row of a PGresult
class PqResultRow {
public:
- PqResultRow(pg_result* result, int row_num) : result_(result), row_num_(row_num) {
+ PqResultRow(PGresult* result, int row_num) : result_(result), row_num_(row_num) {
ncols_ = PQnfields(result);
}
@@ -68,7 +83,7 @@ class PqResultRow {
}
private:
- pg_result* result_ = nullptr;
+ PGresult* result_ = nullptr;
int row_num_;
int ncols_;
};
@@ -94,10 +109,11 @@ class PqResultHelper {
PGresult* result =
PQprepare(conn_, /*stmtName=*/"", query_.c_str(), param_values_.size(), NULL);
if (PQresultStatus(result) != PGRES_COMMAND_OK) {
- SetError(error_, "[libpq] Failed to prepare query: %s\nQuery was:%s",
- PQerrorMessage(conn_), query_.c_str());
+ AdbcStatusCode code =
+ SetError(error_, result, "[libpq] Failed to prepare query: %s\nQuery was:%s",
+ PQerrorMessage(conn_), query_.c_str());
PQclear(result);
- return ADBC_STATUS_IO;
+ return code;
}
PQclear(result);
@@ -114,9 +130,12 @@ class PqResultHelper {
result_ = PQexecPrepared(conn_, "", param_values_.size(), param_c_strs.data(), NULL,
NULL, 0);
- if (PQresultStatus(result_) != PGRES_TUPLES_OK) {
- SetError(error_, "[libpq] Failed to execute query: %s", PQerrorMessage(conn_));
- return ADBC_STATUS_IO;
+ ExecStatusType status = PQresultStatus(result_);
+ if (status != PGRES_TUPLES_OK && status != PGRES_COMMAND_OK) {
+ AdbcStatusCode error =
+ SetError(error_, result_, "[libpq] Failed to execute query '%s': %s",
+ query_.c_str(), PQerrorMessage(conn_));
+ return error;
}
return ADBC_STATUS_OK;
@@ -164,7 +183,7 @@ class PqResultHelper {
iterator end() { return iterator(*this, NumRows()); }
private:
- pg_result* result_ = nullptr;
+ PGresult* result_ = nullptr;
PGconn* conn_;
std::string query_;
std::vector param_values_;
@@ -727,7 +746,19 @@ class PqGetObjectsHelper {
} // namespace
-namespace adbcpq {
+AdbcStatusCode PostgresConnection::Cancel(struct AdbcError* error) {
+ // > errbuf must be a char array of size errbufsize (the recommended size is
+ // > 256 bytes).
+ // https://www.postgresql.org/docs/current/libpq-cancel.html
+ char errbuf[256];
+ // > The return value is 1 if the cancel request was successfully dispatched
+ // > and 0 if not.
+ if (PQcancel(cancel_, errbuf, sizeof(errbuf)) != 1) {
+ SetError(error, "[libpq] Failed to cancel operation: %s", errbuf);
+ return ADBC_STATUS_UNKNOWN;
+ }
+ return ADBC_STATUS_OK;
+}
AdbcStatusCode PostgresConnection::Commit(struct AdbcError* error) {
if (autocommit_) {
@@ -737,9 +768,10 @@ AdbcStatusCode PostgresConnection::Commit(struct AdbcError* error) {
PGresult* result = PQexec(conn_, "COMMIT");
if (PQresultStatus(result) != PGRES_COMMAND_OK) {
- SetError(error, "%s%s", "[libpq] Failed to commit: ", PQerrorMessage(conn_));
+ AdbcStatusCode code = SetError(error, result, "%s%s",
+ "[libpq] Failed to commit: ", PQerrorMessage(conn_));
PQclear(result);
- return ADBC_STATUS_IO;
+ return code;
}
PQclear(result);
return ADBC_STATUS_OK;
@@ -776,6 +808,10 @@ AdbcStatusCode PostgresConnectionGetInfoImpl(const uint32_t* info_codes,
RAISE_ADBC(AdbcConnectionGetInfoAppendString(array, info_codes[i],
NANOARROW_VERSION, error));
break;
+ case ADBC_INFO_DRIVER_ADBC_VERSION:
+ RAISE_ADBC(AdbcConnectionGetInfoAppendInt(array, info_codes[i],
+ ADBC_VERSION_1_1_0, error));
+ break;
default:
// Ignore
continue;
@@ -791,13 +827,12 @@ AdbcStatusCode PostgresConnectionGetInfoImpl(const uint32_t* info_codes,
}
AdbcStatusCode PostgresConnection::GetInfo(struct AdbcConnection* connection,
- uint32_t* info_codes, size_t info_codes_length,
+ const uint32_t* info_codes,
+ size_t info_codes_length,
struct ArrowArrayStream* out,
struct AdbcError* error) {
- // XXX: mistake in adbc.h (should have been const pointer)
- const uint32_t* codes = info_codes;
if (!info_codes) {
- codes = kSupportedInfoCodes;
+ info_codes = kSupportedInfoCodes;
info_codes_length = sizeof(kSupportedInfoCodes) / sizeof(kSupportedInfoCodes[0]);
}
@@ -806,8 +841,8 @@ AdbcStatusCode PostgresConnection::GetInfo(struct AdbcConnection* connection,
struct ArrowArray array;
std::memset(&array, 0, sizeof(array));
- AdbcStatusCode status =
- PostgresConnectionGetInfoImpl(codes, info_codes_length, &schema, &array, error);
+ AdbcStatusCode status = PostgresConnectionGetInfoImpl(info_codes, info_codes_length,
+ &schema, &array, error);
if (status != ADBC_STATUS_OK) {
if (schema.release) schema.release(&schema);
if (array.release) array.release(&array);
@@ -840,6 +875,399 @@ AdbcStatusCode PostgresConnection::GetObjects(
return BatchToArrayStream(&array, &schema, out, error);
}
+AdbcStatusCode PostgresConnection::GetOption(const char* option, char* value,
+ size_t* length, struct AdbcError* error) {
+ std::string output;
+ if (std::strcmp(option, ADBC_CONNECTION_OPTION_CURRENT_CATALOG) == 0) {
+ output = PQdb(conn_);
+ } else if (std::strcmp(option, ADBC_CONNECTION_OPTION_CURRENT_DB_SCHEMA) == 0) {
+ PqResultHelper result_helper{conn_, "SELECT CURRENT_SCHEMA", {}, error};
+ RAISE_ADBC(result_helper.Prepare());
+ RAISE_ADBC(result_helper.Execute());
+ auto it = result_helper.begin();
+ if (it == result_helper.end()) {
+ SetError(error, "[libpq] PostgreSQL returned no rows for 'SELECT CURRENT_SCHEMA'");
+ return ADBC_STATUS_INTERNAL;
+ }
+ output = (*it)[0].data;
+ } else if (std::strcmp(option, ADBC_CONNECTION_OPTION_AUTOCOMMIT) == 0) {
+ output = autocommit_ ? ADBC_OPTION_VALUE_ENABLED : ADBC_OPTION_VALUE_DISABLED;
+ } else {
+ return ADBC_STATUS_NOT_FOUND;
+ }
+
+ if (output.size() + 1 <= *length) {
+ std::memcpy(value, output.c_str(), output.size() + 1);
+ }
+ *length = output.size() + 1;
+ return ADBC_STATUS_OK;
+}
+AdbcStatusCode PostgresConnection::GetOptionBytes(const char* option, uint8_t* value,
+ size_t* length,
+ struct AdbcError* error) {
+ return ADBC_STATUS_NOT_FOUND;
+}
+AdbcStatusCode PostgresConnection::GetOptionInt(const char* option, int64_t* value,
+ struct AdbcError* error) {
+ return ADBC_STATUS_NOT_FOUND;
+}
+AdbcStatusCode PostgresConnection::GetOptionDouble(const char* option, double* value,
+ struct AdbcError* error) {
+ return ADBC_STATUS_NOT_FOUND;
+}
+
+AdbcStatusCode PostgresConnectionGetStatisticsImpl(PGconn* conn, const char* db_schema,
+ const char* table_name,
+ struct ArrowSchema* schema,
+ struct ArrowArray* array,
+ struct AdbcError* error) {
+ // Set up schema
+ auto uschema = nanoarrow::UniqueSchema();
+ {
+ ArrowSchemaInit(uschema.get());
+ CHECK_NA(INTERNAL, ArrowSchemaSetTypeStruct(uschema.get(), /*num_columns=*/2), error);
+ CHECK_NA(INTERNAL, ArrowSchemaSetType(uschema->children[0], NANOARROW_TYPE_STRING),
+ error);
+ CHECK_NA(INTERNAL, ArrowSchemaSetName(uschema->children[0], "catalog_name"), error);
+ CHECK_NA(INTERNAL, ArrowSchemaSetType(uschema->children[1], NANOARROW_TYPE_LIST),
+ error);
+ CHECK_NA(INTERNAL, ArrowSchemaSetName(uschema->children[1], "catalog_db_schemas"),
+ error);
+ CHECK_NA(INTERNAL, ArrowSchemaSetTypeStruct(uschema->children[1]->children[0], 2),
+ error);
+ uschema->children[1]->flags &= ~ARROW_FLAG_NULLABLE;
+
+ struct ArrowSchema* db_schema_schema = uschema->children[1]->children[0];
+ CHECK_NA(INTERNAL,
+ ArrowSchemaSetType(db_schema_schema->children[0], NANOARROW_TYPE_STRING),
+ error);
+ CHECK_NA(INTERNAL,
+ ArrowSchemaSetName(db_schema_schema->children[0], "db_schema_name"), error);
+ CHECK_NA(INTERNAL,
+ ArrowSchemaSetType(db_schema_schema->children[1], NANOARROW_TYPE_LIST),
+ error);
+ CHECK_NA(INTERNAL,
+ ArrowSchemaSetName(db_schema_schema->children[1], "db_schema_statistics"),
+ error);
+ CHECK_NA(INTERNAL,
+ ArrowSchemaSetTypeStruct(db_schema_schema->children[1]->children[0], 5),
+ error);
+ db_schema_schema->children[1]->flags &= ~ARROW_FLAG_NULLABLE;
+
+ struct ArrowSchema* statistics_schema = db_schema_schema->children[1]->children[0];
+ CHECK_NA(INTERNAL,
+ ArrowSchemaSetType(statistics_schema->children[0], NANOARROW_TYPE_STRING),
+ error);
+ CHECK_NA(INTERNAL, ArrowSchemaSetName(statistics_schema->children[0], "table_name"),
+ error);
+ statistics_schema->children[0]->flags &= ~ARROW_FLAG_NULLABLE;
+ CHECK_NA(INTERNAL,
+ ArrowSchemaSetType(statistics_schema->children[1], NANOARROW_TYPE_STRING),
+ error);
+ CHECK_NA(INTERNAL, ArrowSchemaSetName(statistics_schema->children[1], "column_name"),
+ error);
+ CHECK_NA(INTERNAL,
+ ArrowSchemaSetType(statistics_schema->children[2], NANOARROW_TYPE_INT16),
+ error);
+ CHECK_NA(INTERNAL,
+ ArrowSchemaSetName(statistics_schema->children[2], "statistic_key"), error);
+ statistics_schema->children[2]->flags &= ~ARROW_FLAG_NULLABLE;
+ CHECK_NA(INTERNAL,
+ ArrowSchemaSetTypeUnion(statistics_schema->children[3],
+ NANOARROW_TYPE_DENSE_UNION, 4),
+ error);
+ CHECK_NA(INTERNAL,
+ ArrowSchemaSetName(statistics_schema->children[3], "statistic_value"),
+ error);
+ statistics_schema->children[3]->flags &= ~ARROW_FLAG_NULLABLE;
+ CHECK_NA(INTERNAL,
+ ArrowSchemaSetType(statistics_schema->children[4], NANOARROW_TYPE_BOOL),
+ error);
+ CHECK_NA(
+ INTERNAL,
+ ArrowSchemaSetName(statistics_schema->children[4], "statistic_is_approximate"),
+ error);
+ statistics_schema->children[4]->flags &= ~ARROW_FLAG_NULLABLE;
+
+ struct ArrowSchema* value_schema = statistics_schema->children[3];
+ CHECK_NA(INTERNAL,
+ ArrowSchemaSetType(value_schema->children[0], NANOARROW_TYPE_INT64), error);
+ CHECK_NA(INTERNAL, ArrowSchemaSetName(value_schema->children[0], "int64"), error);
+ CHECK_NA(INTERNAL,
+ ArrowSchemaSetType(value_schema->children[1], NANOARROW_TYPE_UINT64), error);
+ CHECK_NA(INTERNAL, ArrowSchemaSetName(value_schema->children[1], "uint64"), error);
+ CHECK_NA(INTERNAL,
+ ArrowSchemaSetType(value_schema->children[2], NANOARROW_TYPE_DOUBLE), error);
+ CHECK_NA(INTERNAL, ArrowSchemaSetName(value_schema->children[2], "float64"), error);
+ CHECK_NA(INTERNAL,
+ ArrowSchemaSetType(value_schema->children[3], NANOARROW_TYPE_BINARY), error);
+ CHECK_NA(INTERNAL, ArrowSchemaSetName(value_schema->children[3], "binary"), error);
+ }
+
+ // Set up builders
+ struct ArrowError na_error = {0};
+ CHECK_NA_DETAIL(INTERNAL, ArrowArrayInitFromSchema(array, uschema.get(), &na_error),
+ &na_error, error);
+ CHECK_NA(INTERNAL, ArrowArrayStartAppending(array), error);
+
+ struct ArrowArray* catalog_name_col = array->children[0];
+ struct ArrowArray* catalog_db_schemas_col = array->children[1];
+ struct ArrowArray* catalog_db_schemas_items = catalog_db_schemas_col->children[0];
+ struct ArrowArray* db_schema_name_col = catalog_db_schemas_items->children[0];
+ struct ArrowArray* db_schema_statistics_col = catalog_db_schemas_items->children[1];
+ struct ArrowArray* db_schema_statistics_items = db_schema_statistics_col->children[0];
+ struct ArrowArray* statistics_table_name_col = db_schema_statistics_items->children[0];
+ struct ArrowArray* statistics_column_name_col = db_schema_statistics_items->children[1];
+ struct ArrowArray* statistics_key_col = db_schema_statistics_items->children[2];
+ struct ArrowArray* statistics_value_col = db_schema_statistics_items->children[3];
+ struct ArrowArray* statistics_is_approximate_col =
+ db_schema_statistics_items->children[4];
+ // struct ArrowArray* value_int64_col = statistics_value_col->children[0];
+ // struct ArrowArray* value_uint64_col = statistics_value_col->children[1];
+ struct ArrowArray* value_float64_col = statistics_value_col->children[2];
+ // struct ArrowArray* value_binary_col = statistics_value_col->children[3];
+
+ // Query (could probably be massively improved)
+ std::string query = R"(
+ WITH
+ class AS (
+ SELECT nspname, relname, reltuples
+ FROM pg_namespace
+ INNER JOIN pg_class ON pg_class.relnamespace = pg_namespace.oid
+ )
+ SELECT tablename, attname, null_frac, avg_width, n_distinct, reltuples
+ FROM pg_stats
+ INNER JOIN class ON pg_stats.schemaname = class.nspname AND pg_stats.tablename = class.relname
+ WHERE pg_stats.schemaname = $1 AND tablename LIKE $2
+ ORDER BY tablename
+)";
+
+ CHECK_NA(INTERNAL, ArrowArrayAppendString(catalog_name_col, ArrowCharView(PQdb(conn))),
+ error);
+ CHECK_NA(INTERNAL, ArrowArrayAppendString(db_schema_name_col, ArrowCharView(db_schema)),
+ error);
+
+ constexpr int8_t kStatsVariantFloat64 = 2;
+
+ std::string prev_table;
+
+ {
+ PqResultHelper result_helper{
+ conn, query, {db_schema, table_name ? table_name : "%"}, error};
+ RAISE_ADBC(result_helper.Prepare());
+ RAISE_ADBC(result_helper.Execute());
+
+ for (PqResultRow row : result_helper) {
+ auto reltuples = row[5].ParseDouble();
+ if (!reltuples.first) {
+ SetError(error, "[libpq] Invalid double value in reltuples: '%s'", row[5].data);
+ return ADBC_STATUS_INTERNAL;
+ }
+
+ if (std::strcmp(prev_table.c_str(), row[0].data) != 0) {
+ CHECK_NA(INTERNAL,
+ ArrowArrayAppendString(statistics_table_name_col,
+ ArrowStringView{row[0].data, row[0].len}),
+ error);
+ CHECK_NA(INTERNAL, ArrowArrayAppendNull(statistics_column_name_col, 1), error);
+ CHECK_NA(INTERNAL,
+ ArrowArrayAppendInt(statistics_key_col, ADBC_STATISTIC_ROW_COUNT_KEY),
+ error);
+ CHECK_NA(INTERNAL, ArrowArrayAppendDouble(value_float64_col, reltuples.second),
+ error);
+ CHECK_NA(INTERNAL,
+ ArrowArrayFinishUnionElement(statistics_value_col, kStatsVariantFloat64),
+ error);
+ CHECK_NA(INTERNAL, ArrowArrayAppendInt(statistics_is_approximate_col, 1), error);
+ CHECK_NA(INTERNAL, ArrowArrayFinishElement(db_schema_statistics_items), error);
+ prev_table = std::string(row[0].data, row[0].len);
+ }
+
+ auto null_frac = row[2].ParseDouble();
+ if (!null_frac.first) {
+ SetError(error, "[libpq] Invalid double value in null_frac: '%s'", row[2].data);
+ return ADBC_STATUS_INTERNAL;
+ }
+
+ CHECK_NA(INTERNAL,
+ ArrowArrayAppendString(statistics_table_name_col,
+ ArrowStringView{row[0].data, row[0].len}),
+ error);
+ CHECK_NA(INTERNAL,
+ ArrowArrayAppendString(statistics_column_name_col,
+ ArrowStringView{row[1].data, row[1].len}),
+ error);
+ CHECK_NA(INTERNAL,
+ ArrowArrayAppendInt(statistics_key_col, ADBC_STATISTIC_NULL_COUNT_KEY),
+ error);
+ CHECK_NA(
+ INTERNAL,
+ ArrowArrayAppendDouble(value_float64_col, null_frac.second * reltuples.second),
+ error);
+ CHECK_NA(INTERNAL,
+ ArrowArrayFinishUnionElement(statistics_value_col, kStatsVariantFloat64),
+ error);
+ CHECK_NA(INTERNAL, ArrowArrayAppendInt(statistics_is_approximate_col, 1), error);
+ CHECK_NA(INTERNAL, ArrowArrayFinishElement(db_schema_statistics_items), error);
+
+ auto average_byte_width = row[3].ParseDouble();
+ if (!average_byte_width.first) {
+ SetError(error, "[libpq] Invalid double value in avg_width: '%s'", row[3].data);
+ return ADBC_STATUS_INTERNAL;
+ }
+
+ CHECK_NA(INTERNAL,
+ ArrowArrayAppendString(statistics_table_name_col,
+ ArrowStringView{row[0].data, row[0].len}),
+ error);
+ CHECK_NA(INTERNAL,
+ ArrowArrayAppendString(statistics_column_name_col,
+ ArrowStringView{row[1].data, row[1].len}),
+ error);
+ CHECK_NA(
+ INTERNAL,
+ ArrowArrayAppendInt(statistics_key_col, ADBC_STATISTIC_AVERAGE_BYTE_WIDTH_KEY),
+ error);
+ CHECK_NA(INTERNAL,
+ ArrowArrayAppendDouble(value_float64_col, average_byte_width.second),
+ error);
+ CHECK_NA(INTERNAL,
+ ArrowArrayFinishUnionElement(statistics_value_col, kStatsVariantFloat64),
+ error);
+ CHECK_NA(INTERNAL, ArrowArrayAppendInt(statistics_is_approximate_col, 1), error);
+ CHECK_NA(INTERNAL, ArrowArrayFinishElement(db_schema_statistics_items), error);
+
+ auto n_distinct = row[4].ParseDouble();
+ if (!n_distinct.first) {
+ SetError(error, "[libpq] Invalid double value in avg_width: '%s'", row[4].data);
+ return ADBC_STATUS_INTERNAL;
+ }
+
+ CHECK_NA(INTERNAL,
+ ArrowArrayAppendString(statistics_table_name_col,
+ ArrowStringView{row[0].data, row[0].len}),
+ error);
+ CHECK_NA(INTERNAL,
+ ArrowArrayAppendString(statistics_column_name_col,
+ ArrowStringView{row[1].data, row[1].len}),
+ error);
+ CHECK_NA(INTERNAL,
+ ArrowArrayAppendInt(statistics_key_col, ADBC_STATISTIC_DISTINCT_COUNT_KEY),
+ error);
+ // > If greater than zero, the estimated number of distinct values in
+ // > the column. If less than zero, the negative of the number of
+ // > distinct values divided by the number of rows.
+ // https://www.postgresql.org/docs/current/view-pg-stats.html
+ CHECK_NA(
+ INTERNAL,
+ ArrowArrayAppendDouble(value_float64_col,
+ n_distinct.second > 0
+ ? n_distinct.second
+ : (std::fabs(n_distinct.second) * reltuples.second)),
+ error);
+ CHECK_NA(INTERNAL,
+ ArrowArrayFinishUnionElement(statistics_value_col, kStatsVariantFloat64),
+ error);
+ CHECK_NA(INTERNAL, ArrowArrayAppendInt(statistics_is_approximate_col, 1), error);
+ CHECK_NA(INTERNAL, ArrowArrayFinishElement(db_schema_statistics_items), error);
+ }
+ }
+
+ CHECK_NA(INTERNAL, ArrowArrayFinishElement(db_schema_statistics_col), error);
+ CHECK_NA(INTERNAL, ArrowArrayFinishElement(catalog_db_schemas_items), error);
+ CHECK_NA(INTERNAL, ArrowArrayFinishElement(catalog_db_schemas_col), error);
+ CHECK_NA(INTERNAL, ArrowArrayFinishElement(array), error);
+
+ CHECK_NA_DETAIL(INTERNAL, ArrowArrayFinishBuildingDefault(array, &na_error), &na_error,
+ error);
+ uschema.move(schema);
+ return ADBC_STATUS_OK;
+}
+
+AdbcStatusCode PostgresConnection::GetStatistics(const char* catalog,
+ const char* db_schema,
+ const char* table_name, bool approximate,
+ struct ArrowArrayStream* out,
+ struct AdbcError* error) {
+ // Simplify our jobs here
+ if (!approximate) {
+ SetError(error, "[libpq] Exact statistics are not implemented");
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+ } else if (!db_schema) {
+ SetError(error, "[libpq] Must request statistics for a single schema");
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+ } else if (catalog && std::strcmp(catalog, PQdb(conn_)) != 0) {
+ SetError(error, "[libpq] Can only request statistics for current catalog");
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+ }
+
+ struct ArrowSchema schema;
+ std::memset(&schema, 0, sizeof(schema));
+ struct ArrowArray array;
+ std::memset(&array, 0, sizeof(array));
+
+ AdbcStatusCode status = PostgresConnectionGetStatisticsImpl(
+ conn_, db_schema, table_name, &schema, &array, error);
+ if (status != ADBC_STATUS_OK) {
+ if (schema.release) schema.release(&schema);
+ if (array.release) array.release(&array);
+ return status;
+ }
+
+ return BatchToArrayStream(&array, &schema, out, error);
+}
+
+AdbcStatusCode PostgresConnectionGetStatisticNamesImpl(struct ArrowSchema* schema,
+ struct ArrowArray* array,
+ struct AdbcError* error) {
+ auto uschema = nanoarrow::UniqueSchema();
+ ArrowSchemaInit(uschema.get());
+
+ CHECK_NA(INTERNAL, ArrowSchemaSetType(uschema.get(), NANOARROW_TYPE_STRUCT), error);
+ CHECK_NA(INTERNAL, ArrowSchemaAllocateChildren(uschema.get(), /*num_columns=*/2),
+ error);
+
+ ArrowSchemaInit(uschema.get()->children[0]);
+ CHECK_NA(INTERNAL,
+ ArrowSchemaSetType(uschema.get()->children[0], NANOARROW_TYPE_STRING), error);
+ CHECK_NA(INTERNAL, ArrowSchemaSetName(uschema.get()->children[0], "statistic_name"),
+ error);
+ uschema.get()->children[0]->flags &= ~ARROW_FLAG_NULLABLE;
+
+ ArrowSchemaInit(uschema.get()->children[1]);
+ CHECK_NA(INTERNAL, ArrowSchemaSetType(uschema.get()->children[1], NANOARROW_TYPE_INT16),
+ error);
+ CHECK_NA(INTERNAL, ArrowSchemaSetName(uschema.get()->children[1], "statistic_key"),
+ error);
+ uschema.get()->children[1]->flags &= ~ARROW_FLAG_NULLABLE;
+
+ CHECK_NA(INTERNAL, ArrowArrayInitFromSchema(array, uschema.get(), NULL), error);
+ CHECK_NA(INTERNAL, ArrowArrayStartAppending(array), error);
+ CHECK_NA(INTERNAL, ArrowArrayFinishBuildingDefault(array, NULL), error);
+
+ uschema.move(schema);
+ return ADBC_STATUS_OK;
+}
+
+AdbcStatusCode PostgresConnection::GetStatisticNames(struct ArrowArrayStream* out,
+ struct AdbcError* error) {
+ // We don't support any extended statistics, just return an empty stream
+ struct ArrowSchema schema;
+ std::memset(&schema, 0, sizeof(schema));
+ struct ArrowArray array;
+ std::memset(&array, 0, sizeof(array));
+
+ AdbcStatusCode status = PostgresConnectionGetStatisticNamesImpl(&schema, &array, error);
+ if (status != ADBC_STATUS_OK) {
+ if (schema.release) schema.release(&schema);
+ if (array.release) array.release(&array);
+ return status;
+ }
+ return BatchToArrayStream(&array, &schema, out, error);
+
+ return ADBC_STATUS_OK;
+}
+
AdbcStatusCode PostgresConnection::GetTableSchema(const char* catalog,
const char* db_schema,
const char* table_name,
@@ -964,16 +1392,26 @@ AdbcStatusCode PostgresConnection::GetTableTypes(struct AdbcConnection* connecti
AdbcStatusCode PostgresConnection::Init(struct AdbcDatabase* database,
struct AdbcError* error) {
if (!database || !database->private_data) {
- SetError(error, "%s", "[libpq] Must provide an initialized AdbcDatabase");
+ SetError(error, "[libpq] Must provide an initialized AdbcDatabase");
return ADBC_STATUS_INVALID_ARGUMENT;
}
database_ =
*reinterpret_cast*>(database->private_data);
type_resolver_ = database_->type_resolver();
- return database_->Connect(&conn_, error);
+ RAISE_ADBC(database_->Connect(&conn_, error));
+ cancel_ = PQgetCancel(conn_);
+ if (!cancel_) {
+ SetError(error, "[libpq] Could not initialize PGcancel");
+ return ADBC_STATUS_UNKNOWN;
+ }
+ return ADBC_STATUS_OK;
}
AdbcStatusCode PostgresConnection::Release(struct AdbcError* error) {
+ if (cancel_) {
+ PQfreeCancel(cancel_);
+ cancel_ = nullptr;
+ }
if (conn_) {
return database_->Disconnect(&conn_, error);
}
@@ -1023,8 +1461,35 @@ AdbcStatusCode PostgresConnection::SetOption(const char* key, const char* value,
autocommit_ = autocommit;
}
return ADBC_STATUS_OK;
+ } else if (std::strcmp(key, ADBC_CONNECTION_OPTION_CURRENT_DB_SCHEMA) == 0) {
+ // PostgreSQL doesn't accept a parameter here
+ PqResultHelper result_helper{
+ conn_, std::string("SET search_path TO ") + value, {}, error};
+ RAISE_ADBC(result_helper.Prepare());
+ RAISE_ADBC(result_helper.Execute());
+ return ADBC_STATUS_OK;
}
SetError(error, "%s%s", "[libpq] Unknown option ", key);
return ADBC_STATUS_NOT_IMPLEMENTED;
}
+
+AdbcStatusCode PostgresConnection::SetOptionBytes(const char* key, const uint8_t* value,
+ size_t length,
+ struct AdbcError* error) {
+ SetError(error, "%s%s", "[libpq] Unknown option ", key);
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode PostgresConnection::SetOptionDouble(const char* key, double value,
+ struct AdbcError* error) {
+ SetError(error, "%s%s", "[libpq] Unknown option ", key);
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode PostgresConnection::SetOptionInt(const char* key, int64_t value,
+ struct AdbcError* error) {
+ SetError(error, "%s%s", "[libpq] Unknown option ", key);
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
} // namespace adbcpq
diff --git a/c/driver/postgresql/connection.h b/c/driver/postgresql/connection.h
index 74315ee053..50d3ebe322 100644
--- a/c/driver/postgresql/connection.h
+++ b/c/driver/postgresql/connection.h
@@ -29,10 +29,12 @@ namespace adbcpq {
class PostgresDatabase;
class PostgresConnection {
public:
- PostgresConnection() : database_(nullptr), conn_(nullptr), autocommit_(true) {}
+ PostgresConnection()
+ : database_(nullptr), conn_(nullptr), cancel_(nullptr), autocommit_(true) {}
+ AdbcStatusCode Cancel(struct AdbcError* error);
AdbcStatusCode Commit(struct AdbcError* error);
- AdbcStatusCode GetInfo(struct AdbcConnection* connection, uint32_t* info_codes,
+ AdbcStatusCode GetInfo(struct AdbcConnection* connection, const uint32_t* info_codes,
size_t info_codes_length, struct ArrowArrayStream* out,
struct AdbcError* error);
AdbcStatusCode GetObjects(struct AdbcConnection* connection, int depth,
@@ -40,6 +42,18 @@ class PostgresConnection {
const char* table_name, const char** table_types,
const char* column_name, struct ArrowArrayStream* out,
struct AdbcError* error);
+ AdbcStatusCode GetOption(const char* option, char* value, size_t* length,
+ struct AdbcError* error);
+ AdbcStatusCode GetOptionBytes(const char* option, uint8_t* value, size_t* length,
+ struct AdbcError* error);
+ AdbcStatusCode GetOptionDouble(const char* option, double* value,
+ struct AdbcError* error);
+ AdbcStatusCode GetOptionInt(const char* option, int64_t* value,
+ struct AdbcError* error);
+ AdbcStatusCode GetStatistics(const char* catalog, const char* db_schema,
+ const char* table_name, bool approximate,
+ struct ArrowArrayStream* out, struct AdbcError* error);
+ AdbcStatusCode GetStatisticNames(struct ArrowArrayStream* out, struct AdbcError* error);
AdbcStatusCode GetTableSchema(const char* catalog, const char* db_schema,
const char* table_name, struct ArrowSchema* schema,
struct AdbcError* error);
@@ -49,6 +63,10 @@ class PostgresConnection {
AdbcStatusCode Release(struct AdbcError* error);
AdbcStatusCode Rollback(struct AdbcError* error);
AdbcStatusCode SetOption(const char* key, const char* value, struct AdbcError* error);
+ AdbcStatusCode SetOptionBytes(const char* key, const uint8_t* value, size_t length,
+ struct AdbcError* error);
+ AdbcStatusCode SetOptionDouble(const char* key, double value, struct AdbcError* error);
+ AdbcStatusCode SetOptionInt(const char* key, int64_t value, struct AdbcError* error);
PGconn* conn() const { return conn_; }
const std::shared_ptr& type_resolver() const {
@@ -60,6 +78,7 @@ class PostgresConnection {
std::shared_ptr database_;
std::shared_ptr type_resolver_;
PGconn* conn_;
+ PGcancel* cancel_;
bool autocommit_;
};
} // namespace adbcpq
diff --git a/c/driver/postgresql/database.cc b/c/driver/postgresql/database.cc
index 3976c4b08d..5de8628095 100644
--- a/c/driver/postgresql/database.cc
+++ b/c/driver/postgresql/database.cc
@@ -36,6 +36,23 @@ PostgresDatabase::PostgresDatabase() : open_connections_(0) {
}
PostgresDatabase::~PostgresDatabase() = default;
+AdbcStatusCode PostgresDatabase::GetOption(const char* option, char* value,
+ size_t* length, struct AdbcError* error) {
+ return ADBC_STATUS_NOT_FOUND;
+}
+AdbcStatusCode PostgresDatabase::GetOptionBytes(const char* option, uint8_t* value,
+ size_t* length, struct AdbcError* error) {
+ return ADBC_STATUS_NOT_FOUND;
+}
+AdbcStatusCode PostgresDatabase::GetOptionInt(const char* option, int64_t* value,
+ struct AdbcError* error) {
+ return ADBC_STATUS_NOT_FOUND;
+}
+AdbcStatusCode PostgresDatabase::GetOptionDouble(const char* option, double* value,
+ struct AdbcError* error) {
+ return ADBC_STATUS_NOT_FOUND;
+}
+
AdbcStatusCode PostgresDatabase::Init(struct AdbcError* error) {
// Connect to validate the parameters.
return RebuildTypeResolver(error);
@@ -61,6 +78,24 @@ AdbcStatusCode PostgresDatabase::SetOption(const char* key, const char* value,
return ADBC_STATUS_OK;
}
+AdbcStatusCode PostgresDatabase::SetOptionBytes(const char* key, const uint8_t* value,
+ size_t length, struct AdbcError* error) {
+ SetError(error, "%s%s", "[libpq] Unknown option ", key);
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode PostgresDatabase::SetOptionDouble(const char* key, double value,
+ struct AdbcError* error) {
+ SetError(error, "%s%s", "[libpq] Unknown option ", key);
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode PostgresDatabase::SetOptionInt(const char* key, int64_t value,
+ struct AdbcError* error) {
+ SetError(error, "%s%s", "[libpq] Unknown option ", key);
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
AdbcStatusCode PostgresDatabase::Connect(PGconn** conn, struct AdbcError* error) {
if (uri_.empty()) {
SetError(error, "%s",
@@ -90,10 +125,10 @@ AdbcStatusCode PostgresDatabase::Disconnect(PGconn** conn, struct AdbcError* err
// Helpers for building the type resolver from queries
static inline int32_t InsertPgAttributeResult(
- pg_result* result, const std::shared_ptr& resolver);
+ PGresult* result, const std::shared_ptr& resolver);
static inline int32_t InsertPgTypeResult(
- pg_result* result, const std::shared_ptr& resolver);
+ PGresult* result, const std::shared_ptr& resolver);
AdbcStatusCode PostgresDatabase::RebuildTypeResolver(struct AdbcError* error) {
PGconn* conn = nullptr;
@@ -142,7 +177,7 @@ ORDER BY
auto resolver = std::make_shared();
// Insert record type definitions (this includes table schemas)
- pg_result* result = PQexec(conn, kColumnsQuery.c_str());
+ PGresult* result = PQexec(conn, kColumnsQuery.c_str());
ExecStatusType pq_status = PQresultStatus(result);
if (pq_status == PGRES_TUPLES_OK) {
InsertPgAttributeResult(result, resolver);
@@ -187,7 +222,7 @@ ORDER BY
}
static inline int32_t InsertPgAttributeResult(
- pg_result* result, const std::shared_ptr& resolver) {
+ PGresult* result, const std::shared_ptr& resolver) {
int num_rows = PQntuples(result);
std::vector> columns;
uint32_t current_type_oid = 0;
@@ -219,7 +254,7 @@ static inline int32_t InsertPgAttributeResult(
}
static inline int32_t InsertPgTypeResult(
- pg_result* result, const std::shared_ptr& resolver) {
+ PGresult* result, const std::shared_ptr& resolver) {
int num_rows = PQntuples(result);
PostgresTypeResolver::Item item;
int32_t n_added = 0;
diff --git a/c/driver/postgresql/database.h b/c/driver/postgresql/database.h
index f10464787a..6c3da58daa 100644
--- a/c/driver/postgresql/database.h
+++ b/c/driver/postgresql/database.h
@@ -36,7 +36,19 @@ class PostgresDatabase {
AdbcStatusCode Init(struct AdbcError* error);
AdbcStatusCode Release(struct AdbcError* error);
+ AdbcStatusCode GetOption(const char* option, char* value, size_t* length,
+ struct AdbcError* error);
+ AdbcStatusCode GetOptionBytes(const char* option, uint8_t* value, size_t* length,
+ struct AdbcError* error);
+ AdbcStatusCode GetOptionDouble(const char* option, double* value,
+ struct AdbcError* error);
+ AdbcStatusCode GetOptionInt(const char* option, int64_t* value,
+ struct AdbcError* error);
AdbcStatusCode SetOption(const char* key, const char* value, struct AdbcError* error);
+ AdbcStatusCode SetOptionBytes(const char* key, const uint8_t* value, size_t length,
+ struct AdbcError* error);
+ AdbcStatusCode SetOptionDouble(const char* key, double value, struct AdbcError* error);
+ AdbcStatusCode SetOptionInt(const char* key, int64_t value, struct AdbcError* error);
// Internal implementation
@@ -54,3 +66,10 @@ class PostgresDatabase {
std::shared_ptr type_resolver_;
};
} // namespace adbcpq
+
+extern "C" {
+/// For applications that want to use the driver struct directly, this gives
+/// them access to the Init routine.
+ADBC_EXPORT
+AdbcStatusCode PostgresqlDriverInit(int, void*, struct AdbcError*);
+}
diff --git a/c/driver/postgresql/error.cc b/c/driver/postgresql/error.cc
new file mode 100644
index 0000000000..47e04496ba
--- /dev/null
+++ b/c/driver/postgresql/error.cc
@@ -0,0 +1,97 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include "error.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "common/utils.h"
+
+namespace adbcpq {
+
+namespace {
+struct DetailField {
+ int code;
+ std::string key;
+};
+
+static const std::vector kDetailFields = {
+ {PG_DIAG_COLUMN_NAME, "PG_DIAG_COLUMN_NAME"},
+ {PG_DIAG_CONTEXT, "PG_DIAG_CONTEXT"},
+ {PG_DIAG_CONSTRAINT_NAME, "PG_DIAG_CONSTRAINT_NAME"},
+ {PG_DIAG_DATATYPE_NAME, "PG_DIAG_DATATYPE_NAME"},
+ {PG_DIAG_INTERNAL_POSITION, "PG_DIAG_INTERNAL_POSITION"},
+ {PG_DIAG_INTERNAL_QUERY, "PG_DIAG_INTERNAL_QUERY"},
+ {PG_DIAG_MESSAGE_PRIMARY, "PG_DIAG_MESSAGE_PRIMARY"},
+ {PG_DIAG_MESSAGE_DETAIL, "PG_DIAG_MESSAGE_DETAIL"},
+ {PG_DIAG_MESSAGE_HINT, "PG_DIAG_MESSAGE_HINT"},
+ {PG_DIAG_SEVERITY_NONLOCALIZED, "PG_DIAG_SEVERITY_NONLOCALIZED"},
+ {PG_DIAG_SQLSTATE, "PG_DIAG_SQLSTATE"},
+ {PG_DIAG_STATEMENT_POSITION, "PG_DIAG_STATEMENT_POSITION"},
+ {PG_DIAG_SCHEMA_NAME, "PG_DIAG_SCHEMA_NAME"},
+ {PG_DIAG_TABLE_NAME, "PG_DIAG_TABLE_NAME"},
+};
+} // namespace
+
+AdbcStatusCode SetError(struct AdbcError* error, PGresult* result, const char* format,
+ ...) {
+ va_list args;
+ va_start(args, format);
+ SetErrorVariadic(error, format, args);
+ va_end(args);
+
+ AdbcStatusCode code = ADBC_STATUS_IO;
+
+ const char* sqlstate = PQresultErrorField(result, PG_DIAG_SQLSTATE);
+ if (sqlstate) {
+ // https://www.postgresql.org/docs/current/errcodes-appendix.html
+ // This can be extended in the future
+ if (std::strcmp(sqlstate, "57014") == 0) {
+ code = ADBC_STATUS_CANCELLED;
+ } else if (std::strncmp(sqlstate, "42", 0) == 0) {
+ // Class 42 — Syntax Error or Access Rule Violation
+ code = ADBC_STATUS_INVALID_ARGUMENT;
+ }
+
+ static_assert(sizeof(error->sqlstate) == 5, "");
+ // N.B. strncpy generates warnings when used for this purpose
+ int i = 0;
+ for (; sqlstate[i] != '\0' && i < 5; i++) {
+ error->sqlstate[i] = sqlstate[i];
+ }
+ for (; i < 5; i++) {
+ error->sqlstate[i] = '\0';
+ }
+ }
+
+ for (const auto& field : kDetailFields) {
+ const char* value = PQresultErrorField(result, field.code);
+ if (value) {
+ AppendErrorDetail(error, field.key.c_str(), reinterpret_cast(value),
+ std::strlen(value));
+ }
+ }
+ return code;
+}
+
+} // namespace adbcpq
diff --git a/c/driver/postgresql/error.h b/c/driver/postgresql/error.h
new file mode 100644
index 0000000000..75c52b46c3
--- /dev/null
+++ b/c/driver/postgresql/error.h
@@ -0,0 +1,42 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// Error handling utilities.
+
+#pragma once
+
+#include
+#include
+
+namespace adbcpq {
+
+// The printf checking attribute doesn't work properly on gcc 4.8
+// and results in spurious compiler warnings
+#if defined(__clang__) || (defined(__GNUC__) && __GNUC__ >= 5)
+#define ADBC_CHECK_PRINTF_ATTRIBUTE(x, y) __attribute__((format(printf, x, y)))
+#else
+#define ADBC_CHECK_PRINTF_ATTRIBUTE(x, y)
+#endif
+
+/// \brief Set an error based on a PGresult, inferring the proper ADBC status
+/// code from the PGresult.
+AdbcStatusCode SetError(struct AdbcError* error, PGresult* result, const char* format,
+ ...) ADBC_CHECK_PRINTF_ATTRIBUTE(3, 4);
+
+#undef ADBC_CHECK_PRINTF_ATTRIBUTE
+
+} // namespace adbcpq
diff --git a/c/driver/postgresql/postgres_copy_reader.h b/c/driver/postgresql/postgres_copy_reader.h
index 4aa5a82e69..5c7214dc04 100644
--- a/c/driver/postgresql/postgres_copy_reader.h
+++ b/c/driver/postgresql/postgres_copy_reader.h
@@ -893,12 +893,13 @@ static inline ArrowErrorCode MakeCopyFieldReader(const PostgresType& pg_type,
class PostgresCopyStreamReader {
public:
- ArrowErrorCode Init(const PostgresType& pg_type) {
+ ArrowErrorCode Init(PostgresType pg_type) {
if (pg_type.type_id() != PostgresTypeId::kRecord) {
return EINVAL;
}
- root_reader_.Init(pg_type);
+ pg_type_ = std::move(pg_type);
+ root_reader_.Init(pg_type_);
array_size_approx_bytes_ = 0;
return NANOARROW_OK;
}
@@ -1022,7 +1023,10 @@ class PostgresCopyStreamReader {
return NANOARROW_OK;
}
+ const PostgresType& pg_type() const { return pg_type_; }
+
private:
+ PostgresType pg_type_;
PostgresCopyFieldTupleReader root_reader_;
nanoarrow::UniqueSchema schema_;
nanoarrow::UniqueArray array_;
diff --git a/c/driver/postgresql/postgresql.cc b/c/driver/postgresql/postgresql.cc
index 29fd04cddc..2e25c4bedf 100644
--- a/c/driver/postgresql/postgresql.cc
+++ b/c/driver/postgresql/postgresql.cc
@@ -34,7 +34,7 @@ using adbcpq::PostgresStatement;
// ---------------------------------------------------------------------
// ADBC interface implementation - as private functions so that these
// don't get replaced by the dynamic linker. If we implemented these
-// under the Adbc* names, then DriverInit, the linker may resolve
+// under the Adbc* names, then in DriverInit, the linker may resolve
// functions to the address of the functions provided by the driver
// manager instead of our functions.
//
@@ -47,6 +47,30 @@ using adbcpq::PostgresStatement;
//
// So in the end some manual effort here was chosen.
+// ---------------------------------------------------------------------
+// AdbcError
+
+namespace {
+const struct AdbcError* PostgresErrorFromArrayStream(struct ArrowArrayStream* stream,
+ AdbcStatusCode* status) {
+ // Currently only valid for TupleReader
+ return adbcpq::TupleReader::ErrorFromArrayStream(stream, status);
+}
+} // namespace
+
+int AdbcErrorGetDetailCount(const struct AdbcError* error) {
+ return CommonErrorGetDetailCount(error);
+}
+
+struct AdbcErrorDetail AdbcErrorGetDetail(const struct AdbcError* error, int index) {
+ return CommonErrorGetDetail(error, index);
+}
+
+const struct AdbcError* AdbcErrorFromArrayStream(struct ArrowArrayStream* stream,
+ AdbcStatusCode* status) {
+ return PostgresErrorFromArrayStream(stream, status);
+}
+
// ---------------------------------------------------------------------
// AdbcDatabase
@@ -83,14 +107,92 @@ AdbcStatusCode PostgresDatabaseRelease(struct AdbcDatabase* database,
return status;
}
+AdbcStatusCode PostgresDatabaseGetOption(struct AdbcDatabase* database, const char* key,
+ char* value, size_t* length,
+ struct AdbcError* error) {
+ if (!database->private_data) return ADBC_STATUS_INVALID_STATE;
+ auto ptr = reinterpret_cast*>(database->private_data);
+ return (*ptr)->GetOption(key, value, length, error);
+}
+
+AdbcStatusCode PostgresDatabaseGetOptionBytes(struct AdbcDatabase* database,
+ const char* key, uint8_t* value,
+ size_t* length, struct AdbcError* error) {
+ if (!database->private_data) return ADBC_STATUS_INVALID_STATE;
+ auto ptr = reinterpret_cast*>(database->private_data);
+ return (*ptr)->GetOptionBytes(key, value, length, error);
+}
+
+AdbcStatusCode PostgresDatabaseGetOptionDouble(struct AdbcDatabase* database,
+ const char* key, double* value,
+ struct AdbcError* error) {
+ if (!database->private_data) return ADBC_STATUS_INVALID_STATE;
+ auto ptr = reinterpret_cast*>(database->private_data);
+ return (*ptr)->GetOptionDouble(key, value, error);
+}
+
+AdbcStatusCode PostgresDatabaseGetOptionInt(struct AdbcDatabase* database,
+ const char* key, int64_t* value,
+ struct AdbcError* error) {
+ if (!database->private_data) return ADBC_STATUS_INVALID_STATE;
+ auto ptr = reinterpret_cast*>(database->private_data);
+ return (*ptr)->GetOptionInt(key, value, error);
+}
+
AdbcStatusCode PostgresDatabaseSetOption(struct AdbcDatabase* database, const char* key,
const char* value, struct AdbcError* error) {
if (!database || !database->private_data) return ADBC_STATUS_INVALID_STATE;
auto ptr = reinterpret_cast*>(database->private_data);
return (*ptr)->SetOption(key, value, error);
}
+
+AdbcStatusCode PostgresDatabaseSetOptionBytes(struct AdbcDatabase* database,
+ const char* key, const uint8_t* value,
+ size_t length, struct AdbcError* error) {
+ if (!database->private_data) return ADBC_STATUS_INVALID_STATE;
+ auto ptr = reinterpret_cast*>(database->private_data);
+ return (*ptr)->SetOptionBytes(key, value, length, error);
+}
+
+AdbcStatusCode PostgresDatabaseSetOptionDouble(struct AdbcDatabase* database,
+ const char* key, double value,
+ struct AdbcError* error) {
+ if (!database->private_data) return ADBC_STATUS_INVALID_STATE;
+ auto ptr = reinterpret_cast*>(database->private_data);
+ return (*ptr)->SetOptionDouble(key, value, error);
+}
+
+AdbcStatusCode PostgresDatabaseSetOptionInt(struct AdbcDatabase* database,
+ const char* key, int64_t value,
+ struct AdbcError* error) {
+ if (!database->private_data) return ADBC_STATUS_INVALID_STATE;
+ auto ptr = reinterpret_cast*>(database->private_data);
+ return (*ptr)->SetOptionInt(key, value, error);
+}
} // namespace
+AdbcStatusCode AdbcDatabaseGetOption(struct AdbcDatabase* database, const char* key,
+ char* value, size_t* length,
+ struct AdbcError* error) {
+ return PostgresDatabaseGetOption(database, key, value, length, error);
+}
+
+AdbcStatusCode AdbcDatabaseGetOptionBytes(struct AdbcDatabase* database, const char* key,
+ uint8_t* value, size_t* length,
+ struct AdbcError* error) {
+ return PostgresDatabaseGetOptionBytes(database, key, value, length, error);
+}
+
+AdbcStatusCode AdbcDatabaseGetOptionInt(struct AdbcDatabase* database, const char* key,
+ int64_t* value, struct AdbcError* error) {
+ return PostgresDatabaseGetOptionInt(database, key, value, error);
+}
+
+AdbcStatusCode AdbcDatabaseGetOptionDouble(struct AdbcDatabase* database, const char* key,
+ double* value, struct AdbcError* error) {
+ return PostgresDatabaseGetOptionDouble(database, key, value, error);
+}
+
AdbcStatusCode AdbcDatabaseInit(struct AdbcDatabase* database, struct AdbcError* error) {
return PostgresDatabaseInit(database, error);
}
@@ -109,10 +211,34 @@ AdbcStatusCode AdbcDatabaseSetOption(struct AdbcDatabase* database, const char*
return PostgresDatabaseSetOption(database, key, value, error);
}
+AdbcStatusCode AdbcDatabaseSetOptionBytes(struct AdbcDatabase* database, const char* key,
+ const uint8_t* value, size_t length,
+ struct AdbcError* error) {
+ return PostgresDatabaseSetOptionBytes(database, key, value, length, error);
+}
+
+AdbcStatusCode AdbcDatabaseSetOptionInt(struct AdbcDatabase* database, const char* key,
+ int64_t value, struct AdbcError* error) {
+ return PostgresDatabaseSetOptionInt(database, key, value, error);
+}
+
+AdbcStatusCode AdbcDatabaseSetOptionDouble(struct AdbcDatabase* database, const char* key,
+ double value, struct AdbcError* error) {
+ return PostgresDatabaseSetOptionDouble(database, key, value, error);
+}
+
// ---------------------------------------------------------------------
// AdbcConnection
namespace {
+AdbcStatusCode PostgresConnectionCancel(struct AdbcConnection* connection,
+ struct AdbcError* error) {
+ if (!connection->private_data) return ADBC_STATUS_INVALID_STATE;
+ auto ptr =
+ reinterpret_cast*>(connection->private_data);
+ return (*ptr)->Cancel(error);
+}
+
AdbcStatusCode PostgresConnectionCommit(struct AdbcConnection* connection,
struct AdbcError* error) {
if (!connection->private_data) return ADBC_STATUS_INVALID_STATE;
@@ -122,7 +248,8 @@ AdbcStatusCode PostgresConnectionCommit(struct AdbcConnection* connection,
}
AdbcStatusCode PostgresConnectionGetInfo(struct AdbcConnection* connection,
- uint32_t* info_codes, size_t info_codes_length,
+ const uint32_t* info_codes,
+ size_t info_codes_length,
struct ArrowArrayStream* stream,
struct AdbcError* error) {
if (!connection->private_data) return ADBC_STATUS_INVALID_STATE;
@@ -142,6 +269,63 @@ AdbcStatusCode PostgresConnectionGetObjects(
table_types, column_name, stream, error);
}
+AdbcStatusCode PostgresConnectionGetOption(struct AdbcConnection* connection,
+ const char* key, char* value, size_t* length,
+ struct AdbcError* error) {
+ if (!connection->private_data) return ADBC_STATUS_INVALID_STATE;
+ auto ptr =
+ reinterpret_cast*>(connection->private_data);
+ return (*ptr)->GetOption(key, value, length, error);
+}
+
+AdbcStatusCode PostgresConnectionGetOptionBytes(struct AdbcConnection* connection,
+ const char* key, uint8_t* value,
+ size_t* length, struct AdbcError* error) {
+ if (!connection->private_data) return ADBC_STATUS_INVALID_STATE;
+ auto ptr =
+ reinterpret_cast*>(connection->private_data);
+ return (*ptr)->GetOptionBytes(key, value, length, error);
+}
+
+AdbcStatusCode PostgresConnectionGetOptionDouble(struct AdbcConnection* connection,
+ const char* key, double* value,
+ struct AdbcError* error) {
+ if (!connection->private_data) return ADBC_STATUS_INVALID_STATE;
+ auto ptr =
+ reinterpret_cast*>(connection->private_data);
+ return (*ptr)->GetOptionDouble(key, value, error);
+}
+
+AdbcStatusCode PostgresConnectionGetOptionInt(struct AdbcConnection* connection,
+ const char* key, int64_t* value,
+ struct AdbcError* error) {
+ if (!connection->private_data) return ADBC_STATUS_INVALID_STATE;
+ auto ptr =
+ reinterpret_cast*>(connection->private_data);
+ return (*ptr)->GetOptionInt(key, value, error);
+}
+
+AdbcStatusCode PostgresConnectionGetStatistics(struct AdbcConnection* connection,
+ const char* catalog, const char* db_schema,
+ const char* table_name, char approximate,
+ struct ArrowArrayStream* out,
+ struct AdbcError* error) {
+ if (!connection->private_data) return ADBC_STATUS_INVALID_STATE;
+ auto ptr =
+ reinterpret_cast*>(connection->private_data);
+ return (*ptr)->GetStatistics(catalog, db_schema, table_name, approximate == 1, out,
+ error);
+}
+
+AdbcStatusCode PostgresConnectionGetStatisticNames(struct AdbcConnection* connection,
+ struct ArrowArrayStream* out,
+ struct AdbcError* error) {
+ if (!connection->private_data) return ADBC_STATUS_INVALID_STATE;
+ auto ptr =
+ reinterpret_cast*>(connection->private_data);
+ return (*ptr)->GetStatisticNames(out, error);
+}
+
AdbcStatusCode PostgresConnectionGetTableSchema(
struct AdbcConnection* connection, const char* catalog, const char* db_schema,
const char* table_name, struct ArrowSchema* schema, struct AdbcError* error) {
@@ -213,14 +397,47 @@ AdbcStatusCode PostgresConnectionSetOption(struct AdbcConnection* connection,
return (*ptr)->SetOption(key, value, error);
}
+AdbcStatusCode PostgresConnectionSetOptionBytes(struct AdbcConnection* connection,
+ const char* key, const uint8_t* value,
+ size_t length, struct AdbcError* error) {
+ if (!connection->private_data) return ADBC_STATUS_INVALID_STATE;
+ auto ptr =
+ reinterpret_cast*>(connection->private_data);
+ return (*ptr)->SetOptionBytes(key, value, length, error);
+}
+
+AdbcStatusCode PostgresConnectionSetOptionDouble(struct AdbcConnection* connection,
+ const char* key, double value,
+ struct AdbcError* error) {
+ if (!connection->private_data) return ADBC_STATUS_INVALID_STATE;
+ auto ptr =
+ reinterpret_cast*>(connection->private_data);
+ return (*ptr)->SetOptionDouble(key, value, error);
+}
+
+AdbcStatusCode PostgresConnectionSetOptionInt(struct AdbcConnection* connection,
+ const char* key, int64_t value,
+ struct AdbcError* error) {
+ if (!connection->private_data) return ADBC_STATUS_INVALID_STATE;
+ auto ptr =
+ reinterpret_cast*>(connection->private_data);
+ return (*ptr)->SetOptionInt(key, value, error);
+}
+
} // namespace
+
+AdbcStatusCode AdbcConnectionCancel(struct AdbcConnection* connection,
+ struct AdbcError* error) {
+ return PostgresConnectionCancel(connection, error);
+}
+
AdbcStatusCode AdbcConnectionCommit(struct AdbcConnection* connection,
struct AdbcError* error) {
return PostgresConnectionCommit(connection, error);
}
AdbcStatusCode AdbcConnectionGetInfo(struct AdbcConnection* connection,
- uint32_t* info_codes, size_t info_codes_length,
+ const uint32_t* info_codes, size_t info_codes_length,
struct ArrowArrayStream* stream,
struct AdbcError* error) {
return PostgresConnectionGetInfo(connection, info_codes, info_codes_length, stream,
@@ -237,6 +454,45 @@ AdbcStatusCode AdbcConnectionGetObjects(struct AdbcConnection* connection, int d
table_types, column_name, stream, error);
}
+AdbcStatusCode AdbcConnectionGetOption(struct AdbcConnection* connection, const char* key,
+ char* value, size_t* length,
+ struct AdbcError* error) {
+ return PostgresConnectionGetOption(connection, key, value, length, error);
+}
+
+AdbcStatusCode AdbcConnectionGetOptionBytes(struct AdbcConnection* connection,
+ const char* key, uint8_t* value,
+ size_t* length, struct AdbcError* error) {
+ return PostgresConnectionGetOptionBytes(connection, key, value, length, error);
+}
+
+AdbcStatusCode AdbcConnectionGetOptionInt(struct AdbcConnection* connection,
+ const char* key, int64_t* value,
+ struct AdbcError* error) {
+ return PostgresConnectionGetOptionInt(connection, key, value, error);
+}
+
+AdbcStatusCode AdbcConnectionGetOptionDouble(struct AdbcConnection* connection,
+ const char* key, double* value,
+ struct AdbcError* error) {
+ return PostgresConnectionGetOptionDouble(connection, key, value, error);
+}
+
+AdbcStatusCode AdbcConnectionGetStatistics(struct AdbcConnection* connection,
+ const char* catalog, const char* db_schema,
+ const char* table_name, char approximate,
+ struct ArrowArrayStream* out,
+ struct AdbcError* error) {
+ return PostgresConnectionGetStatistics(connection, catalog, db_schema, table_name,
+ approximate, out, error);
+}
+
+AdbcStatusCode AdbcConnectionGetStatisticNames(struct AdbcConnection* connection,
+ struct ArrowArrayStream* out,
+ struct AdbcError* error) {
+ return PostgresConnectionGetStatisticNames(connection, out, error);
+}
+
AdbcStatusCode AdbcConnectionGetTableSchema(struct AdbcConnection* connection,
const char* catalog, const char* db_schema,
const char* table_name,
@@ -287,6 +543,24 @@ AdbcStatusCode AdbcConnectionSetOption(struct AdbcConnection* connection, const
return PostgresConnectionSetOption(connection, key, value, error);
}
+AdbcStatusCode AdbcConnectionSetOptionBytes(struct AdbcConnection* connection,
+ const char* key, const uint8_t* value,
+ size_t length, struct AdbcError* error) {
+ return PostgresConnectionSetOptionBytes(connection, key, value, length, error);
+}
+
+AdbcStatusCode AdbcConnectionSetOptionInt(struct AdbcConnection* connection,
+ const char* key, int64_t value,
+ struct AdbcError* error) {
+ return PostgresConnectionSetOptionInt(connection, key, value, error);
+}
+
+AdbcStatusCode AdbcConnectionSetOptionDouble(struct AdbcConnection* connection,
+ const char* key, double value,
+ struct AdbcError* error) {
+ return PostgresConnectionSetOptionDouble(connection, key, value, error);
+}
+
// ---------------------------------------------------------------------
// AdbcStatement
@@ -310,6 +584,14 @@ AdbcStatusCode PostgresStatementBindStream(struct AdbcStatement* statement,
return (*ptr)->Bind(stream, error);
}
+AdbcStatusCode PostgresStatementCancel(struct AdbcStatement* statement,
+ struct AdbcError* error) {
+ if (!statement->private_data) return ADBC_STATUS_INVALID_STATE;
+ auto* ptr =
+ reinterpret_cast*>(statement->private_data);
+ return (*ptr)->Cancel(error);
+}
+
AdbcStatusCode PostgresStatementExecutePartitions(struct AdbcStatement* statement,
struct ArrowSchema* schema,
struct AdbcPartitions* partitions,
@@ -329,16 +611,49 @@ AdbcStatusCode PostgresStatementExecuteQuery(struct AdbcStatement* statement,
return (*ptr)->ExecuteQuery(output, rows_affected, error);
}
-AdbcStatusCode PostgresStatementGetPartitionDesc(struct AdbcStatement* statement,
- uint8_t* partition_desc,
- struct AdbcError* error) {
- return ADBC_STATUS_NOT_IMPLEMENTED;
+AdbcStatusCode PostgresStatementExecuteSchema(struct AdbcStatement* statement,
+ struct ArrowSchema* schema,
+ struct AdbcError* error) {
+ if (!statement->private_data) return ADBC_STATUS_INVALID_STATE;
+ auto* ptr =
+ reinterpret_cast*>(statement->private_data);
+ return (*ptr)->ExecuteSchema(schema, error);
}
-AdbcStatusCode PostgresStatementGetPartitionDescSize(struct AdbcStatement* statement,
- size_t* length,
- struct AdbcError* error) {
- return ADBC_STATUS_NOT_IMPLEMENTED;
+AdbcStatusCode PostgresStatementGetOption(struct AdbcStatement* statement,
+ const char* key, char* value, size_t* length,
+ struct AdbcError* error) {
+ if (!statement->private_data) return ADBC_STATUS_INVALID_STATE;
+ auto ptr =
+ reinterpret_cast*>(statement->private_data);
+ return (*ptr)->GetOption(key, value, length, error);
+}
+
+AdbcStatusCode PostgresStatementGetOptionBytes(struct AdbcStatement* statement,
+ const char* key, uint8_t* value,
+ size_t* length, struct AdbcError* error) {
+ if (!statement->private_data) return ADBC_STATUS_INVALID_STATE;
+ auto ptr =
+ reinterpret_cast*>(statement->private_data);
+ return (*ptr)->GetOptionBytes(key, value, length, error);
+}
+
+AdbcStatusCode PostgresStatementGetOptionDouble(struct AdbcStatement* statement,
+ const char* key, double* value,
+ struct AdbcError* error) {
+ if (!statement->private_data) return ADBC_STATUS_INVALID_STATE;
+ auto ptr =
+ reinterpret_cast*>(statement->private_data);
+ return (*ptr)->GetOptionDouble(key, value, error);
+}
+
+AdbcStatusCode PostgresStatementGetOptionInt(struct AdbcStatement* statement,
+ const char* key, int64_t* value,
+ struct AdbcError* error) {
+ if (!statement->private_data) return ADBC_STATUS_INVALID_STATE;
+ auto ptr =
+ reinterpret_cast*>(statement->private_data);
+ return (*ptr)->GetOptionInt(key, value, error);
}
AdbcStatusCode PostgresStatementGetParameterSchema(struct AdbcStatement* statement,
@@ -386,6 +701,33 @@ AdbcStatusCode PostgresStatementSetOption(struct AdbcStatement* statement,
return (*ptr)->SetOption(key, value, error);
}
+AdbcStatusCode PostgresStatementSetOptionBytes(struct AdbcStatement* statement,
+ const char* key, const uint8_t* value,
+ size_t length, struct AdbcError* error) {
+ if (!statement->private_data) return ADBC_STATUS_INVALID_STATE;
+ auto ptr =
+ reinterpret_cast*>(statement->private_data);
+ return (*ptr)->SetOptionBytes(key, value, length, error);
+}
+
+AdbcStatusCode PostgresStatementSetOptionDouble(struct AdbcStatement* statement,
+ const char* key, double value,
+ struct AdbcError* error) {
+ if (!statement->private_data) return ADBC_STATUS_INVALID_STATE;
+ auto ptr =
+ reinterpret_cast*>(statement->private_data);
+ return (*ptr)->SetOptionDouble(key, value, error);
+}
+
+AdbcStatusCode PostgresStatementSetOptionInt(struct AdbcStatement* statement,
+ const char* key, int64_t value,
+ struct AdbcError* error) {
+ if (!statement->private_data) return ADBC_STATUS_INVALID_STATE;
+ auto ptr =
+ reinterpret_cast*>(statement->private_data);
+ return (*ptr)->SetOptionInt(key, value, error);
+}
+
AdbcStatusCode PostgresStatementSetSqlQuery(struct AdbcStatement* statement,
const char* query, struct AdbcError* error) {
if (!statement->private_data) return ADBC_STATUS_INVALID_STATE;
@@ -407,6 +749,11 @@ AdbcStatusCode AdbcStatementBindStream(struct AdbcStatement* statement,
return PostgresStatementBindStream(statement, stream, error);
}
+AdbcStatusCode AdbcStatementCancel(struct AdbcStatement* statement,
+ struct AdbcError* error) {
+ return PostgresStatementCancel(statement, error);
+}
+
AdbcStatusCode AdbcStatementExecutePartitions(struct AdbcStatement* statement,
ArrowSchema* schema,
struct AdbcPartitions* partitions,
@@ -423,16 +770,32 @@ AdbcStatusCode AdbcStatementExecuteQuery(struct AdbcStatement* statement,
return PostgresStatementExecuteQuery(statement, output, rows_affected, error);
}
-AdbcStatusCode AdbcStatementGetPartitionDesc(struct AdbcStatement* statement,
- uint8_t* partition_desc,
- struct AdbcError* error) {
- return PostgresStatementGetPartitionDesc(statement, partition_desc, error);
+AdbcStatusCode AdbcStatementExecuteSchema(struct AdbcStatement* statement,
+ ArrowSchema* schema, struct AdbcError* error) {
+ return PostgresStatementExecuteSchema(statement, schema, error);
}
-AdbcStatusCode AdbcStatementGetPartitionDescSize(struct AdbcStatement* statement,
- size_t* length,
- struct AdbcError* error) {
- return PostgresStatementGetPartitionDescSize(statement, length, error);
+AdbcStatusCode AdbcStatementGetOption(struct AdbcStatement* statement, const char* key,
+ char* value, size_t* length,
+ struct AdbcError* error) {
+ return PostgresStatementGetOption(statement, key, value, length, error);
+}
+
+AdbcStatusCode AdbcStatementGetOptionBytes(struct AdbcStatement* statement,
+ const char* key, uint8_t* value,
+ size_t* length, struct AdbcError* error) {
+ return PostgresStatementGetOptionBytes(statement, key, value, length, error);
+}
+
+AdbcStatusCode AdbcStatementGetOptionInt(struct AdbcStatement* statement, const char* key,
+ int64_t* value, struct AdbcError* error) {
+ return PostgresStatementGetOptionInt(statement, key, value, error);
+}
+
+AdbcStatusCode AdbcStatementGetOptionDouble(struct AdbcStatement* statement,
+ const char* key, double* value,
+ struct AdbcError* error) {
+ return PostgresStatementGetOptionDouble(statement, key, value, error);
}
AdbcStatusCode AdbcStatementGetParameterSchema(struct AdbcStatement* statement,
@@ -462,6 +825,23 @@ AdbcStatusCode AdbcStatementSetOption(struct AdbcStatement* statement, const cha
return PostgresStatementSetOption(statement, key, value, error);
}
+AdbcStatusCode AdbcStatementSetOptionBytes(struct AdbcStatement* statement,
+ const char* key, const uint8_t* value,
+ size_t length, struct AdbcError* error) {
+ return PostgresStatementSetOptionBytes(statement, key, value, length, error);
+}
+
+AdbcStatusCode AdbcStatementSetOptionInt(struct AdbcStatement* statement, const char* key,
+ int64_t value, struct AdbcError* error) {
+ return PostgresStatementSetOptionInt(statement, key, value, error);
+}
+
+AdbcStatusCode AdbcStatementSetOptionDouble(struct AdbcStatement* statement,
+ const char* key, double value,
+ struct AdbcError* error) {
+ return PostgresStatementSetOptionDouble(statement, key, value, error);
+}
+
AdbcStatusCode AdbcStatementSetSqlQuery(struct AdbcStatement* statement,
const char* query, struct AdbcError* error) {
return PostgresStatementSetSqlQuery(statement, query, error);
@@ -469,11 +849,53 @@ AdbcStatusCode AdbcStatementSetSqlQuery(struct AdbcStatement* statement,
extern "C" {
ADBC_EXPORT
-AdbcStatusCode AdbcDriverInit(int version, void* raw_driver, struct AdbcError* error) {
- if (version != ADBC_VERSION_1_0_0) return ADBC_STATUS_NOT_IMPLEMENTED;
+AdbcStatusCode PostgresqlDriverInit(int version, void* raw_driver,
+ struct AdbcError* error) {
+ if (version != ADBC_VERSION_1_0_0 && version != ADBC_VERSION_1_1_0) {
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+ }
+ if (!raw_driver) return ADBC_STATUS_INVALID_ARGUMENT;
auto* driver = reinterpret_cast(raw_driver);
- std::memset(driver, 0, sizeof(*driver));
+ if (version >= ADBC_VERSION_1_1_0) {
+ std::memset(driver, 0, ADBC_DRIVER_1_1_0_SIZE);
+
+ driver->ErrorGetDetailCount = CommonErrorGetDetailCount;
+ driver->ErrorGetDetail = CommonErrorGetDetail;
+ driver->ErrorFromArrayStream = PostgresErrorFromArrayStream;
+
+ driver->DatabaseGetOption = PostgresDatabaseGetOption;
+ driver->DatabaseGetOptionBytes = PostgresDatabaseGetOptionBytes;
+ driver->DatabaseGetOptionDouble = PostgresDatabaseGetOptionDouble;
+ driver->DatabaseGetOptionInt = PostgresDatabaseGetOptionInt;
+ driver->DatabaseSetOptionBytes = PostgresDatabaseSetOptionBytes;
+ driver->DatabaseSetOptionDouble = PostgresDatabaseSetOptionDouble;
+ driver->DatabaseSetOptionInt = PostgresDatabaseSetOptionInt;
+
+ driver->ConnectionCancel = PostgresConnectionCancel;
+ driver->ConnectionGetOption = PostgresConnectionGetOption;
+ driver->ConnectionGetOptionBytes = PostgresConnectionGetOptionBytes;
+ driver->ConnectionGetOptionDouble = PostgresConnectionGetOptionDouble;
+ driver->ConnectionGetOptionInt = PostgresConnectionGetOptionInt;
+ driver->ConnectionGetStatistics = PostgresConnectionGetStatistics;
+ driver->ConnectionGetStatisticNames = PostgresConnectionGetStatisticNames;
+ driver->ConnectionSetOptionBytes = PostgresConnectionSetOptionBytes;
+ driver->ConnectionSetOptionDouble = PostgresConnectionSetOptionDouble;
+ driver->ConnectionSetOptionInt = PostgresConnectionSetOptionInt;
+
+ driver->StatementCancel = PostgresStatementCancel;
+ driver->StatementExecuteSchema = PostgresStatementExecuteSchema;
+ driver->StatementGetOption = PostgresStatementGetOption;
+ driver->StatementGetOptionBytes = PostgresStatementGetOptionBytes;
+ driver->StatementGetOptionDouble = PostgresStatementGetOptionDouble;
+ driver->StatementGetOptionInt = PostgresStatementGetOptionInt;
+ driver->StatementSetOptionBytes = PostgresStatementSetOptionBytes;
+ driver->StatementSetOptionDouble = PostgresStatementSetOptionDouble;
+ driver->StatementSetOptionInt = PostgresStatementSetOptionInt;
+ } else {
+ std::memset(driver, 0, ADBC_DRIVER_1_0_0_SIZE);
+ }
+
driver->DatabaseInit = PostgresDatabaseInit;
driver->DatabaseNew = PostgresDatabaseNew;
driver->DatabaseRelease = PostgresDatabaseRelease;
@@ -501,6 +923,12 @@ AdbcStatusCode AdbcDriverInit(int version, void* raw_driver, struct AdbcError* e
driver->StatementRelease = PostgresStatementRelease;
driver->StatementSetOption = PostgresStatementSetOption;
driver->StatementSetSqlQuery = PostgresStatementSetSqlQuery;
+
return ADBC_STATUS_OK;
}
+
+ADBC_EXPORT
+AdbcStatusCode AdbcDriverInit(int version, void* raw_driver, struct AdbcError* error) {
+ return PostgresqlDriverInit(version, raw_driver, error);
+}
}
diff --git a/c/driver/postgresql/postgresql_test.cc b/c/driver/postgresql/postgresql_test.cc
index a826e17267..84a264f3c8 100644
--- a/c/driver/postgresql/postgresql_test.cc
+++ b/c/driver/postgresql/postgresql_test.cc
@@ -15,6 +15,8 @@
// specific language governing permissions and limitations
// under the License.
+#include
+#include
#include
#include
#include
@@ -25,8 +27,9 @@
#include
#include
#include
-#include "common/utils.h"
+#include "common/utils.h"
+#include "database.h"
#include "validation/adbc_validation.h"
#include "validation/adbc_validation_util.h"
@@ -103,6 +106,30 @@ class PostgresQuirks : public adbc_validation::DriverQuirks {
std::string catalog() const override { return "postgres"; }
std::string db_schema() const override { return "public"; }
+
+ bool supports_cancel() const override { return true; }
+ bool supports_execute_schema() const override { return true; }
+ std::optional supports_get_sql_info(
+ uint32_t info_code) const override {
+ switch (info_code) {
+ case ADBC_INFO_DRIVER_ADBC_VERSION:
+ return ADBC_VERSION_1_1_0;
+ case ADBC_INFO_DRIVER_NAME:
+ return "ADBC PostgreSQL Driver";
+ case ADBC_INFO_DRIVER_VERSION:
+ return "(unknown)";
+ case ADBC_INFO_VENDOR_NAME:
+ return "PostgreSQL";
+ case ADBC_INFO_VENDOR_VERSION:
+ // Strings are checked via substring match
+ return "15";
+ default:
+ return std::nullopt;
+ }
+ }
+ bool supports_metadata_current_catalog() const override { return true; }
+ bool supports_metadata_current_db_schema() const override { return true; }
+ bool supports_statistics() const override { return true; }
};
class PostgresDatabaseTest : public ::testing::Test,
@@ -117,6 +144,20 @@ class PostgresDatabaseTest : public ::testing::Test,
};
ADBCV_TEST_DATABASE(PostgresDatabaseTest)
+TEST_F(PostgresDatabaseTest, AdbcDriverBackwardsCompatibility) {
+ // XXX: sketchy cast
+ auto* driver = static_cast(malloc(ADBC_DRIVER_1_0_0_SIZE));
+ std::memset(driver, 0, ADBC_DRIVER_1_0_0_SIZE);
+
+ ASSERT_THAT(::PostgresqlDriverInit(ADBC_VERSION_1_0_0, driver, &error),
+ IsOkStatus(&error));
+
+ ASSERT_THAT(::PostgresqlDriverInit(424242, driver, &error),
+ IsStatus(ADBC_STATUS_NOT_IMPLEMENTED, &error));
+
+ free(driver);
+}
+
class PostgresConnectionTest : public ::testing::Test,
public adbc_validation::ConnectionTest {
public:
@@ -134,10 +175,8 @@ TEST_F(PostgresConnectionTest, GetInfoMetadata) {
adbc_validation::StreamReader reader;
std::vector info = {
- ADBC_INFO_DRIVER_NAME,
- ADBC_INFO_DRIVER_VERSION,
- ADBC_INFO_VENDOR_NAME,
- ADBC_INFO_VENDOR_VERSION,
+ ADBC_INFO_DRIVER_NAME, ADBC_INFO_DRIVER_VERSION, ADBC_INFO_DRIVER_ADBC_VERSION,
+ ADBC_INFO_VENDOR_NAME, ADBC_INFO_VENDOR_VERSION,
};
ASSERT_THAT(AdbcConnectionGetInfo(&connection, info.data(), info.size(),
&reader.stream.value, &error),
@@ -153,29 +192,30 @@ TEST_F(PostgresConnectionTest, GetInfoMetadata) {
ASSERT_FALSE(ArrowArrayViewIsNull(reader.array_view->children[0], row));
const uint32_t code =
reader.array_view->children[0]->buffer_views[1].data.as_uint32[row];
+ const uint32_t offset =
+ reader.array_view->children[1]->buffer_views[1].data.as_int32[row];
seen.push_back(code);
- int str_child_index = 0;
- struct ArrowArrayView* str_child =
- reader.array_view->children[1]->children[str_child_index];
+ struct ArrowArrayView* str_child = reader.array_view->children[1]->children[0];
+ struct ArrowArrayView* int_child = reader.array_view->children[1]->children[2];
switch (code) {
case ADBC_INFO_DRIVER_NAME: {
- ArrowStringView val = ArrowArrayViewGetStringUnsafe(str_child, 0);
+ ArrowStringView val = ArrowArrayViewGetStringUnsafe(str_child, offset);
EXPECT_EQ("ADBC PostgreSQL Driver", std::string(val.data, val.size_bytes));
break;
}
case ADBC_INFO_DRIVER_VERSION: {
- ArrowStringView val = ArrowArrayViewGetStringUnsafe(str_child, 1);
+ ArrowStringView val = ArrowArrayViewGetStringUnsafe(str_child, offset);
EXPECT_EQ("(unknown)", std::string(val.data, val.size_bytes));
break;
}
case ADBC_INFO_VENDOR_NAME: {
- ArrowStringView val = ArrowArrayViewGetStringUnsafe(str_child, 2);
+ ArrowStringView val = ArrowArrayViewGetStringUnsafe(str_child, offset);
EXPECT_EQ("PostgreSQL", std::string(val.data, val.size_bytes));
break;
}
case ADBC_INFO_VENDOR_VERSION: {
- ArrowStringView val = ArrowArrayViewGetStringUnsafe(str_child, 3);
+ ArrowStringView val = ArrowArrayViewGetStringUnsafe(str_child, offset);
#ifdef __WIN32
const char* pater = "\\d\\d\\d\\d\\d\\d";
#else
@@ -185,6 +225,10 @@ TEST_F(PostgresConnectionTest, GetInfoMetadata) {
::testing::MatchesRegex(pater));
break;
}
+ case ADBC_INFO_DRIVER_ADBC_VERSION: {
+ EXPECT_EQ(ADBC_VERSION_1_1_0, ArrowArrayViewGetIntUnsafe(int_child, offset));
+ break;
+ }
default:
// Ignored
break;
@@ -198,10 +242,6 @@ TEST_F(PostgresConnectionTest, GetObjectsGetCatalogs) {
ASSERT_THAT(AdbcConnectionNew(&connection, &error), IsOkStatus(&error));
ASSERT_THAT(AdbcConnectionInit(&connection, &database, &error), IsOkStatus(&error));
- if (!quirks()->supports_get_objects()) {
- GTEST_SKIP();
- }
-
adbc_validation::StreamReader reader;
ASSERT_THAT(
AdbcConnectionGetObjects(&connection, ADBC_OBJECT_DEPTH_CATALOGS, nullptr, nullptr,
@@ -228,10 +268,6 @@ TEST_F(PostgresConnectionTest, GetObjectsGetDbSchemas) {
ASSERT_THAT(AdbcConnectionNew(&connection, &error), IsOkStatus(&error));
ASSERT_THAT(AdbcConnectionInit(&connection, &database, &error), IsOkStatus(&error));
- if (!quirks()->supports_get_objects()) {
- GTEST_SKIP();
- }
-
adbc_validation::StreamReader reader;
ASSERT_THAT(AdbcConnectionGetObjects(&connection, ADBC_OBJECT_DEPTH_DB_SCHEMAS, nullptr,
nullptr, nullptr, nullptr, nullptr,
@@ -255,10 +291,6 @@ TEST_F(PostgresConnectionTest, GetObjectsGetAllFindsPrimaryKey) {
ASSERT_THAT(AdbcConnectionNew(&connection, &error), IsOkStatus(&error));
ASSERT_THAT(AdbcConnectionInit(&connection, &database, &error), IsOkStatus(&error));
- if (!quirks()->supports_get_objects()) {
- GTEST_SKIP();
- }
-
ASSERT_THAT(quirks()->DropTable(&connection, "adbc_pkey_test", &error),
IsOkStatus(&error));
@@ -329,10 +361,6 @@ TEST_F(PostgresConnectionTest, GetObjectsGetAllFindsForeignKey) {
ASSERT_THAT(AdbcConnectionNew(&connection, &error), IsOkStatus(&error));
ASSERT_THAT(AdbcConnectionInit(&connection, &database, &error), IsOkStatus(&error));
- if (!quirks()->supports_get_objects()) {
- GTEST_SKIP();
- }
-
ASSERT_THAT(quirks()->DropTable(&connection, "adbc_fkey_test", &error),
IsOkStatus(&error));
ASSERT_THAT(quirks()->DropTable(&connection, "adbc_fkey_test_base", &error),
@@ -450,10 +478,6 @@ TEST_F(PostgresConnectionTest, GetObjectsTableTypesFilter) {
ASSERT_THAT(AdbcConnectionNew(&connection, &error), IsOkStatus(&error));
ASSERT_THAT(AdbcConnectionInit(&connection, &database, &error), IsOkStatus(&error));
- if (!quirks()->supports_get_objects()) {
- GTEST_SKIP();
- }
-
ASSERT_THAT(quirks()->DropView(&connection, "adbc_table_types_view_test", &error),
IsOkStatus(&error));
ASSERT_THAT(quirks()->DropTable(&connection, "adbc_table_types_table_test", &error),
@@ -516,7 +540,7 @@ TEST_F(PostgresConnectionTest, GetObjectsTableTypesFilter) {
}
TEST_F(PostgresConnectionTest, MetadataGetTableSchemaInjection) {
- if (!quirks()->supports_bulk_ingest()) {
+ if (!quirks()->supports_bulk_ingest(ADBC_INGEST_OPTION_MODE_CREATE)) {
GTEST_SKIP();
}
ASSERT_THAT(AdbcConnectionNew(&connection, &error), IsOkStatus(&error));
@@ -531,13 +555,13 @@ TEST_F(PostgresConnectionTest, MetadataGetTableSchemaInjection) {
/*db_schema=*/nullptr,
"0'::int; DROP TABLE bulk_ingest;--",
&schema.value, &error),
- IsStatus(ADBC_STATUS_IO, &error));
+ IsStatus(ADBC_STATUS_INVALID_ARGUMENT, &error));
ASSERT_THAT(
AdbcConnectionGetTableSchema(&connection, /*catalog=*/nullptr,
/*db_schema=*/"0'::int; DROP TABLE bulk_ingest;--",
"DROP TABLE bulk_ingest;", &schema.value, &error),
- IsStatus(ADBC_STATUS_IO, &error));
+ IsStatus(ADBC_STATUS_INVALID_ARGUMENT, &error));
ASSERT_THAT(AdbcConnectionGetTableSchema(&connection, /*catalog=*/nullptr,
/*db_schema=*/nullptr, "bulk_ingest",
@@ -549,6 +573,236 @@ TEST_F(PostgresConnectionTest, MetadataGetTableSchemaInjection) {
{"strings", NANOARROW_TYPE_STRING, true}}));
}
+TEST_F(PostgresConnectionTest, MetadataSetCurrentDbSchema) {
+ ASSERT_THAT(AdbcConnectionNew(&connection, &error), IsOkStatus(&error));
+ ASSERT_THAT(AdbcConnectionInit(&connection, &database, &error), IsOkStatus(&error));
+
+ {
+ adbc_validation::Handle statement;
+ ASSERT_THAT(AdbcStatementNew(&connection, &statement.value, &error),
+ IsOkStatus(&error));
+
+ ASSERT_THAT(AdbcStatementSetSqlQuery(
+ &statement.value, "CREATE SCHEMA IF NOT EXISTS testschema", &error),
+ IsOkStatus(&error));
+ ASSERT_THAT(AdbcStatementExecuteQuery(&statement.value, nullptr, nullptr, &error),
+ IsOkStatus(&error));
+
+ ASSERT_THAT(
+ AdbcStatementSetSqlQuery(
+ &statement.value,
+ "CREATE TABLE IF NOT EXISTS testschema.schematable (ints INT)", &error),
+ IsOkStatus(&error));
+ ASSERT_THAT(AdbcStatementExecuteQuery(&statement.value, nullptr, nullptr, &error),
+ IsOkStatus(&error));
+
+ ASSERT_THAT(AdbcStatementRelease(&statement.value, &error), IsOkStatus(&error));
+ }
+
+ adbc_validation::Handle statement;
+ ASSERT_THAT(AdbcStatementNew(&connection, &statement.value, &error),
+ IsOkStatus(&error));
+
+ // Table does not exist in this schema
+ error.vendor_code = ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA;
+ ASSERT_THAT(
+ AdbcStatementSetSqlQuery(&statement.value, "SELECT * FROM schematable", &error),
+ IsOkStatus(&error));
+ ASSERT_THAT(AdbcStatementExecuteQuery(&statement.value, nullptr, nullptr, &error),
+ IsStatus(ADBC_STATUS_INVALID_ARGUMENT, &error));
+ // 42P01 = table not found
+ ASSERT_EQ("42P01", std::string_view(error.sqlstate, 5));
+ ASSERT_NE(0, AdbcErrorGetDetailCount(&error));
+ bool found = false;
+ for (int i = 0; i < AdbcErrorGetDetailCount(&error); i++) {
+ struct AdbcErrorDetail detail = AdbcErrorGetDetail(&error, i);
+ if (std::strcmp(detail.key, "PG_DIAG_MESSAGE_PRIMARY") == 0) {
+ found = true;
+ std::string_view message(reinterpret_cast(detail.value),
+ detail.value_length);
+ ASSERT_THAT(message, ::testing::HasSubstr("schematable"));
+ }
+ }
+ error.release(&error);
+ ASSERT_TRUE(found) << "Did not find expected error detail";
+
+ ASSERT_THAT(
+ AdbcConnectionSetOption(&connection, ADBC_CONNECTION_OPTION_CURRENT_DB_SCHEMA,
+ "testschema", &error),
+ IsOkStatus(&error));
+
+ ASSERT_THAT(
+ AdbcStatementSetSqlQuery(&statement.value, "SELECT * FROM schematable", &error),
+ IsOkStatus(&error));
+ ASSERT_THAT(AdbcStatementExecuteQuery(&statement.value, nullptr, nullptr, &error),
+ IsOkStatus(&error));
+
+ ASSERT_THAT(AdbcStatementRelease(&statement.value, &error), IsOkStatus(&error));
+}
+
+TEST_F(PostgresConnectionTest, MetadataGetStatistics) {
+ if (!quirks()->supports_statistics()) {
+ GTEST_SKIP();
+ }
+
+ ASSERT_THAT(AdbcConnectionNew(&connection, &error), IsOkStatus(&error));
+ ASSERT_THAT(AdbcConnectionInit(&connection, &database, &error), IsOkStatus(&error));
+
+ // Create sample table
+ {
+ adbc_validation::Handle statement;
+ ASSERT_THAT(AdbcStatementNew(&connection, &statement.value, &error),
+ IsOkStatus(&error));
+
+ ASSERT_THAT(AdbcStatementSetSqlQuery(&statement.value,
+ "DROP TABLE IF EXISTS statstable", &error),
+ IsOkStatus(&error));
+ ASSERT_THAT(AdbcStatementExecuteQuery(&statement.value, nullptr, nullptr, &error),
+ IsOkStatus(&error));
+
+ ASSERT_THAT(
+ AdbcStatementSetSqlQuery(&statement.value,
+ "CREATE TABLE statstable (ints INT, strs TEXT)", &error),
+ IsOkStatus(&error));
+ ASSERT_THAT(AdbcStatementExecuteQuery(&statement.value, nullptr, nullptr, &error),
+ IsOkStatus(&error));
+
+ ASSERT_THAT(
+ AdbcStatementSetSqlQuery(
+ &statement.value,
+ "INSERT INTO statstable VALUES (1, 'a'), (NULL, 'bcd'), (-5, NULL)", &error),
+ IsOkStatus(&error));
+ ASSERT_THAT(AdbcStatementExecuteQuery(&statement.value, nullptr, nullptr, &error),
+ IsOkStatus(&error));
+
+ ASSERT_THAT(AdbcStatementSetSqlQuery(&statement.value, "ANALYZE statstable", &error),
+ IsOkStatus(&error));
+ ASSERT_THAT(AdbcStatementExecuteQuery(&statement.value, nullptr, nullptr, &error),
+ IsOkStatus(&error));
+
+ ASSERT_THAT(AdbcStatementRelease(&statement.value, &error), IsOkStatus(&error));
+ }
+
+ adbc_validation::StreamReader reader;
+ ASSERT_THAT(
+ AdbcConnectionGetStatistics(&connection, nullptr, quirks()->db_schema().c_str(),
+ "statstable", 1, &reader.stream.value, &error),
+ IsOkStatus(&error));
+ ASSERT_NO_FATAL_FAILURE(reader.GetSchema());
+
+ ASSERT_NO_FATAL_FAILURE(adbc_validation::CompareSchema(
+ &reader.schema.value, {
+ {"catalog_name", NANOARROW_TYPE_STRING, true},
+ {"catalog_db_schemas", NANOARROW_TYPE_LIST, false},
+ }));
+
+ ASSERT_NO_FATAL_FAILURE(adbc_validation::CompareSchema(
+ reader.schema->children[1]->children[0],
+ {
+ {"db_schema_name", NANOARROW_TYPE_STRING, true},
+ {"db_schema_statistics", NANOARROW_TYPE_LIST, false},
+ }));
+
+ ASSERT_NO_FATAL_FAILURE(adbc_validation::CompareSchema(
+ reader.schema->children[1]->children[0]->children[1]->children[0],
+ {
+ {"table_name", NANOARROW_TYPE_STRING, false},
+ {"column_name", NANOARROW_TYPE_STRING, true},
+ {"statistic_key", NANOARROW_TYPE_INT16, false},
+ {"statistic_value", NANOARROW_TYPE_DENSE_UNION, false},
+ {"statistic_is_approximate", NANOARROW_TYPE_BOOL, false},
+ }));
+
+ ASSERT_NO_FATAL_FAILURE(adbc_validation::CompareSchema(
+ reader.schema->children[1]->children[0]->children[1]->children[0]->children[3],
+ {
+ {"int64", NANOARROW_TYPE_INT64, true},
+ {"uint64", NANOARROW_TYPE_UINT64, true},
+ {"float64", NANOARROW_TYPE_DOUBLE, true},
+ {"binary", NANOARROW_TYPE_BINARY, true},
+ }));
+
+ std::vector, int16_t, int64_t>> seen;
+ while (true) {
+ ASSERT_NO_FATAL_FAILURE(reader.Next());
+ if (!reader.array->release) break;
+
+ for (int64_t catalog_index = 0; catalog_index < reader.array->length;
+ catalog_index++) {
+ struct ArrowStringView catalog_name =
+ ArrowArrayViewGetStringUnsafe(reader.array_view->children[0], catalog_index);
+ ASSERT_EQ(quirks()->catalog(),
+ std::string_view(catalog_name.data,
+ static_cast(catalog_name.size_bytes)));
+
+ struct ArrowArrayView* catalog_db_schemas = reader.array_view->children[1];
+ struct ArrowArrayView* schema_stats = catalog_db_schemas->children[0]->children[1];
+ struct ArrowArrayView* stats =
+ catalog_db_schemas->children[0]->children[1]->children[0];
+ for (int64_t schema_index =
+ ArrowArrayViewListChildOffset(catalog_db_schemas, catalog_index);
+ schema_index <
+ ArrowArrayViewListChildOffset(catalog_db_schemas, catalog_index + 1);
+ schema_index++) {
+ struct ArrowStringView schema_name = ArrowArrayViewGetStringUnsafe(
+ catalog_db_schemas->children[0]->children[0], schema_index);
+ ASSERT_EQ(quirks()->db_schema(),
+ std::string_view(schema_name.data,
+ static_cast(schema_name.size_bytes)));
+
+ for (int64_t stat_index =
+ ArrowArrayViewListChildOffset(schema_stats, schema_index);
+ stat_index < ArrowArrayViewListChildOffset(schema_stats, schema_index + 1);
+ stat_index++) {
+ struct ArrowStringView table_name =
+ ArrowArrayViewGetStringUnsafe(stats->children[0], stat_index);
+ ASSERT_EQ("statstable",
+ std::string_view(table_name.data,
+ static_cast(table_name.size_bytes)));
+ std::optional column_name;
+ if (!ArrowArrayViewIsNull(stats->children[1], stat_index)) {
+ struct ArrowStringView value =
+ ArrowArrayViewGetStringUnsafe(stats->children[1], stat_index);
+ column_name = std::string(value.data, value.size_bytes);
+ }
+ ASSERT_TRUE(ArrowArrayViewGetIntUnsafe(stats->children[4], stat_index));
+
+ const int16_t stat_key = static_cast(
+ ArrowArrayViewGetIntUnsafe(stats->children[2], stat_index));
+ const int32_t offset =
+ stats->children[3]->buffer_views[1].data.as_int32[stat_index];
+ int64_t stat_value;
+ switch (stat_key) {
+ case ADBC_STATISTIC_AVERAGE_BYTE_WIDTH_KEY:
+ case ADBC_STATISTIC_DISTINCT_COUNT_KEY:
+ case ADBC_STATISTIC_NULL_COUNT_KEY:
+ case ADBC_STATISTIC_ROW_COUNT_KEY:
+ stat_value = static_cast(
+ std::round(100 * ArrowArrayViewGetDoubleUnsafe(
+ stats->children[3]->children[2], offset)));
+ break;
+ default:
+ continue;
+ }
+ seen.emplace_back(std::move(column_name), stat_key, stat_value);
+ }
+ }
+ }
+ }
+
+ ASSERT_THAT(seen,
+ ::testing::UnorderedElementsAreArray(
+ std::vector, int16_t, int64_t>>{
+ {"ints", ADBC_STATISTIC_AVERAGE_BYTE_WIDTH_KEY, 400},
+ {"strs", ADBC_STATISTIC_AVERAGE_BYTE_WIDTH_KEY, 300},
+ {"ints", ADBC_STATISTIC_NULL_COUNT_KEY, 100},
+ {"strs", ADBC_STATISTIC_NULL_COUNT_KEY, 100},
+ {"ints", ADBC_STATISTIC_DISTINCT_COUNT_KEY, 200},
+ {"strs", ADBC_STATISTIC_DISTINCT_COUNT_KEY, 200},
+ {std::nullopt, ADBC_STATISTIC_ROW_COUNT_KEY, 300},
+ }));
+}
+
ADBCV_TEST_CONNECTION(PostgresConnectionTest)
class PostgresStatementTest : public ::testing::Test,
@@ -704,6 +958,67 @@ TEST_F(PostgresStatementTest, BatchSizeHint) {
}
}
+// Test that an ADBC 1.0.0-sized error still works
+TEST_F(PostgresStatementTest, AdbcErrorBackwardsCompatibility) {
+ // XXX: sketchy cast
+ auto* error = static_cast(malloc(ADBC_ERROR_1_0_0_SIZE));
+ std::memset(error, 0, ADBC_ERROR_1_0_0_SIZE);
+
+ ASSERT_THAT(AdbcStatementNew(&connection, &statement, error), IsOkStatus(error));
+ ASSERT_THAT(
+ AdbcStatementSetSqlQuery(&statement, "SELECT * FROM thistabledoesnotexist", error),
+ IsOkStatus(error));
+ adbc_validation::StreamReader reader;
+ ASSERT_THAT(AdbcStatementExecuteQuery(&statement, &reader.stream.value,
+ &reader.rows_affected, error),
+ IsStatus(ADBC_STATUS_INVALID_ARGUMENT, error));
+
+ ASSERT_EQ("42P01", std::string_view(error->sqlstate, 5));
+ ASSERT_EQ(0, AdbcErrorGetDetailCount(error));
+
+ error->release(error);
+ free(error);
+}
+
+TEST_F(PostgresStatementTest, Cancel) {
+ ASSERT_THAT(AdbcStatementNew(&connection, &statement, &error), IsOkStatus(&error));
+
+ for (const char* query : {
+ "DROP TABLE IF EXISTS test_cancel",
+ "CREATE TABLE test_cancel (ints INT)",
+ R"(INSERT INTO test_cancel (ints)
+ SELECT g :: INT FROM GENERATE_SERIES(1, 65536) temp(g))",
+ }) {
+ ASSERT_THAT(AdbcStatementSetSqlQuery(&statement, query, &error), IsOkStatus(&error));
+ ASSERT_THAT(AdbcStatementExecuteQuery(&statement, nullptr, nullptr, &error),
+ IsOkStatus(&error));
+ }
+
+ ASSERT_THAT(AdbcStatementSetSqlQuery(&statement, "SELECT * FROM test_cancel", &error),
+ IsOkStatus(&error));
+ adbc_validation::StreamReader reader;
+ ASSERT_THAT(AdbcStatementExecuteQuery(&statement, &reader.stream.value,
+ &reader.rows_affected, &error),
+ IsOkStatus(&error));
+
+ ASSERT_THAT(AdbcStatementCancel(&statement, &error), IsOkStatus(&error));
+
+ int retcode = 0;
+ while (true) {
+ retcode = reader.MaybeNext();
+ if (retcode != 0 || !reader.array->release) break;
+ }
+
+ ASSERT_EQ(ECANCELED, retcode);
+ AdbcStatusCode status = ADBC_STATUS_OK;
+ const struct AdbcError* detail =
+ AdbcErrorFromArrayStream(&reader.stream.value, &status);
+ ASSERT_NE(nullptr, detail);
+ ASSERT_EQ(ADBC_STATUS_CANCELLED, status);
+ ASSERT_EQ("57014", std::string_view(detail->sqlstate, 5));
+ ASSERT_NE(0, AdbcErrorGetDetailCount(detail));
+}
+
struct TypeTestCase {
std::string name;
std::string sql_type;
diff --git a/c/driver/postgresql/statement.cc b/c/driver/postgresql/statement.cc
index 0b8f1fc080..6ea52b53ea 100644
--- a/c/driver/postgresql/statement.cc
+++ b/c/driver/postgresql/statement.cc
@@ -33,6 +33,7 @@
#include "common/utils.h"
#include "connection.h"
+#include "error.h"
#include "postgres_copy_reader.h"
#include "postgres_type.h"
#include "postgres_util.h"
@@ -272,20 +273,23 @@ struct BindStream {
if (autocommit) {
PGresult* begin_result = PQexec(conn, "BEGIN");
if (PQresultStatus(begin_result) != PGRES_COMMAND_OK) {
- SetError(error, "[libpq] Failed to begin transaction for timezone data: %s",
- PQerrorMessage(conn));
+ AdbcStatusCode code =
+ SetError(error, begin_result,
+ "[libpq] Failed to begin transaction for timezone data: %s",
+ PQerrorMessage(conn));
PQclear(begin_result);
- return ADBC_STATUS_IO;
+ return code;
}
PQclear(begin_result);
}
PGresult* get_tz_result = PQexec(conn, "SELECT current_setting('TIMEZONE')");
if (PQresultStatus(get_tz_result) != PGRES_TUPLES_OK) {
- SetError(error, "[libpq] Could not query current timezone: %s",
- PQerrorMessage(conn));
+ AdbcStatusCode code = SetError(error, get_tz_result,
+ "[libpq] Could not query current timezone: %s",
+ PQerrorMessage(conn));
PQclear(get_tz_result);
- return ADBC_STATUS_IO;
+ return code;
}
tz_setting = std::string(PQgetvalue(get_tz_result, 0, 0));
@@ -293,10 +297,11 @@ struct BindStream {
PGresult* set_utc_result = PQexec(conn, "SET TIME ZONE 'UTC'");
if (PQresultStatus(set_utc_result) != PGRES_COMMAND_OK) {
- SetError(error, "[libpq] Failed to set time zone to UTC: %s",
- PQerrorMessage(conn));
+ AdbcStatusCode code = SetError(error, set_utc_result,
+ "[libpq] Failed to set time zone to UTC: %s",
+ PQerrorMessage(conn));
PQclear(set_utc_result);
- return ADBC_STATUS_IO;
+ return code;
}
PQclear(set_utc_result);
break;
@@ -306,10 +311,11 @@ struct BindStream {
PGresult* result = PQprepare(conn, /*stmtName=*/"", query.c_str(),
/*nParams=*/bind_schema->n_children, param_types.data());
if (PQresultStatus(result) != PGRES_COMMAND_OK) {
- SetError(error, "[libpq] Failed to prepare query: %s\nQuery was:%s",
- PQerrorMessage(conn), query.c_str());
+ AdbcStatusCode code =
+ SetError(error, result, "[libpq] Failed to prepare query: %s\nQuery was:%s",
+ PQerrorMessage(conn), query.c_str());
PQclear(result);
- return ADBC_STATUS_IO;
+ return code;
}
PQclear(result);
return ADBC_STATUS_OK;
@@ -476,10 +482,11 @@ struct BindStream {
/*resultFormat=*/0 /*text*/);
if (PQresultStatus(result) != PGRES_COMMAND_OK) {
- SetError(error, "%s%s", "[libpq] Failed to execute prepared statement: ",
- PQerrorMessage(conn));
+ AdbcStatusCode code = SetError(
+ error, result, "%s%s",
+ "[libpq] Failed to execute prepared statement: ", PQerrorMessage(conn));
PQclear(result);
- return ADBC_STATUS_IO;
+ return code;
}
PQclear(result);
@@ -490,18 +497,21 @@ struct BindStream {
std::string reset_query = "SET TIME ZONE '" + tz_setting + "'";
PGresult* reset_tz_result = PQexec(conn, reset_query.c_str());
if (PQresultStatus(reset_tz_result) != PGRES_COMMAND_OK) {
- SetError(error, "[libpq] Failed to reset time zone: %s", PQerrorMessage(conn));
+ AdbcStatusCode code =
+ SetError(error, reset_tz_result, "[libpq] Failed to reset time zone: %s",
+ PQerrorMessage(conn));
PQclear(reset_tz_result);
- return ADBC_STATUS_IO;
+ return code;
}
PQclear(reset_tz_result);
PGresult* commit_result = PQexec(conn, "COMMIT");
if (PQresultStatus(commit_result) != PGRES_COMMAND_OK) {
- SetError(error, "[libpq] Failed to commit transaction: %s",
- PQerrorMessage(conn));
+ AdbcStatusCode code =
+ SetError(error, commit_result, "[libpq] Failed to commit transaction: %s",
+ PQerrorMessage(conn));
PQclear(commit_result);
- return ADBC_STATUS_IO;
+ return code;
}
PQclear(commit_result);
}
@@ -516,12 +526,13 @@ int TupleReader::GetSchema(struct ArrowSchema* out) {
int na_res = copy_reader_->GetSchema(out);
if (out->release == nullptr) {
- StringBuilderAppend(&error_builder_,
- "[libpq] Result set was already consumed or freed");
- return EINVAL;
+ SetError(&error_, "[libpq] Result set was already consumed or freed");
+ status_ = ADBC_STATUS_INVALID_STATE;
+ return AdbcStatusCodeToErrno(status_);
} else if (na_res != NANOARROW_OK) {
// e.g., Can't allocate memory
- StringBuilderAppend(&error_builder_, "[libpq] Error copying schema");
+ SetError(&error_, "[libpq] Error copying schema");
+ status_ = ADBC_STATUS_INTERNAL;
}
return na_res;
@@ -534,15 +545,16 @@ int TupleReader::InitQueryAndFetchFirst(struct ArrowError* error) {
data_.data.as_char = pgbuf_;
if (get_copy_res == -2) {
- StringBuilderAppend(&error_builder_, "[libpq] Fetch header failed: %s",
- PQerrorMessage(conn_));
- return EIO;
+ SetError(&error_, "[libpq] Fetch header failed: %s", PQerrorMessage(conn_));
+ status_ = ADBC_STATUS_IO;
+ return AdbcStatusCodeToErrno(status_);
}
int na_res = copy_reader_->ReadHeader(&data_, error);
if (na_res != NANOARROW_OK) {
- StringBuilderAppend(&error_builder_, "[libpq] ReadHeader failed: %s", error->message);
- return EIO;
+ SetError(&error_, "[libpq] ReadHeader failed: %s", error->message);
+ status_ = ADBC_STATUS_IO;
+ return AdbcStatusCodeToErrno(status_);
}
return NANOARROW_OK;
@@ -553,9 +565,9 @@ int TupleReader::AppendRowAndFetchNext(struct ArrowError* error) {
// call to PQgetCopyData())
int na_res = copy_reader_->ReadRecord(&data_, error);
if (na_res != NANOARROW_OK && na_res != ENODATA) {
- StringBuilderAppend(&error_builder_,
- "[libpq] ReadRecord failed at row %" PRId64 ": %s", row_id_,
- error->message);
+ SetError(&error_, "[libpq] ReadRecord failed at row %" PRId64 ": %s", row_id_,
+ error->message);
+ status_ = ADBC_STATUS_IO;
return na_res;
}
@@ -569,10 +581,10 @@ int TupleReader::AppendRowAndFetchNext(struct ArrowError* error) {
data_.data.as_char = pgbuf_;
if (get_copy_res == -2) {
- StringBuilderAppend(&error_builder_,
- "[libpq] PQgetCopyData failed at row %" PRId64 ": %s", row_id_,
- PQerrorMessage(conn_));
- return EIO;
+ SetError(&error_, "[libpq] PQgetCopyData failed at row %" PRId64 ": %s", row_id_,
+ PQerrorMessage(conn_));
+ status_ = ADBC_STATUS_IO;
+ return AdbcStatusCodeToErrno(status_);
} else if (get_copy_res == -1) {
// Returned when COPY has finished successfully
return ENODATA;
@@ -594,8 +606,8 @@ int TupleReader::BuildOutput(struct ArrowArray* out, struct ArrowError* error) {
int na_res = copy_reader_->GetArray(out, error);
if (na_res != NANOARROW_OK) {
- StringBuilderAppend(&error_builder_, "[libpq] Failed to build result array: %s",
- error->message);
+ SetError(&error_, "[libpq] Failed to build result array: %s", error->message);
+ status_ = ADBC_STATUS_INTERNAL;
return na_res;
}
@@ -641,16 +653,22 @@ int TupleReader::GetNext(struct ArrowArray* out) {
// Check the server-side response
result_ = PQgetResult(conn_);
- const int pq_status = PQresultStatus(result_);
+ const ExecStatusType pq_status = PQresultStatus(result_);
if (pq_status != PGRES_COMMAND_OK) {
- StringBuilderAppend(&error_builder_, "[libpq] Query failed [%d]: %s", pq_status,
- PQresultErrorMessage(result_));
+ const char* sqlstate = PQresultErrorField(result_, PG_DIAG_SQLSTATE);
+ SetError(&error_, result_, "[libpq] Query failed [%s]: %s", PQresStatus(pq_status),
+ PQresultErrorMessage(result_));
if (tmp.release != nullptr) {
tmp.release(&tmp);
}
- return EIO;
+ if (sqlstate != nullptr && std::strcmp(sqlstate, "57014") == 0) {
+ status_ = ADBC_STATUS_CANCELLED;
+ } else {
+ status_ = ADBC_STATUS_IO;
+ }
+ return AdbcStatusCodeToErrno(status_);
}
ArrowArrayMove(&tmp, out);
@@ -658,7 +676,11 @@ int TupleReader::GetNext(struct ArrowArray* out) {
}
void TupleReader::Release() {
- StringBuilderReset(&error_builder_);
+ if (error_.release) {
+ error_.release(&error_);
+ }
+ error_ = ADBC_ERROR_INIT;
+ status_ = ADBC_STATUS_OK;
if (result_) {
PQclear(result_);
@@ -686,6 +708,19 @@ void TupleReader::ExportTo(struct ArrowArrayStream* stream) {
stream->private_data = this;
}
+const struct AdbcError* TupleReader::ErrorFromArrayStream(struct ArrowArrayStream* stream,
+ AdbcStatusCode* status) {
+ if (!stream->private_data || stream->release != &ReleaseTrampoline) {
+ return nullptr;
+ }
+
+ TupleReader* reader = static_cast(stream->private_data);
+ if (status) {
+ *status = reader->status_;
+ }
+ return &reader->error_;
+}
+
int TupleReader::GetSchemaTrampoline(struct ArrowArrayStream* self,
struct ArrowSchema* out) {
if (!self || !self->private_data) return EINVAL;
@@ -767,11 +802,42 @@ AdbcStatusCode PostgresStatement::Bind(struct ArrowArrayStream* stream,
return ADBC_STATUS_OK;
}
+AdbcStatusCode PostgresStatement::Cancel(struct AdbcError* error) {
+ // Ultimately the same underlying PGconn
+ return connection_->Cancel(error);
+}
+
AdbcStatusCode PostgresStatement::CreateBulkTable(
const struct ArrowSchema& source_schema,
const std::vector& source_schema_fields,
struct AdbcError* error) {
std::string create = "CREATE TABLE ";
+ switch (ingest_.mode) {
+ case IngestMode::kCreate:
+ // Nothing to do
+ break;
+ case IngestMode::kAppend:
+ return ADBC_STATUS_OK;
+ case IngestMode::kReplace: {
+ std::string drop = "DROP TABLE IF EXISTS " + ingest_.target;
+ PGresult* result = PQexecParams(connection_->conn(), drop.c_str(), /*nParams=*/0,
+ /*paramTypes=*/nullptr, /*paramValues=*/nullptr,
+ /*paramLengths=*/nullptr, /*paramFormats=*/nullptr,
+ /*resultFormat=*/1 /*(binary)*/);
+ if (PQresultStatus(result) != PGRES_COMMAND_OK) {
+ AdbcStatusCode code =
+ SetError(error, result, "[libpq] Failed to drop table: %s\nQuery was: %s",
+ PQerrorMessage(connection_->conn()), drop.c_str());
+ PQclear(result);
+ return code;
+ }
+ PQclear(result);
+ break;
+ }
+ case IngestMode::kCreateAppend:
+ create += "IF NOT EXISTS ";
+ break;
+ }
create += ingest_.target;
create += " (";
@@ -830,10 +896,11 @@ AdbcStatusCode PostgresStatement::CreateBulkTable(
/*paramLengths=*/nullptr, /*paramFormats=*/nullptr,
/*resultFormat=*/1 /*(binary)*/);
if (PQresultStatus(result) != PGRES_COMMAND_OK) {
- SetError(error, "[libpq] Failed to create table: %s\nQuery was: %s",
- PQerrorMessage(connection_->conn()), create.c_str());
+ AdbcStatusCode code =
+ SetError(error, result, "[libpq] Failed to create table: %s\nQuery was: %s",
+ PQerrorMessage(connection_->conn()), create.c_str());
PQclear(result);
- return ADBC_STATUS_IO;
+ return code;
}
PQclear(result);
return ADBC_STATUS_OK;
@@ -894,50 +961,12 @@ AdbcStatusCode PostgresStatement::ExecuteQuery(struct ArrowArrayStream* stream,
// 1. Prepare the query to get the schema
{
- // TODO: we should pipeline here and assume this will succeed
- PGresult* result = PQprepare(connection_->conn(), /*stmtName=*/"", query_.c_str(),
- /*nParams=*/0, nullptr);
- if (PQresultStatus(result) != PGRES_COMMAND_OK) {
- SetError(error,
- "[libpq] Failed to execute query: could not infer schema: failed to "
- "prepare query: %s\nQuery was:%s",
- PQerrorMessage(connection_->conn()), query_.c_str());
- PQclear(result);
- return ADBC_STATUS_IO;
- }
- PQclear(result);
- result = PQdescribePrepared(connection_->conn(), /*stmtName=*/"");
- if (PQresultStatus(result) != PGRES_COMMAND_OK) {
- SetError(error,
- "[libpq] Failed to execute query: could not infer schema: failed to "
- "describe prepared statement: %s\nQuery was:%s",
- PQerrorMessage(connection_->conn()), query_.c_str());
- PQclear(result);
- return ADBC_STATUS_IO;
- }
-
- // Resolve the information from the PGresult into a PostgresType
- PostgresType root_type;
- AdbcStatusCode status =
- ResolvePostgresType(*type_resolver_, result, &root_type, error);
- PQclear(result);
- if (status != ADBC_STATUS_OK) return status;
-
- // Initialize the copy reader and infer the output schema (i.e., error for
- // unsupported types before issuing the COPY query)
- reader_.copy_reader_.reset(new PostgresCopyStreamReader());
- reader_.copy_reader_->Init(root_type);
- struct ArrowError na_error;
- int na_res = reader_.copy_reader_->InferOutputSchema(&na_error);
- if (na_res != NANOARROW_OK) {
- SetError(error, "[libpq] Failed to infer output schema: %s", na_error.message);
- return na_res;
- }
+ RAISE_ADBC(SetupReader(error));
// If the caller did not request a result set or if there are no
// inferred output columns (e.g. a CREATE or UPDATE), then don't
// use COPY (which would fail anyways)
- if (!stream || root_type.n_children() == 0) {
+ if (!stream || reader_.copy_reader_->pg_type().n_children() == 0) {
RAISE_ADBC(ExecuteUpdateQuery(rows_affected, error));
if (stream) {
struct ArrowSchema schema;
@@ -951,7 +980,8 @@ AdbcStatusCode PostgresStatement::ExecuteQuery(struct ArrowArrayStream* stream,
// This resolves the reader specific to each PostgresType -> ArrowSchema
// conversion. It is unlikely that this will fail given that we have just
// inferred these conversions ourselves.
- na_res = reader_.copy_reader_->InitFieldReaders(&na_error);
+ struct ArrowError na_error;
+ int na_res = reader_.copy_reader_->InitFieldReaders(&na_error);
if (na_res != NANOARROW_OK) {
SetError(error, "[libpq] Failed to initialize field readers: %s", na_error.message);
return na_res;
@@ -966,11 +996,12 @@ AdbcStatusCode PostgresStatement::ExecuteQuery(struct ArrowArrayStream* stream,
/*paramTypes=*/nullptr, /*paramValues=*/nullptr,
/*paramLengths=*/nullptr, /*paramFormats=*/nullptr, kPgBinaryFormat);
if (PQresultStatus(reader_.result_) != PGRES_COPY_OUT) {
- SetError(error,
- "[libpq] Failed to execute query: could not begin COPY: %s\nQuery was: %s",
- PQerrorMessage(connection_->conn()), copy_query.c_str());
+ AdbcStatusCode code = SetError(
+ error, reader_.result_,
+ "[libpq] Failed to execute query: could not begin COPY: %s\nQuery was: %s",
+ PQerrorMessage(connection_->conn()), copy_query.c_str());
ClearResult();
- return ADBC_STATUS_IO;
+ return code;
}
// Result is read from the connection, not the result, but we won't clear it here
}
@@ -980,6 +1011,23 @@ AdbcStatusCode PostgresStatement::ExecuteQuery(struct ArrowArrayStream* stream,
return ADBC_STATUS_OK;
}
+AdbcStatusCode PostgresStatement::ExecuteSchema(struct ArrowSchema* schema,
+ struct AdbcError* error) {
+ ClearResult();
+ if (query_.empty()) {
+ SetError(error, "%s", "[libpq] Must SetSqlQuery before ExecuteQuery");
+ return ADBC_STATUS_INVALID_STATE;
+ } else if (bind_.release) {
+ // TODO: if we have parameters, bind them (since they can affect the output schema)
+ SetError(error, "[libpq] ExecuteSchema with parameters is not implemented");
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+ }
+
+ RAISE_ADBC(SetupReader(error));
+ CHECK_NA(INTERNAL, reader_.copy_reader_->GetSchema(schema), error);
+ return ADBC_STATUS_OK;
+}
+
AdbcStatusCode PostgresStatement::ExecuteUpdateBulk(int64_t* rows_affected,
struct AdbcError* error) {
if (!bind_.release) {
@@ -991,12 +1039,8 @@ AdbcStatusCode PostgresStatement::ExecuteUpdateBulk(int64_t* rows_affected,
std::memset(&bind_, 0, sizeof(bind_));
RAISE_ADBC(bind_stream.Begin(
[&]() -> AdbcStatusCode {
- if (!ingest_.append) {
- // CREATE TABLE
- return CreateBulkTable(bind_stream.bind_schema.value,
- bind_stream.bind_schema_fields, error);
- }
- return ADBC_STATUS_OK;
+ return CreateBulkTable(bind_stream.bind_schema.value,
+ bind_stream.bind_schema_fields, error);
},
error));
RAISE_ADBC(bind_stream.SetParamTypes(*type_resolver_, error));
@@ -1024,17 +1068,77 @@ AdbcStatusCode PostgresStatement::ExecuteUpdateQuery(int64_t* rows_affected,
PQexecPrepared(connection_->conn(), /*stmtName=*/"", /*nParams=*/0,
/*paramValues=*/nullptr, /*paramLengths=*/nullptr,
/*paramFormats=*/nullptr, /*resultFormat=*/kPgBinaryFormat);
- if (PQresultStatus(result) != PGRES_COMMAND_OK) {
- SetError(error, "[libpq] Failed to execute query: %s\nQuery was:%s",
- PQerrorMessage(connection_->conn()), query_.c_str());
+ ExecStatusType status = PQresultStatus(result);
+ if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
+ AdbcStatusCode code =
+ SetError(error, result, "[libpq] Failed to execute query: %s\nQuery was:%s",
+ PQerrorMessage(connection_->conn()), query_.c_str());
PQclear(result);
- return ADBC_STATUS_IO;
+ return code;
}
if (rows_affected) *rows_affected = PQntuples(reader_.result_);
PQclear(result);
return ADBC_STATUS_OK;
}
+AdbcStatusCode PostgresStatement::GetOption(const char* key, char* value, size_t* length,
+ struct AdbcError* error) {
+ std::string result;
+ if (std::strcmp(key, ADBC_INGEST_OPTION_TARGET_TABLE) == 0) {
+ result = ingest_.target;
+ } else if (std::strcmp(key, ADBC_INGEST_OPTION_MODE) == 0) {
+ switch (ingest_.mode) {
+ case IngestMode::kCreate:
+ result = ADBC_INGEST_OPTION_MODE_CREATE;
+ break;
+ case IngestMode::kAppend:
+ result = ADBC_INGEST_OPTION_MODE_APPEND;
+ break;
+ case IngestMode::kReplace:
+ result = ADBC_INGEST_OPTION_MODE_REPLACE;
+ break;
+ case IngestMode::kCreateAppend:
+ result = ADBC_INGEST_OPTION_MODE_CREATE_APPEND;
+ break;
+ }
+ } else if (std::strcmp(key, ADBC_POSTGRESQL_OPTION_BATCH_SIZE_HINT_BYTES) == 0) {
+ result = std::to_string(reader_.batch_size_hint_bytes_);
+ } else {
+ SetError(error, "[libpq] Unknown statement option '%s'", key);
+ return ADBC_STATUS_NOT_FOUND;
+ }
+
+ if (result.size() + 1 <= *length) {
+ std::memcpy(value, result.data(), result.size() + 1);
+ }
+ *length = static_cast(result.size() + 1);
+ return ADBC_STATUS_OK;
+}
+
+AdbcStatusCode PostgresStatement::GetOptionBytes(const char* key, uint8_t* value,
+ size_t* length,
+ struct AdbcError* error) {
+ SetError(error, "[libpq] Unknown statement option '%s'", key);
+ return ADBC_STATUS_NOT_FOUND;
+}
+
+AdbcStatusCode PostgresStatement::GetOptionDouble(const char* key, double* value,
+ struct AdbcError* error) {
+ SetError(error, "[libpq] Unknown statement option '%s'", key);
+ return ADBC_STATUS_NOT_FOUND;
+}
+
+AdbcStatusCode PostgresStatement::GetOptionInt(const char* key, int64_t* value,
+ struct AdbcError* error) {
+ std::string result;
+ if (std::strcmp(key, ADBC_POSTGRESQL_OPTION_BATCH_SIZE_HINT_BYTES) == 0) {
+ *value = reader_.batch_size_hint_bytes_;
+ return ADBC_STATUS_OK;
+ }
+ SetError(error, "[libpq] Unknown statement option '%s'", key);
+ return ADBC_STATUS_NOT_FOUND;
+}
+
AdbcStatusCode PostgresStatement::GetParameterSchema(struct ArrowSchema* schema,
struct AdbcError* error) {
return ADBC_STATUS_NOT_IMPLEMENTED;
@@ -1073,16 +1177,22 @@ AdbcStatusCode PostgresStatement::SetOption(const char* key, const char* value,
if (std::strcmp(key, ADBC_INGEST_OPTION_TARGET_TABLE) == 0) {
query_.clear();
ingest_.target = value;
+ prepared_ = false;
} else if (std::strcmp(key, ADBC_INGEST_OPTION_MODE) == 0) {
if (std::strcmp(value, ADBC_INGEST_OPTION_MODE_CREATE) == 0) {
- ingest_.append = false;
+ ingest_.mode = IngestMode::kCreate;
} else if (std::strcmp(value, ADBC_INGEST_OPTION_MODE_APPEND) == 0) {
- ingest_.append = true;
+ ingest_.mode = IngestMode::kAppend;
+ } else if (std::strcmp(value, ADBC_INGEST_OPTION_MODE_REPLACE) == 0) {
+ ingest_.mode = IngestMode::kReplace;
+ } else if (std::strcmp(value, ADBC_INGEST_OPTION_MODE_CREATE_APPEND) == 0) {
+ ingest_.mode = IngestMode::kCreateAppend;
} else {
SetError(error, "[libpq] Invalid value '%s' for option '%s'", value, key);
return ADBC_STATUS_INVALID_ARGUMENT;
}
- } else if (std::strcmp(value, ADBC_POSTGRESQL_OPTION_BATCH_SIZE_HINT_BYTES)) {
+ prepared_ = false;
+ } else if (std::strcmp(key, ADBC_POSTGRESQL_OPTION_BATCH_SIZE_HINT_BYTES) == 0) {
int64_t int_value = std::atol(value);
if (int_value <= 0) {
SetError(error, "[libpq] Invalid value '%s' for option '%s'", value, key);
@@ -1091,12 +1201,84 @@ AdbcStatusCode PostgresStatement::SetOption(const char* key, const char* value,
this->reader_.batch_size_hint_bytes_ = int_value;
} else {
- SetError(error, "[libq] Unknown statement option '%s'", key);
+ SetError(error, "[libpq] Unknown statement option '%s'", key);
return ADBC_STATUS_NOT_IMPLEMENTED;
}
return ADBC_STATUS_OK;
}
+AdbcStatusCode PostgresStatement::SetOptionBytes(const char* key, const uint8_t* value,
+ size_t length, struct AdbcError* error) {
+ SetError(error, "%s%s", "[libpq] Unknown statement option ", key);
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode PostgresStatement::SetOptionDouble(const char* key, double value,
+ struct AdbcError* error) {
+ SetError(error, "%s%s", "[libpq] Unknown statement option ", key);
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode PostgresStatement::SetOptionInt(const char* key, int64_t value,
+ struct AdbcError* error) {
+ if (std::strcmp(key, ADBC_POSTGRESQL_OPTION_BATCH_SIZE_HINT_BYTES) == 0) {
+ if (value <= 0) {
+ SetError(error, "[libpq] Invalid value '%" PRIi64 "' for option '%s'", value, key);
+ return ADBC_STATUS_INVALID_ARGUMENT;
+ }
+
+ this->reader_.batch_size_hint_bytes_ = value;
+ return ADBC_STATUS_OK;
+ }
+ SetError(error, "[libpq] Unknown statement option '%s'", key);
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode PostgresStatement::SetupReader(struct AdbcError* error) {
+ // TODO: we should pipeline here and assume this will succeed
+ PGresult* result = PQprepare(connection_->conn(), /*stmtName=*/"", query_.c_str(),
+ /*nParams=*/0, nullptr);
+ if (PQresultStatus(result) != PGRES_COMMAND_OK) {
+ AdbcStatusCode code =
+ SetError(error, result,
+ "[libpq] Failed to execute query: could not infer schema: failed to "
+ "prepare query: %s\nQuery was:%s",
+ PQerrorMessage(connection_->conn()), query_.c_str());
+ PQclear(result);
+ return code;
+ }
+ PQclear(result);
+ result = PQdescribePrepared(connection_->conn(), /*stmtName=*/"");
+ if (PQresultStatus(result) != PGRES_COMMAND_OK) {
+ AdbcStatusCode code =
+ SetError(error, result,
+ "[libpq] Failed to execute query: could not infer schema: failed to "
+ "describe prepared statement: %s\nQuery was:%s",
+ PQerrorMessage(connection_->conn()), query_.c_str());
+ PQclear(result);
+ return code;
+ }
+
+ // Resolve the information from the PGresult into a PostgresType
+ PostgresType root_type;
+ AdbcStatusCode status = ResolvePostgresType(*type_resolver_, result, &root_type, error);
+ PQclear(result);
+ if (status != ADBC_STATUS_OK) return status;
+
+ // Initialize the copy reader and infer the output schema (i.e., error for
+ // unsupported types before issuing the COPY query)
+ reader_.copy_reader_.reset(new PostgresCopyStreamReader());
+ reader_.copy_reader_->Init(root_type);
+ struct ArrowError na_error;
+ int na_res = reader_.copy_reader_->InferOutputSchema(&na_error);
+ if (na_res != NANOARROW_OK) {
+ SetError(error, "[libpq] Failed to infer output schema: (%d) %s: %s", na_res,
+ std::strerror(na_res), na_error.message);
+ return ADBC_STATUS_INTERNAL;
+ }
+ return ADBC_STATUS_OK;
+}
+
void PostgresStatement::ClearResult() {
// TODO: we may want to synchronize here for safety
reader_.Release();
diff --git a/c/driver/postgresql/statement.h b/c/driver/postgresql/statement.h
index 0326e80e08..59d3032cf4 100644
--- a/c/driver/postgresql/statement.h
+++ b/c/driver/postgresql/statement.h
@@ -41,30 +41,28 @@ class PostgresStatement;
class TupleReader final {
public:
TupleReader(PGconn* conn)
- : conn_(conn),
+ : status_(ADBC_STATUS_OK),
+ error_(ADBC_ERROR_INIT),
+ conn_(conn),
result_(nullptr),
pgbuf_(nullptr),
copy_reader_(nullptr),
row_id_(-1),
batch_size_hint_bytes_(16777216),
is_finished_(false) {
- StringBuilderInit(&error_builder_, 0);
data_.data.as_char = nullptr;
data_.size_bytes = 0;
}
int GetSchema(struct ArrowSchema* out);
int GetNext(struct ArrowArray* out);
- const char* last_error() const {
- if (error_builder_.size > 0) {
- return error_builder_.buffer;
- } else {
- return nullptr;
- }
- }
+ const char* last_error() const { return error_.message; }
void Release();
void ExportTo(struct ArrowArrayStream* stream);
+ static const struct AdbcError* ErrorFromArrayStream(struct ArrowArrayStream* stream,
+ AdbcStatusCode* status);
+
private:
friend class PostgresStatement;
@@ -77,11 +75,12 @@ class TupleReader final {
static const char* GetLastErrorTrampoline(struct ArrowArrayStream* self);
static void ReleaseTrampoline(struct ArrowArrayStream* self);
+ AdbcStatusCode status_;
+ struct AdbcError error_;
PGconn* conn_;
PGresult* result_;
char* pgbuf_;
struct ArrowBufferView data_;
- struct StringBuilder error_builder_;
std::unique_ptr copy_reader_;
int64_t row_id_;
int64_t batch_size_hint_bytes_;
@@ -101,13 +100,25 @@ class PostgresStatement {
AdbcStatusCode Bind(struct ArrowArray* values, struct ArrowSchema* schema,
struct AdbcError* error);
AdbcStatusCode Bind(struct ArrowArrayStream* stream, struct AdbcError* error);
+ AdbcStatusCode Cancel(struct AdbcError* error);
AdbcStatusCode ExecuteQuery(struct ArrowArrayStream* stream, int64_t* rows_affected,
struct AdbcError* error);
+ AdbcStatusCode ExecuteSchema(struct ArrowSchema* schema, struct AdbcError* error);
+ AdbcStatusCode GetOption(const char* key, char* value, size_t* length,
+ struct AdbcError* error);
+ AdbcStatusCode GetOptionBytes(const char* key, uint8_t* value, size_t* length,
+ struct AdbcError* error);
+ AdbcStatusCode GetOptionDouble(const char* key, double* value, struct AdbcError* error);
+ AdbcStatusCode GetOptionInt(const char* key, int64_t* value, struct AdbcError* error);
AdbcStatusCode GetParameterSchema(struct ArrowSchema* schema, struct AdbcError* error);
AdbcStatusCode New(struct AdbcConnection* connection, struct AdbcError* error);
AdbcStatusCode Prepare(struct AdbcError* error);
AdbcStatusCode Release(struct AdbcError* error);
AdbcStatusCode SetOption(const char* key, const char* value, struct AdbcError* error);
+ AdbcStatusCode SetOptionBytes(const char* key, const uint8_t* value, size_t length,
+ struct AdbcError* error);
+ AdbcStatusCode SetOptionDouble(const char* key, double value, struct AdbcError* error);
+ AdbcStatusCode SetOptionInt(const char* key, int64_t value, struct AdbcError* error);
AdbcStatusCode SetSqlQuery(const char* query, struct AdbcError* error);
// ---------------------------------------------------------------------
@@ -123,6 +134,7 @@ class PostgresStatement {
AdbcStatusCode ExecutePreparedStatement(struct ArrowArrayStream* stream,
int64_t* rows_affected,
struct AdbcError* error);
+ AdbcStatusCode SetupReader(struct AdbcError* error);
private:
std::shared_ptr type_resolver_;
@@ -134,9 +146,16 @@ class PostgresStatement {
struct ArrowArrayStream bind_;
// Bulk ingest state
+ enum class IngestMode {
+ kCreate,
+ kAppend,
+ kReplace,
+ kCreateAppend,
+ };
+
struct {
std::string target;
- bool append = false;
+ IngestMode mode = IngestMode::kCreate;
} ingest_;
TupleReader reader_;
diff --git a/c/driver/snowflake/snowflake_test.cc b/c/driver/snowflake/snowflake_test.cc
index ed2f5de07c..8c3cd72c8b 100644
--- a/c/driver/snowflake/snowflake_test.cc
+++ b/c/driver/snowflake/snowflake_test.cc
@@ -106,11 +106,13 @@ class SnowflakeQuirks : public adbc_validation::DriverQuirks {
}
std::string BindParameter(int index) const override { return "?"; }
+ bool supports_bulk_ingest(const char* /*mode*/) const override { return true; }
bool supports_concurrent_statements() const override { return true; }
bool supports_transactions() const override { return true; }
bool supports_get_sql_info() const override { return false; }
bool supports_get_objects() const override { return true; }
- bool supports_bulk_ingest() const override { return true; }
+ bool supports_metadata_current_catalog() const override { return false; }
+ bool supports_metadata_current_db_schema() const override { return false; }
bool supports_partitioned_data() const override { return false; }
bool supports_dynamic_parameter_binding() const override { return false; }
bool ddl_implicit_commit_txn() const override { return true; }
@@ -156,6 +158,10 @@ class SnowflakeConnectionTest : public ::testing::Test,
}
}
+ // Supported, but we don't validate the values
+ void TestMetadataCurrentCatalog() { GTEST_SKIP(); }
+ void TestMetadataCurrentDbSchema() { GTEST_SKIP(); }
+
protected:
SnowflakeQuirks quirks_;
};
diff --git a/c/driver/sqlite/sqlite.c b/c/driver/sqlite/sqlite.c
index 87e20c998d..7b4072c2ba 100644
--- a/c/driver/sqlite/sqlite.c
+++ b/c/driver/sqlite/sqlite.c
@@ -86,6 +86,26 @@ AdbcStatusCode SqliteDatabaseSetOption(struct AdbcDatabase* database, const char
return ADBC_STATUS_NOT_IMPLEMENTED;
}
+AdbcStatusCode SqliteDatabaseSetOptionBytes(struct AdbcDatabase* database,
+ const char* key, const uint8_t* value,
+ size_t length, struct AdbcError* error) {
+ CHECK_DB_INIT(database, error);
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode SqliteDatabaseSetOptionDouble(struct AdbcDatabase* database,
+ const char* key, double value,
+ struct AdbcError* error) {
+ CHECK_DB_INIT(database, error);
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode SqliteDatabaseSetOptionInt(struct AdbcDatabase* database, const char* key,
+ int64_t value, struct AdbcError* error) {
+ CHECK_DB_INIT(database, error);
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
int OpenDatabase(const char* maybe_uri, sqlite3** db, struct AdbcError* error) {
const char* uri = maybe_uri ? maybe_uri : kDefaultUri;
int rc = sqlite3_open_v2(uri, db,
@@ -120,6 +140,33 @@ AdbcStatusCode ExecuteQuery(struct SqliteConnection* conn, const char* query,
return ADBC_STATUS_OK;
}
+AdbcStatusCode SqliteDatabaseGetOption(struct AdbcDatabase* database, const char* key,
+ char* value, size_t* length,
+ struct AdbcError* error) {
+ CHECK_DB_INIT(database, error);
+ return ADBC_STATUS_NOT_FOUND;
+}
+
+AdbcStatusCode SqliteDatabaseGetOptionBytes(struct AdbcDatabase* database,
+ const char* key, uint8_t* value,
+ size_t* length, struct AdbcError* error) {
+ CHECK_DB_INIT(database, error);
+ return ADBC_STATUS_NOT_FOUND;
+}
+
+AdbcStatusCode SqliteDatabaseGetOptionDouble(struct AdbcDatabase* database,
+ const char* key, double* value,
+ struct AdbcError* error) {
+ CHECK_DB_INIT(database, error);
+ return ADBC_STATUS_NOT_FOUND;
+}
+
+AdbcStatusCode SqliteDatabaseGetOptionInt(struct AdbcDatabase* database, const char* key,
+ int64_t* value, struct AdbcError* error) {
+ CHECK_DB_INIT(database, error);
+ return ADBC_STATUS_NOT_FOUND;
+}
+
AdbcStatusCode SqliteDatabaseInit(struct AdbcDatabase* database,
struct AdbcError* error) {
CHECK_DB_INIT(database, error);
@@ -204,6 +251,27 @@ AdbcStatusCode SqliteConnectionSetOption(struct AdbcConnection* connection,
return ADBC_STATUS_NOT_IMPLEMENTED;
}
+AdbcStatusCode SqliteConnectionSetOptionBytes(struct AdbcConnection* connection,
+ const char* key, const uint8_t* value,
+ size_t length, struct AdbcError* error) {
+ CHECK_DB_INIT(connection, error);
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode SqliteConnectionSetOptionDouble(struct AdbcConnection* connection,
+ const char* key, double value,
+ struct AdbcError* error) {
+ CHECK_DB_INIT(connection, error);
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode SqliteConnectionSetOptionInt(struct AdbcConnection* connection,
+ const char* key, int64_t value,
+ struct AdbcError* error) {
+ CHECK_DB_INIT(connection, error);
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
AdbcStatusCode SqliteConnectionInit(struct AdbcConnection* connection,
struct AdbcDatabase* database,
struct AdbcError* error) {
@@ -282,7 +350,8 @@ AdbcStatusCode SqliteConnectionGetInfoImpl(const uint32_t* info_codes,
} // NOLINT(whitespace/indent)
AdbcStatusCode SqliteConnectionGetInfo(struct AdbcConnection* connection,
- uint32_t* info_codes, size_t info_codes_length,
+ const uint32_t* info_codes,
+ size_t info_codes_length,
struct ArrowArrayStream* out,
struct AdbcError* error) {
CHECK_CONN_INIT(connection, error);
@@ -754,6 +823,34 @@ AdbcStatusCode SqliteConnectionGetObjects(struct AdbcConnection* connection, int
return BatchToArrayStream(&array, &schema, out, error);
}
+AdbcStatusCode SqliteConnectionGetOption(struct AdbcConnection* connection,
+ const char* key, char* value, size_t* length,
+ struct AdbcError* error) {
+ CHECK_DB_INIT(connection, error);
+ return ADBC_STATUS_NOT_FOUND;
+}
+
+AdbcStatusCode SqliteConnectionGetOptionBytes(struct AdbcConnection* connection,
+ const char* key, uint8_t* value,
+ size_t* length, struct AdbcError* error) {
+ CHECK_DB_INIT(connection, error);
+ return ADBC_STATUS_NOT_FOUND;
+}
+
+AdbcStatusCode SqliteConnectionGetOptionDouble(struct AdbcConnection* connection,
+ const char* key, double* value,
+ struct AdbcError* error) {
+ CHECK_DB_INIT(connection, error);
+ return ADBC_STATUS_NOT_FOUND;
+}
+
+AdbcStatusCode SqliteConnectionGetOptionInt(struct AdbcConnection* connection,
+ const char* key, int64_t* value,
+ struct AdbcError* error) {
+ CHECK_DB_INIT(connection, error);
+ return ADBC_STATUS_NOT_FOUND;
+}
+
AdbcStatusCode SqliteConnectionGetTableSchema(struct AdbcConnection* connection,
const char* catalog, const char* db_schema,
const char* table_name,
@@ -1286,6 +1383,34 @@ AdbcStatusCode SqliteStatementBindStream(struct AdbcStatement* statement,
return AdbcSqliteBinderSetArrayStream(&stmt->binder, stream, error);
}
+AdbcStatusCode SqliteStatementGetOption(struct AdbcStatement* statement, const char* key,
+ char* value, size_t* length,
+ struct AdbcError* error) {
+ CHECK_DB_INIT(statement, error);
+ return ADBC_STATUS_NOT_FOUND;
+}
+
+AdbcStatusCode SqliteStatementGetOptionBytes(struct AdbcStatement* statement,
+ const char* key, uint8_t* value,
+ size_t* length, struct AdbcError* error) {
+ CHECK_DB_INIT(statement, error);
+ return ADBC_STATUS_NOT_FOUND;
+}
+
+AdbcStatusCode SqliteStatementGetOptionDouble(struct AdbcStatement* statement,
+ const char* key, double* value,
+ struct AdbcError* error) {
+ CHECK_DB_INIT(statement, error);
+ return ADBC_STATUS_NOT_FOUND;
+}
+
+AdbcStatusCode SqliteStatementGetOptionInt(struct AdbcStatement* statement,
+ const char* key, int64_t* value,
+ struct AdbcError* error) {
+ CHECK_DB_INIT(statement, error);
+ return ADBC_STATUS_NOT_FOUND;
+}
+
AdbcStatusCode SqliteStatementGetParameterSchema(struct AdbcStatement* statement,
struct ArrowSchema* schema,
struct AdbcError* error) {
@@ -1375,6 +1500,27 @@ AdbcStatusCode SqliteStatementSetOption(struct AdbcStatement* statement, const c
return ADBC_STATUS_NOT_IMPLEMENTED;
}
+AdbcStatusCode SqliteStatementSetOptionBytes(struct AdbcStatement* statement,
+ const char* key, const uint8_t* value,
+ size_t length, struct AdbcError* error) {
+ CHECK_DB_INIT(statement, error);
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode SqliteStatementSetOptionDouble(struct AdbcStatement* statement,
+ const char* key, double value,
+ struct AdbcError* error) {
+ CHECK_DB_INIT(statement, error);
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode SqliteStatementSetOptionInt(struct AdbcStatement* statement,
+ const char* key, int64_t value,
+ struct AdbcError* error) {
+ CHECK_DB_INIT(statement, error);
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
AdbcStatusCode SqliteStatementExecutePartitions(struct AdbcStatement* statement,
struct ArrowSchema* schema,
struct AdbcPartitions* partitions,
@@ -1393,7 +1539,7 @@ AdbcStatusCode SqliteDriverInit(int version, void* raw_driver, struct AdbcError*
}
struct AdbcDriver* driver = (struct AdbcDriver*)raw_driver;
- memset(driver, 0, sizeof(*driver));
+ memset(driver, 0, ADBC_DRIVER_1_0_0_SIZE);
driver->DatabaseInit = SqliteDatabaseInit;
driver->DatabaseNew = SqliteDatabaseNew;
driver->DatabaseRelease = SqliteDatabaseRelease;
@@ -1425,24 +1571,91 @@ AdbcStatusCode SqliteDriverInit(int version, void* raw_driver, struct AdbcError*
// Public names
-AdbcStatusCode AdbcDatabaseNew(struct AdbcDatabase* database, struct AdbcError* error) {
- return SqliteDatabaseNew(database, error);
+AdbcStatusCode AdbcDatabaseGetOption(struct AdbcDatabase* database, const char* key,
+ char* value, size_t* length,
+ struct AdbcError* error) {
+ return SqliteDatabaseGetOption(database, key, value, length, error);
}
-AdbcStatusCode AdbcDatabaseSetOption(struct AdbcDatabase* database, const char* key,
- const char* value, struct AdbcError* error) {
- return SqliteDatabaseSetOption(database, key, value, error);
+AdbcStatusCode AdbcDatabaseGetOptionBytes(struct AdbcDatabase* database, const char* key,
+ uint8_t* value, size_t* length,
+ struct AdbcError* error) {
+ return SqliteDatabaseGetOptionBytes(database, key, value, length, error);
+}
+
+AdbcStatusCode AdbcDatabaseGetOptionInt(struct AdbcDatabase* database, const char* key,
+ int64_t* value, struct AdbcError* error) {
+ return SqliteDatabaseGetOptionInt(database, key, value, error);
+}
+
+AdbcStatusCode AdbcDatabaseGetOptionDouble(struct AdbcDatabase* database, const char* key,
+ double* value, struct AdbcError* error) {
+ return SqliteDatabaseGetOptionDouble(database, key, value, error);
}
AdbcStatusCode AdbcDatabaseInit(struct AdbcDatabase* database, struct AdbcError* error) {
return SqliteDatabaseInit(database, error);
}
+AdbcStatusCode AdbcDatabaseNew(struct AdbcDatabase* database, struct AdbcError* error) {
+ return SqliteDatabaseNew(database, error);
+}
+
AdbcStatusCode AdbcDatabaseRelease(struct AdbcDatabase* database,
struct AdbcError* error) {
return SqliteDatabaseRelease(database, error);
}
+AdbcStatusCode AdbcDatabaseSetOption(struct AdbcDatabase* database, const char* key,
+ const char* value, struct AdbcError* error) {
+ return SqliteDatabaseSetOption(database, key, value, error);
+}
+
+AdbcStatusCode AdbcDatabaseSetOptionBytes(struct AdbcDatabase* database, const char* key,
+ const uint8_t* value, size_t length,
+ struct AdbcError* error) {
+ return SqliteDatabaseSetOptionBytes(database, key, value, length, error);
+}
+
+AdbcStatusCode AdbcDatabaseSetOptionInt(struct AdbcDatabase* database, const char* key,
+ int64_t value, struct AdbcError* error) {
+ return SqliteDatabaseSetOptionInt(database, key, value, error);
+}
+
+AdbcStatusCode AdbcDatabaseSetOptionDouble(struct AdbcDatabase* database, const char* key,
+ double value, struct AdbcError* error) {
+ return SqliteDatabaseSetOptionDouble(database, key, value, error);
+}
+
+AdbcStatusCode AdbcConnectionCancel(struct AdbcConnection* connection,
+ struct AdbcError* error) {
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode AdbcConnectionGetOption(struct AdbcConnection* connection, const char* key,
+ char* value, size_t* length,
+ struct AdbcError* error) {
+ return SqliteConnectionGetOption(connection, key, value, length, error);
+}
+
+AdbcStatusCode AdbcConnectionGetOptionBytes(struct AdbcConnection* connection,
+ const char* key, uint8_t* value,
+ size_t* length, struct AdbcError* error) {
+ return SqliteConnectionGetOptionBytes(connection, key, value, length, error);
+}
+
+AdbcStatusCode AdbcConnectionGetOptionInt(struct AdbcConnection* connection,
+ const char* key, int64_t* value,
+ struct AdbcError* error) {
+ return SqliteConnectionGetOptionInt(connection, key, value, error);
+}
+
+AdbcStatusCode AdbcConnectionGetOptionDouble(struct AdbcConnection* connection,
+ const char* key, double* value,
+ struct AdbcError* error) {
+ return SqliteConnectionGetOptionDouble(connection, key, value, error);
+}
+
AdbcStatusCode AdbcConnectionNew(struct AdbcConnection* connection,
struct AdbcError* error) {
return SqliteConnectionNew(connection, error);
@@ -1453,6 +1666,24 @@ AdbcStatusCode AdbcConnectionSetOption(struct AdbcConnection* connection, const
return SqliteConnectionSetOption(connection, key, value, error);
}
+AdbcStatusCode AdbcConnectionSetOptionBytes(struct AdbcConnection* connection,
+ const char* key, const uint8_t* value,
+ size_t length, struct AdbcError* error) {
+ return SqliteConnectionSetOptionBytes(connection, key, value, length, error);
+}
+
+AdbcStatusCode AdbcConnectionSetOptionInt(struct AdbcConnection* connection,
+ const char* key, int64_t value,
+ struct AdbcError* error) {
+ return SqliteConnectionSetOptionInt(connection, key, value, error);
+}
+
+AdbcStatusCode AdbcConnectionSetOptionDouble(struct AdbcConnection* connection,
+ const char* key, double value,
+ struct AdbcError* error) {
+ return SqliteConnectionSetOptionDouble(connection, key, value, error);
+}
+
AdbcStatusCode AdbcConnectionInit(struct AdbcConnection* connection,
struct AdbcDatabase* database,
struct AdbcError* error) {
@@ -1465,7 +1696,7 @@ AdbcStatusCode AdbcConnectionRelease(struct AdbcConnection* connection,
}
AdbcStatusCode AdbcConnectionGetInfo(struct AdbcConnection* connection,
- uint32_t* info_codes, size_t info_codes_length,
+ const uint32_t* info_codes, size_t info_codes_length,
struct ArrowArrayStream* out,
struct AdbcError* error) {
return SqliteConnectionGetInfo(connection, info_codes, info_codes_length, out, error);
@@ -1481,6 +1712,20 @@ AdbcStatusCode AdbcConnectionGetObjects(struct AdbcConnection* connection, int d
table_type, column_name, out, error);
}
+AdbcStatusCode AdbcConnectionGetStatistics(struct AdbcConnection* connection,
+ const char* catalog, const char* db_schema,
+ const char* table_name, char approximate,
+ struct ArrowArrayStream* out,
+ struct AdbcError* error) {
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode AdbcConnectionGetStatisticNames(struct AdbcConnection* connection,
+ struct ArrowArrayStream* out,
+ struct AdbcError* error) {
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
AdbcStatusCode AdbcConnectionGetTableSchema(struct AdbcConnection* connection,
const char* catalog, const char* db_schema,
const char* table_name,
@@ -1515,6 +1760,11 @@ AdbcStatusCode AdbcConnectionRollback(struct AdbcConnection* connection,
return SqliteConnectionRollback(connection, error);
}
+AdbcStatusCode AdbcStatementCancel(struct AdbcStatement* statement,
+ struct AdbcError* error) {
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
AdbcStatusCode AdbcStatementNew(struct AdbcConnection* connection,
struct AdbcStatement* statement,
struct AdbcError* error) {
@@ -1533,6 +1783,12 @@ AdbcStatusCode AdbcStatementExecuteQuery(struct AdbcStatement* statement,
return SqliteStatementExecuteQuery(statement, out, rows_affected, error);
}
+AdbcStatusCode AdbcStatementExecuteSchema(struct AdbcStatement* statement,
+ struct ArrowSchema* schema,
+ struct AdbcError* error) {
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
AdbcStatusCode AdbcStatementPrepare(struct AdbcStatement* statement,
struct AdbcError* error) {
return SqliteStatementPrepare(statement, error);
@@ -1561,6 +1817,29 @@ AdbcStatusCode AdbcStatementBindStream(struct AdbcStatement* statement,
return SqliteStatementBindStream(statement, stream, error);
}
+AdbcStatusCode AdbcStatementGetOption(struct AdbcStatement* statement, const char* key,
+ char* value, size_t* length,
+ struct AdbcError* error) {
+ return SqliteStatementGetOption(statement, key, value, length, error);
+}
+
+AdbcStatusCode AdbcStatementGetOptionBytes(struct AdbcStatement* statement,
+ const char* key, uint8_t* value,
+ size_t* length, struct AdbcError* error) {
+ return SqliteStatementGetOptionBytes(statement, key, value, length, error);
+}
+
+AdbcStatusCode AdbcStatementGetOptionInt(struct AdbcStatement* statement, const char* key,
+ int64_t* value, struct AdbcError* error) {
+ return SqliteStatementGetOptionInt(statement, key, value, error);
+}
+
+AdbcStatusCode AdbcStatementGetOptionDouble(struct AdbcStatement* statement,
+ const char* key, double* value,
+ struct AdbcError* error) {
+ return SqliteStatementGetOptionDouble(statement, key, value, error);
+}
+
AdbcStatusCode AdbcStatementGetParameterSchema(struct AdbcStatement* statement,
struct ArrowSchema* schema,
struct AdbcError* error) {
@@ -1572,6 +1851,23 @@ AdbcStatusCode AdbcStatementSetOption(struct AdbcStatement* statement, const cha
return SqliteStatementSetOption(statement, key, value, error);
}
+AdbcStatusCode AdbcStatementSetOptionBytes(struct AdbcStatement* statement,
+ const char* key, const uint8_t* value,
+ size_t length, struct AdbcError* error) {
+ return SqliteStatementSetOptionBytes(statement, key, value, length, error);
+}
+
+AdbcStatusCode AdbcStatementSetOptionInt(struct AdbcStatement* statement, const char* key,
+ int64_t value, struct AdbcError* error) {
+ return SqliteStatementSetOptionInt(statement, key, value, error);
+}
+
+AdbcStatusCode AdbcStatementSetOptionDouble(struct AdbcStatement* statement,
+ const char* key, double value,
+ struct AdbcError* error) {
+ return SqliteStatementSetOptionDouble(statement, key, value, error);
+}
+
AdbcStatusCode AdbcStatementExecutePartitions(struct AdbcStatement* statement,
struct ArrowSchema* schema,
struct AdbcPartitions* partitions,
diff --git a/c/driver/sqlite/sqlite_test.cc b/c/driver/sqlite/sqlite_test.cc
index b03158cca6..d2821a306a 100644
--- a/c/driver/sqlite/sqlite_test.cc
+++ b/c/driver/sqlite/sqlite_test.cc
@@ -92,7 +92,27 @@ class SqliteQuirks : public adbc_validation::DriverQuirks {
return ddl;
}
+ bool supports_bulk_ingest(const char* mode) const override {
+ return std::strcmp(mode, ADBC_INGEST_OPTION_MODE_APPEND) == 0 ||
+ std::strcmp(mode, ADBC_INGEST_OPTION_MODE_CREATE) == 0;
+ }
bool supports_concurrent_statements() const override { return true; }
+ bool supports_get_option() const override { return false; }
+ std::optional supports_get_sql_info(
+ uint32_t info_code) const override {
+ switch (info_code) {
+ case ADBC_INFO_DRIVER_NAME:
+ return "ADBC SQLite Driver";
+ case ADBC_INFO_DRIVER_VERSION:
+ return "(unknown)";
+ case ADBC_INFO_VENDOR_NAME:
+ return "SQLite";
+ case ADBC_INFO_VENDOR_VERSION:
+ return "3.";
+ default:
+ return std::nullopt;
+ }
+ }
std::string catalog() const override { return "main"; }
std::string db_schema() const override { return ""; }
diff --git a/c/driver_manager/CMakeLists.txt b/c/driver_manager/CMakeLists.txt
index dd28470cf6..6fb51d9a6a 100644
--- a/c/driver_manager/CMakeLists.txt
+++ b/c/driver_manager/CMakeLists.txt
@@ -55,13 +55,28 @@ if(ADBC_BUILD_TESTS)
driver-manager
SOURCES
adbc_driver_manager_test.cc
- ../validation/adbc_validation.cc
- ../validation/adbc_validation_util.cc
EXTRA_LINK_LIBS
adbc_driver_common
+ adbc_validation
nanoarrow
${TEST_LINK_LIBS})
target_compile_features(adbc-driver-manager-test PRIVATE cxx_std_17)
target_include_directories(adbc-driver-manager-test SYSTEM
PRIVATE ${REPOSITORY_ROOT}/c/vendor/nanoarrow/)
+
+ add_test_case(version_100_compatibility_test
+ PREFIX
+ adbc
+ EXTRA_LABELS
+ driver-manager
+ SOURCES
+ adbc_version_100.c
+ adbc_version_100_compatibility_test.cc
+ EXTRA_LINK_LIBS
+ adbc_validation_util
+ nanoarrow
+ ${TEST_LINK_LIBS})
+ target_compile_features(adbc-version-100-compatibility-test PRIVATE cxx_std_17)
+ target_include_directories(adbc-version-100-compatibility-test SYSTEM
+ PRIVATE ${REPOSITORY_ROOT}/c/vendor/nanoarrow/)
endif()
diff --git a/c/driver_manager/adbc_driver_manager.cc b/c/driver_manager/adbc_driver_manager.cc
index d2929e2129..516bf9bbf7 100644
--- a/c/driver_manager/adbc_driver_manager.cc
+++ b/c/driver_manager/adbc_driver_manager.cc
@@ -19,6 +19,8 @@
#include
#include
+#include
+#include
#include
#include
#include
@@ -90,17 +92,141 @@ void SetError(struct AdbcError* error, const std::string& message) {
// Driver state
-/// Hold the driver DLL and the driver release callback in the driver struct.
-struct ManagerDriverState {
- // The original release callback
- AdbcStatusCode (*driver_release)(struct AdbcDriver* driver, struct AdbcError* error);
+/// A driver DLL.
+struct ManagedLibrary {
+ ManagedLibrary() : handle(nullptr) {}
+ ManagedLibrary(ManagedLibrary&& other) : handle(other.handle) {
+ other.handle = nullptr;
+ }
+ ManagedLibrary(const ManagedLibrary&) = delete;
+ ManagedLibrary& operator=(const ManagedLibrary&) = delete;
+ ManagedLibrary& operator=(ManagedLibrary&& other) noexcept {
+ this->handle = other.handle;
+ other.handle = nullptr;
+ return *this;
+ }
+
+ ~ManagedLibrary() { Release(); }
+
+ void Release() {
+ // TODO(apache/arrow-adbc#204): causes tests to segfault
+ // Need to refcount the driver DLL; also, errors may retain a reference to
+ // release() from the DLL - how to handle this?
+ }
+
+ AdbcStatusCode Load(const char* library, struct AdbcError* error) {
+ std::string error_message;
+#if defined(_WIN32)
+ HMODULE handle = LoadLibraryExA(library, NULL, 0);
+ if (!handle) {
+ error_message += library;
+ error_message += ": LoadLibraryExA() failed: ";
+ GetWinError(&error_message);
+
+ std::string full_driver_name = library;
+ full_driver_name += ".dll";
+ handle = LoadLibraryExA(full_driver_name.c_str(), NULL, 0);
+ if (!handle) {
+ error_message += '\n';
+ error_message += full_driver_name;
+ error_message += ": LoadLibraryExA() failed: ";
+ GetWinError(&error_message);
+ }
+ }
+ if (!handle) {
+ SetError(error, error_message);
+ return ADBC_STATUS_INTERNAL;
+ } else {
+ this->handle = handle;
+ }
+#else
+ static const std::string kPlatformLibraryPrefix = "lib";
+#if defined(__APPLE__)
+ static const std::string kPlatformLibrarySuffix = ".dylib";
+#else
+ static const std::string kPlatformLibrarySuffix = ".so";
+#endif // defined(__APPLE__)
+
+ void* handle = dlopen(library, RTLD_NOW | RTLD_LOCAL);
+ if (!handle) {
+ error_message = "dlopen() failed: ";
+ error_message += dlerror();
+
+ // If applicable, append the shared library prefix/extension and
+ // try again (this way you don't have to hardcode driver names by
+ // platform in the application)
+ const std::string driver_str = library;
+
+ std::string full_driver_name;
+ if (driver_str.size() < kPlatformLibraryPrefix.size() ||
+ driver_str.compare(0, kPlatformLibraryPrefix.size(), kPlatformLibraryPrefix) !=
+ 0) {
+ full_driver_name += kPlatformLibraryPrefix;
+ }
+ full_driver_name += library;
+ if (driver_str.size() < kPlatformLibrarySuffix.size() ||
+ driver_str.compare(full_driver_name.size() - kPlatformLibrarySuffix.size(),
+ kPlatformLibrarySuffix.size(),
+ kPlatformLibrarySuffix) != 0) {
+ full_driver_name += kPlatformLibrarySuffix;
+ }
+ handle = dlopen(full_driver_name.c_str(), RTLD_NOW | RTLD_LOCAL);
+ if (!handle) {
+ error_message += "\ndlopen() failed: ";
+ error_message += dlerror();
+ }
+ }
+ if (handle) {
+ this->handle = handle;
+ } else {
+ return ADBC_STATUS_INTERNAL;
+ }
+#endif // defined(_WIN32)
+ return ADBC_STATUS_OK;
+ }
+
+ AdbcStatusCode Lookup(const char* name, void** func, struct AdbcError* error) {
+#if defined(_WIN32)
+ void* load_handle = reinterpret_cast(GetProcAddress(handle, name));
+ if (!load_handle) {
+ std::string message = "GetProcAddress(";
+ message += name;
+ message += ") failed: ";
+ GetWinError(&message);
+ SetError(error, message);
+ return ADBC_STATUS_INTERNAL;
+ }
+#else
+ void* load_handle = dlsym(handle, name);
+ if (!load_handle) {
+ std::string message = "dlsym(";
+ message += name;
+ message += ") failed: ";
+ message += dlerror();
+ SetError(error, message);
+ return ADBC_STATUS_INTERNAL;
+ }
+#endif // defined(_WIN32)
+ *func = load_handle;
+ return ADBC_STATUS_OK;
+ }
#if defined(_WIN32)
// The loaded DLL
HMODULE handle;
+#else
+ void* handle;
#endif // defined(_WIN32)
};
+/// Hold the driver DLL and the driver release callback in the driver struct.
+struct ManagerDriverState {
+ // The original release callback
+ AdbcStatusCode (*driver_release)(struct AdbcDriver* driver, struct AdbcError* error);
+
+ ManagedLibrary handle;
+};
+
/// Unload the driver DLL.
static AdbcStatusCode ReleaseDriver(struct AdbcDriver* driver, struct AdbcError* error) {
AdbcStatusCode status = ADBC_STATUS_OK;
@@ -112,35 +238,132 @@ static AdbcStatusCode ReleaseDriver(struct AdbcDriver* driver, struct AdbcError*
if (state->driver_release) {
status = state->driver_release(driver, error);
}
-
-#if defined(_WIN32)
- // TODO(apache/arrow-adbc#204): causes tests to segfault
- // if (!FreeLibrary(state->handle)) {
- // std::string message = "FreeLibrary() failed: ";
- // GetWinError(&message);
- // SetError(error, message);
- // }
-#endif // defined(_WIN32)
+ state->handle.Release();
driver->private_manager = nullptr;
delete state;
return status;
}
+// ArrowArrayStream wrapper to support AdbcErrorFromArrayStream
+
+struct ErrorArrayStream {
+ struct ArrowArrayStream stream;
+ struct AdbcDriver* private_driver;
+};
+
+void ErrorArrayStreamRelease(struct ArrowArrayStream* stream) {
+ if (stream->release != ErrorArrayStreamRelease || !stream->private_data) return;
+
+ auto* private_data = reinterpret_cast(stream->private_data);
+ private_data->stream.release(&private_data->stream);
+ delete private_data;
+ std::memset(stream, 0, sizeof(*stream));
+}
+
+const char* ErrorArrayStreamGetLastError(struct ArrowArrayStream* stream) {
+ if (stream->release != ErrorArrayStreamRelease || !stream->private_data) return nullptr;
+ auto* private_data = reinterpret_cast(stream->private_data);
+ return private_data->stream.get_last_error(&private_data->stream);
+}
+
+int ErrorArrayStreamGetNext(struct ArrowArrayStream* stream, struct ArrowArray* array) {
+ if (stream->release != ErrorArrayStreamRelease || !stream->private_data) return EINVAL;
+ auto* private_data = reinterpret_cast(stream->private_data);
+ return private_data->stream.get_next(&private_data->stream, array);
+}
+
+int ErrorArrayStreamGetSchema(struct ArrowArrayStream* stream,
+ struct ArrowSchema* schema) {
+ if (stream->release != ErrorArrayStreamRelease || !stream->private_data) return EINVAL;
+ auto* private_data = reinterpret_cast(stream->private_data);
+ return private_data->stream.get_schema(&private_data->stream, schema);
+}
+
// Default stubs
+int ErrorGetDetailCount(const struct AdbcError* error) { return 0; }
+
+struct AdbcErrorDetail ErrorGetDetail(const struct AdbcError* error, int index) {
+ return {nullptr, nullptr, 0};
+}
+
+const struct AdbcError* ErrorFromArrayStream(struct ArrowArrayStream* stream,
+ AdbcStatusCode* status) {
+ return nullptr;
+}
+
+void ErrorArrayStreamInit(struct ArrowArrayStream* out,
+ struct AdbcDriver* private_driver) {
+ if (!out || !out->release ||
+ // Don't bother wrapping if driver didn't claim support
+ private_driver->ErrorFromArrayStream == ErrorFromArrayStream) {
+ return;
+ }
+ struct ErrorArrayStream* private_data = new ErrorArrayStream;
+ private_data->stream = *out;
+ private_data->private_driver = private_driver;
+ out->get_last_error = ErrorArrayStreamGetLastError;
+ out->get_next = ErrorArrayStreamGetNext;
+ out->get_schema = ErrorArrayStreamGetSchema;
+ out->release = ErrorArrayStreamRelease;
+ out->private_data = private_data;
+}
+
+AdbcStatusCode DatabaseGetOption(struct AdbcDatabase* database, const char* key,
+ char* value, size_t* length, struct AdbcError* error) {
+ return ADBC_STATUS_NOT_FOUND;
+}
+
+AdbcStatusCode DatabaseGetOptionBytes(struct AdbcDatabase* database, const char* key,
+ uint8_t* value, size_t* length,
+ struct AdbcError* error) {
+ return ADBC_STATUS_NOT_FOUND;
+}
+
+AdbcStatusCode DatabaseGetOptionInt(struct AdbcDatabase* database, const char* key,
+ int64_t* value, struct AdbcError* error) {
+ return ADBC_STATUS_NOT_FOUND;
+}
+
+AdbcStatusCode DatabaseGetOptionDouble(struct AdbcDatabase* database, const char* key,
+ double* value, struct AdbcError* error) {
+ return ADBC_STATUS_NOT_FOUND;
+}
+
AdbcStatusCode DatabaseSetOption(struct AdbcDatabase* database, const char* key,
const char* value, struct AdbcError* error) {
return ADBC_STATUS_NOT_IMPLEMENTED;
}
+AdbcStatusCode DatabaseSetOptionBytes(struct AdbcDatabase* database, const char* key,
+ const uint8_t* value, size_t length,
+ struct AdbcError* error) {
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode DatabaseSetOptionInt(struct AdbcDatabase* database, const char* key,
+ int64_t value, struct AdbcError* error) {
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode DatabaseSetOptionDouble(struct AdbcDatabase* database, const char* key,
+ double value, struct AdbcError* error) {
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode ConnectionCancel(struct AdbcConnection* connection,
+ struct AdbcError* error) {
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
AdbcStatusCode ConnectionCommit(struct AdbcConnection*, struct AdbcError* error) {
return ADBC_STATUS_NOT_IMPLEMENTED;
}
-AdbcStatusCode ConnectionGetInfo(struct AdbcConnection* connection, uint32_t* info_codes,
- size_t info_codes_length, struct ArrowArrayStream* out,
- struct AdbcError* error) {
+AdbcStatusCode ConnectionGetInfo(struct AdbcConnection* connection,
+ const uint32_t* info_codes, size_t info_codes_length,
+ struct ArrowArrayStream* out, struct AdbcError* error) {
return ADBC_STATUS_NOT_IMPLEMENTED;
}
@@ -150,6 +373,39 @@ AdbcStatusCode ConnectionGetObjects(struct AdbcConnection*, int, const char*, co
return ADBC_STATUS_NOT_IMPLEMENTED;
}
+AdbcStatusCode ConnectionGetOption(struct AdbcConnection* connection, const char* key,
+ char* value, size_t* length, struct AdbcError* error) {
+ return ADBC_STATUS_NOT_FOUND;
+}
+
+AdbcStatusCode ConnectionGetOptionBytes(struct AdbcConnection* connection,
+ const char* key, uint8_t* value, size_t* length,
+ struct AdbcError* error) {
+ return ADBC_STATUS_NOT_FOUND;
+}
+
+AdbcStatusCode ConnectionGetOptionInt(struct AdbcConnection* connection, const char* key,
+ int64_t* value, struct AdbcError* error) {
+ return ADBC_STATUS_NOT_FOUND;
+}
+
+AdbcStatusCode ConnectionGetOptionDouble(struct AdbcConnection* connection,
+ const char* key, double* value,
+ struct AdbcError* error) {
+ return ADBC_STATUS_NOT_FOUND;
+}
+
+AdbcStatusCode ConnectionGetStatistics(struct AdbcConnection*, const char*, const char*,
+ const char*, char, struct ArrowArrayStream*,
+ struct AdbcError*) {
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode ConnectionGetStatisticNames(struct AdbcConnection*,
+ struct ArrowArrayStream*, struct AdbcError*) {
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
AdbcStatusCode ConnectionGetTableSchema(struct AdbcConnection*, const char*, const char*,
const char*, struct ArrowSchema*,
struct AdbcError* error) {
@@ -178,11 +434,31 @@ AdbcStatusCode ConnectionSetOption(struct AdbcConnection*, const char*, const ch
return ADBC_STATUS_NOT_IMPLEMENTED;
}
+AdbcStatusCode ConnectionSetOptionBytes(struct AdbcConnection*, const char*,
+ const uint8_t*, size_t, struct AdbcError* error) {
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode ConnectionSetOptionInt(struct AdbcConnection* connection, const char* key,
+ int64_t value, struct AdbcError* error) {
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode ConnectionSetOptionDouble(struct AdbcConnection* connection,
+ const char* key, double value,
+ struct AdbcError* error) {
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
AdbcStatusCode StatementBind(struct AdbcStatement*, struct ArrowArray*,
struct ArrowSchema*, struct AdbcError* error) {
return ADBC_STATUS_NOT_IMPLEMENTED;
}
+AdbcStatusCode StatementCancel(struct AdbcStatement* statement, struct AdbcError* error) {
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
AdbcStatusCode StatementExecutePartitions(struct AdbcStatement* statement,
struct ArrowSchema* schema,
struct AdbcPartitions* partitions,
@@ -191,6 +467,33 @@ AdbcStatusCode StatementExecutePartitions(struct AdbcStatement* statement,
return ADBC_STATUS_NOT_IMPLEMENTED;
}
+AdbcStatusCode StatementExecuteSchema(struct AdbcStatement* statement,
+ struct ArrowSchema* schema,
+ struct AdbcError* error) {
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode StatementGetOption(struct AdbcStatement* statement, const char* key,
+ char* value, size_t* length, struct AdbcError* error) {
+ return ADBC_STATUS_NOT_FOUND;
+}
+
+AdbcStatusCode StatementGetOptionBytes(struct AdbcStatement* statement, const char* key,
+ uint8_t* value, size_t* length,
+ struct AdbcError* error) {
+ return ADBC_STATUS_NOT_FOUND;
+}
+
+AdbcStatusCode StatementGetOptionInt(struct AdbcStatement* statement, const char* key,
+ int64_t* value, struct AdbcError* error) {
+ return ADBC_STATUS_NOT_FOUND;
+}
+
+AdbcStatusCode StatementGetOptionDouble(struct AdbcStatement* statement, const char* key,
+ double* value, struct AdbcError* error) {
+ return ADBC_STATUS_NOT_FOUND;
+}
+
AdbcStatusCode StatementGetParameterSchema(struct AdbcStatement* statement,
struct ArrowSchema* schema,
struct AdbcError* error) {
@@ -206,6 +509,21 @@ AdbcStatusCode StatementSetOption(struct AdbcStatement*, const char*, const char
return ADBC_STATUS_NOT_IMPLEMENTED;
}
+AdbcStatusCode StatementSetOptionBytes(struct AdbcStatement*, const char*, const uint8_t*,
+ size_t, struct AdbcError* error) {
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode StatementSetOptionInt(struct AdbcStatement* statement, const char* key,
+ int64_t value, struct AdbcError* error) {
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode StatementSetOptionDouble(struct AdbcStatement* statement, const char* key,
+ double value, struct AdbcError* error) {
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
AdbcStatusCode StatementSetSqlQuery(struct AdbcStatement*, const char*,
struct AdbcError* error) {
return ADBC_STATUS_NOT_IMPLEMENTED;
@@ -219,20 +537,129 @@ AdbcStatusCode StatementSetSubstraitPlan(struct AdbcStatement*, const uint8_t*,
/// Temporary state while the database is being configured.
struct TempDatabase {
std::unordered_map options;
+ std::unordered_map bytes_options;
+ std::unordered_map int_options;
+ std::unordered_map double_options;
std::string driver;
- // Default name (see adbc.h)
- std::string entrypoint = "AdbcDriverInit";
+ std::string entrypoint;
AdbcDriverInitFunc init_func = nullptr;
};
/// Temporary state while the database is being configured.
struct TempConnection {
std::unordered_map options;
+ std::unordered_map bytes_options;
+ std::unordered_map int_options;
+ std::unordered_map double_options;
};
+
+static const char kDefaultEntrypoint[] = "AdbcDriverInit";
} // namespace
+// Other helpers (intentionally not in an anonymous namespace so they can be tested)
+
+ADBC_EXPORT
+std::string AdbcDriverManagerDefaultEntrypoint(const std::string& driver) {
+ /// - libadbc_driver_sqlite.so.2.0.0 -> AdbcDriverSqliteInit
+ /// - adbc_driver_sqlite.dll -> AdbcDriverSqliteInit
+ /// - proprietary_driver.dll -> AdbcProprietaryDriverInit
+
+ // Potential path -> filename
+ // Treat both \ and / as directory separators on all platforms for simplicity
+ std::string filename;
+ {
+ size_t pos = driver.find_last_of("/\\");
+ if (pos != std::string::npos) {
+ filename = driver.substr(pos + 1);
+ } else {
+ filename = driver;
+ }
+ }
+
+ // Remove all extensions
+ {
+ size_t pos = filename.find('.');
+ if (pos != std::string::npos) {
+ filename = filename.substr(0, pos);
+ }
+ }
+
+ // Remove lib prefix
+ // https://stackoverflow.com/q/1878001/262727
+ if (filename.rfind("lib", 0) == 0) {
+ filename = filename.substr(3);
+ }
+
+ // Split on underscores, hyphens
+ // Capitalize and join
+ std::string entrypoint;
+ entrypoint.reserve(filename.size());
+ size_t pos = 0;
+ while (pos < filename.size()) {
+ size_t prev = pos;
+ pos = filename.find_first_of("-_", pos);
+ // if pos == npos this is the entire filename
+ std::string token = filename.substr(prev, pos - prev);
+ // capitalize first letter
+ token[0] = std::toupper(static_cast(token[0]));
+
+ entrypoint += token;
+
+ if (pos != std::string::npos) {
+ pos++;
+ }
+ }
+
+ if (entrypoint.rfind("Adbc", 0) != 0) {
+ entrypoint = "Adbc" + entrypoint;
+ }
+ entrypoint += "Init";
+
+ return entrypoint;
+}
+
// Direct implementations of API methods
+int AdbcErrorGetDetailCount(const struct AdbcError* error) {
+ if (error->vendor_code == ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA && error->private_data &&
+ error->private_driver) {
+ return error->private_driver->ErrorGetDetailCount(error);
+ }
+ return 0;
+}
+
+struct AdbcErrorDetail AdbcErrorGetDetail(const struct AdbcError* error, int index) {
+ if (error->vendor_code == ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA && error->private_data &&
+ error->private_driver) {
+ return error->private_driver->ErrorGetDetail(error, index);
+ }
+ return {nullptr, nullptr, 0};
+}
+
+const struct AdbcError* AdbcErrorFromArrayStream(struct ArrowArrayStream* stream,
+ AdbcStatusCode* status) {
+ if (!stream->private_data || stream->release != ErrorArrayStreamRelease) {
+ return nullptr;
+ }
+ auto* private_data = reinterpret_cast(stream->private_data);
+ return private_data->private_driver->ErrorFromArrayStream(&private_data->stream,
+ status);
+}
+
+#define INIT_ERROR(ERROR, SOURCE) \
+ if ((ERROR)->vendor_code == ADBC_ERROR_VENDOR_CODE_PRIVATE_DATA) { \
+ (ERROR)->private_driver = (SOURCE)->private_driver; \
+ }
+
+#define WRAP_STREAM(EXPR, OUT, SOURCE) \
+ if (!(OUT)) { \
+ /* Happens for ExecuteQuery where out is optional */ \
+ return EXPR; \
+ } \
+ AdbcStatusCode status_code = EXPR; \
+ ErrorArrayStreamInit(OUT, (SOURCE)->private_driver); \
+ return status_code;
+
AdbcStatusCode AdbcDatabaseNew(struct AdbcDatabase* database, struct AdbcError* error) {
// Allocate a temporary structure to store options pre-Init
database->private_data = new TempDatabase();
@@ -240,9 +667,93 @@ AdbcStatusCode AdbcDatabaseNew(struct AdbcDatabase* database, struct AdbcError*
return ADBC_STATUS_OK;
}
+AdbcStatusCode AdbcDatabaseGetOption(struct AdbcDatabase* database, const char* key,
+ char* value, size_t* length,
+ struct AdbcError* error) {
+ if (database->private_driver) {
+ INIT_ERROR(error, database);
+ return database->private_driver->DatabaseGetOption(database, key, value, length,
+ error);
+ }
+ const auto* args = reinterpret_cast(database->private_data);
+ const std::string* result = nullptr;
+ if (std::strcmp(key, "driver") == 0) {
+ result = &args->driver;
+ } else if (std::strcmp(key, "entrypoint") == 0) {
+ result = &args->entrypoint;
+ } else {
+ const auto it = args->options.find(key);
+ if (it == args->options.end()) {
+ return ADBC_STATUS_NOT_FOUND;
+ }
+ result = &it->second;
+ }
+
+ if (*length <= result->size() + 1) {
+ // Enough space
+ std::memcpy(value, result->c_str(), result->size() + 1);
+ }
+ *length = result->size() + 1;
+ return ADBC_STATUS_OK;
+}
+
+AdbcStatusCode AdbcDatabaseGetOptionBytes(struct AdbcDatabase* database, const char* key,
+ uint8_t* value, size_t* length,
+ struct AdbcError* error) {
+ if (database->private_driver) {
+ INIT_ERROR(error, database);
+ return database->private_driver->DatabaseGetOptionBytes(database, key, value, length,
+ error);
+ }
+ const auto* args = reinterpret_cast(database->private_data);
+ const auto it = args->bytes_options.find(key);
+ if (it == args->options.end()) {
+ return ADBC_STATUS_NOT_FOUND;
+ }
+ const std::string& result = it->second;
+
+ if (*length <= result.size()) {
+ // Enough space
+ std::memcpy(value, result.c_str(), result.size());
+ }
+ *length = result.size();
+ return ADBC_STATUS_OK;
+}
+
+AdbcStatusCode AdbcDatabaseGetOptionInt(struct AdbcDatabase* database, const char* key,
+ int64_t* value, struct AdbcError* error) {
+ if (database->private_driver) {
+ INIT_ERROR(error, database);
+ return database->private_driver->DatabaseGetOptionInt(database, key, value, error);
+ }
+ const auto* args = reinterpret_cast(database->private_data);
+ const auto it = args->int_options.find(key);
+ if (it == args->int_options.end()) {
+ return ADBC_STATUS_NOT_FOUND;
+ }
+ *value = it->second;
+ return ADBC_STATUS_OK;
+}
+
+AdbcStatusCode AdbcDatabaseGetOptionDouble(struct AdbcDatabase* database, const char* key,
+ double* value, struct AdbcError* error) {
+ if (database->private_driver) {
+ INIT_ERROR(error, database);
+ return database->private_driver->DatabaseGetOptionDouble(database, key, value, error);
+ }
+ const auto* args = reinterpret_cast(database->private_data);
+ const auto it = args->double_options.find(key);
+ if (it == args->double_options.end()) {
+ return ADBC_STATUS_NOT_FOUND;
+ }
+ *value = it->second;
+ return ADBC_STATUS_OK;
+}
+
AdbcStatusCode AdbcDatabaseSetOption(struct AdbcDatabase* database, const char* key,
const char* value, struct AdbcError* error) {
if (database->private_driver) {
+ INIT_ERROR(error, database);
return database->private_driver->DatabaseSetOption(database, key, value, error);
}
@@ -257,6 +768,44 @@ AdbcStatusCode AdbcDatabaseSetOption(struct AdbcDatabase* database, const char*
return ADBC_STATUS_OK;
}
+AdbcStatusCode AdbcDatabaseSetOptionBytes(struct AdbcDatabase* database, const char* key,
+ const uint8_t* value, size_t length,
+ struct AdbcError* error) {
+ if (database->private_driver) {
+ INIT_ERROR(error, database);
+ return database->private_driver->DatabaseSetOptionBytes(database, key, value, length,
+ error);
+ }
+
+ TempDatabase* args = reinterpret_cast(database->private_data);
+ args->bytes_options[key] = std::string(reinterpret_cast(value), length);
+ return ADBC_STATUS_OK;
+}
+
+AdbcStatusCode AdbcDatabaseSetOptionInt(struct AdbcDatabase* database, const char* key,
+ int64_t value, struct AdbcError* error) {
+ if (database->private_driver) {
+ INIT_ERROR(error, database);
+ return database->private_driver->DatabaseSetOptionInt(database, key, value, error);
+ }
+
+ TempDatabase* args = reinterpret_cast(database->private_data);
+ args->int_options[key] = value;
+ return ADBC_STATUS_OK;
+}
+
+AdbcStatusCode AdbcDatabaseSetOptionDouble(struct AdbcDatabase* database, const char* key,
+ double value, struct AdbcError* error) {
+ if (database->private_driver) {
+ INIT_ERROR(error, database);
+ return database->private_driver->DatabaseSetOptionDouble(database, key, value, error);
+ }
+
+ TempDatabase* args = reinterpret_cast(database->private_data);
+ args->double_options[key] = value;
+ return ADBC_STATUS_OK;
+}
+
AdbcStatusCode AdbcDriverManagerDatabaseSetInitFunc(struct AdbcDatabase* database,
AdbcDriverInitFunc init_func,
struct AdbcError* error) {
@@ -288,11 +837,14 @@ AdbcStatusCode AdbcDatabaseInit(struct AdbcDatabase* database, struct AdbcError*
// So we don't confuse a driver into thinking it's initialized already
database->private_data = nullptr;
if (args->init_func) {
- status = AdbcLoadDriverFromInitFunc(args->init_func, ADBC_VERSION_1_0_0,
+ status = AdbcLoadDriverFromInitFunc(args->init_func, ADBC_VERSION_1_1_0,
database->private_driver, error);
- } else {
+ } else if (!args->entrypoint.empty()) {
status = AdbcLoadDriver(args->driver.c_str(), args->entrypoint.c_str(),
- ADBC_VERSION_1_0_0, database->private_driver, error);
+ ADBC_VERSION_1_1_0, database->private_driver, error);
+ } else {
+ status = AdbcLoadDriver(args->driver.c_str(), nullptr, ADBC_VERSION_1_1_0,
+ database->private_driver, error);
}
if (status != ADBC_STATUS_OK) {
// Restore private_data so it will be released by AdbcDatabaseRelease
@@ -313,25 +865,49 @@ AdbcStatusCode AdbcDatabaseInit(struct AdbcDatabase* database, struct AdbcError*
database->private_driver = nullptr;
return status;
}
- for (const auto& option : args->options) {
+ auto options = std::move(args->options);
+ auto bytes_options = std::move(args->bytes_options);
+ auto int_options = std::move(args->int_options);
+ auto double_options = std::move(args->double_options);
+ delete args;
+
+ INIT_ERROR(error, database);
+ for (const auto& option : options) {
status = database->private_driver->DatabaseSetOption(database, option.first.c_str(),
option.second.c_str(), error);
- if (status != ADBC_STATUS_OK) {
- delete args;
- // Release the database
- std::ignore = database->private_driver->DatabaseRelease(database, error);
- if (database->private_driver->release) {
- database->private_driver->release(database->private_driver, error);
- }
- delete database->private_driver;
- database->private_driver = nullptr;
- // Should be redundant, but ensure that AdbcDatabaseRelease
- // below doesn't think that it contains a TempDatabase
- database->private_data = nullptr;
- return status;
+ if (status != ADBC_STATUS_OK) break;
+ }
+ for (const auto& option : bytes_options) {
+ status = database->private_driver->DatabaseSetOptionBytes(
+ database, option.first.c_str(),
+ reinterpret_cast(option.second.data()), option.second.size(),
+ error);
+ if (status != ADBC_STATUS_OK) break;
+ }
+ for (const auto& option : int_options) {
+ status = database->private_driver->DatabaseSetOptionInt(
+ database, option.first.c_str(), option.second, error);
+ if (status != ADBC_STATUS_OK) break;
+ }
+ for (const auto& option : double_options) {
+ status = database->private_driver->DatabaseSetOptionDouble(
+ database, option.first.c_str(), option.second, error);
+ if (status != ADBC_STATUS_OK) break;
+ }
+
+ if (status != ADBC_STATUS_OK) {
+ // Release the database
+ std::ignore = database->private_driver->DatabaseRelease(database, error);
+ if (database->private_driver->release) {
+ database->private_driver->release(database->private_driver, error);
}
+ delete database->private_driver;
+ database->private_driver = nullptr;
+ // Should be redundant, but ensure that AdbcDatabaseRelease
+ // below doesn't think that it contains a TempDatabase
+ database->private_data = nullptr;
+ return status;
}
- delete args;
return database->private_driver->DatabaseInit(database, error);
}
@@ -346,6 +922,7 @@ AdbcStatusCode AdbcDatabaseRelease(struct AdbcDatabase* database,
}
return ADBC_STATUS_INVALID_STATE;
}
+ INIT_ERROR(error, database);
auto status = database->private_driver->DatabaseRelease(database, error);
if (database->private_driver->release) {
database->private_driver->release(database->private_driver, error);
@@ -356,23 +933,35 @@ AdbcStatusCode AdbcDatabaseRelease(struct AdbcDatabase* database,
return status;
}
+AdbcStatusCode AdbcConnectionCancel(struct AdbcConnection* connection,
+ struct AdbcError* error) {
+ if (!connection->private_driver) {
+ return ADBC_STATUS_INVALID_STATE;
+ }
+ INIT_ERROR(error, connection);
+ return connection->private_driver->ConnectionCancel(connection, error);
+}
+
AdbcStatusCode AdbcConnectionCommit(struct AdbcConnection* connection,
struct AdbcError* error) {
if (!connection->private_driver) {
return ADBC_STATUS_INVALID_STATE;
}
+ INIT_ERROR(error, connection);
return connection->private_driver->ConnectionCommit(connection, error);
}
AdbcStatusCode AdbcConnectionGetInfo(struct AdbcConnection* connection,
- uint32_t* info_codes, size_t info_codes_length,
+ const uint32_t* info_codes, size_t info_codes_length,
struct ArrowArrayStream* out,
struct AdbcError* error) {
if (!connection->private_driver) {
return ADBC_STATUS_INVALID_STATE;
}
- return connection->private_driver->ConnectionGetInfo(connection, info_codes,
- info_codes_length, out, error);
+ INIT_ERROR(error, connection);
+ WRAP_STREAM(connection->private_driver->ConnectionGetInfo(
+ connection, info_codes, info_codes_length, out, error),
+ out, connection);
}
AdbcStatusCode AdbcConnectionGetObjects(struct AdbcConnection* connection, int depth,
@@ -384,9 +973,132 @@ AdbcStatusCode AdbcConnectionGetObjects(struct AdbcConnection* connection, int d
if (!connection->private_driver) {
return ADBC_STATUS_INVALID_STATE;
}
- return connection->private_driver->ConnectionGetObjects(
- connection, depth, catalog, db_schema, table_name, table_types, column_name, stream,
- error);
+ INIT_ERROR(error, connection);
+ WRAP_STREAM(connection->private_driver->ConnectionGetObjects(
+ connection, depth, catalog, db_schema, table_name, table_types,
+ column_name, stream, error),
+ stream, connection);
+}
+
+AdbcStatusCode AdbcConnectionGetOption(struct AdbcConnection* connection, const char* key,
+ char* value, size_t* length,
+ struct AdbcError* error) {
+ if (!connection->private_data) {
+ SetError(error, "AdbcConnectionGetOption: must AdbcConnectionNew first");
+ return ADBC_STATUS_INVALID_STATE;
+ }
+ if (!connection->private_driver) {
+ // Init not yet called, get the saved option
+ const auto* args = reinterpret_cast(connection->private_data);
+ const auto it = args->options.find(key);
+ if (it == args->options.end()) {
+ return ADBC_STATUS_NOT_FOUND;
+ }
+ if (*length >= it->second.size() + 1) {
+ std::memcpy(value, it->second.c_str(), it->second.size() + 1);
+ }
+ *length = it->second.size() + 1;
+ return ADBC_STATUS_OK;
+ }
+ INIT_ERROR(error, connection);
+ return connection->private_driver->ConnectionGetOption(connection, key, value, length,
+ error);
+}
+
+AdbcStatusCode AdbcConnectionGetOptionBytes(struct AdbcConnection* connection,
+ const char* key, uint8_t* value,
+ size_t* length, struct AdbcError* error) {
+ if (!connection->private_data) {
+ SetError(error, "AdbcConnectionGetOption: must AdbcConnectionNew first");
+ return ADBC_STATUS_INVALID_STATE;
+ }
+ if (!connection->private_driver) {
+ // Init not yet called, get the saved option
+ const auto* args = reinterpret_cast(connection->private_data);
+ const auto it = args->bytes_options.find(key);
+ if (it == args->options.end()) {
+ return ADBC_STATUS_NOT_FOUND;
+ }
+ if (*length >= it->second.size() + 1) {
+ std::memcpy(value, it->second.data(), it->second.size() + 1);
+ }
+ *length = it->second.size() + 1;
+ return ADBC_STATUS_OK;
+ }
+ INIT_ERROR(error, connection);
+ return connection->private_driver->ConnectionGetOptionBytes(connection, key, value,
+ length, error);
+}
+
+AdbcStatusCode AdbcConnectionGetOptionInt(struct AdbcConnection* connection,
+ const char* key, int64_t* value,
+ struct AdbcError* error) {
+ if (!connection->private_data) {
+ SetError(error, "AdbcConnectionGetOption: must AdbcConnectionNew first");
+ return ADBC_STATUS_INVALID_STATE;
+ }
+ if (!connection->private_driver) {
+ // Init not yet called, get the saved option
+ const auto* args = reinterpret_cast(connection->private_data);
+ const auto it = args->int_options.find(key);
+ if (it == args->int_options.end()) {
+ return ADBC_STATUS_NOT_FOUND;
+ }
+ *value = it->second;
+ return ADBC_STATUS_OK;
+ }
+ INIT_ERROR(error, connection);
+ return connection->private_driver->ConnectionGetOptionInt(connection, key, value,
+ error);
+}
+
+AdbcStatusCode AdbcConnectionGetOptionDouble(struct AdbcConnection* connection,
+ const char* key, double* value,
+ struct AdbcError* error) {
+ if (!connection->private_data) {
+ SetError(error, "AdbcConnectionGetOption: must AdbcConnectionNew first");
+ return ADBC_STATUS_INVALID_STATE;
+ }
+ if (!connection->private_driver) {
+ // Init not yet called, get the saved option
+ const auto* args = reinterpret_cast(connection->private_data);
+ const auto it = args->double_options.find(key);
+ if (it == args->double_options.end()) {
+ return ADBC_STATUS_NOT_FOUND;
+ }
+ *value = it->second;
+ return ADBC_STATUS_OK;
+ }
+ INIT_ERROR(error, connection);
+ return connection->private_driver->ConnectionGetOptionDouble(connection, key, value,
+ error);
+}
+
+AdbcStatusCode AdbcConnectionGetStatistics(struct AdbcConnection* connection,
+ const char* catalog, const char* db_schema,
+ const char* table_name, char approximate,
+ struct ArrowArrayStream* out,
+ struct AdbcError* error) {
+ if (!connection->private_driver) {
+ return ADBC_STATUS_INVALID_STATE;
+ }
+ INIT_ERROR(error, connection);
+ WRAP_STREAM(
+ connection->private_driver->ConnectionGetStatistics(
+ connection, catalog, db_schema, table_name, approximate == 1, out, error),
+ out, connection);
+}
+
+AdbcStatusCode AdbcConnectionGetStatisticNames(struct AdbcConnection* connection,
+ struct ArrowArrayStream* out,
+ struct AdbcError* error) {
+ if (!connection->private_driver) {
+ return ADBC_STATUS_INVALID_STATE;
+ }
+ INIT_ERROR(error, connection);
+ WRAP_STREAM(
+ connection->private_driver->ConnectionGetStatisticNames(connection, out, error),
+ out, connection);
}
AdbcStatusCode AdbcConnectionGetTableSchema(struct AdbcConnection* connection,
@@ -397,6 +1109,7 @@ AdbcStatusCode AdbcConnectionGetTableSchema(struct AdbcConnection* connection,
if (!connection->private_driver) {
return ADBC_STATUS_INVALID_STATE;
}
+ INIT_ERROR(error, connection);
return connection->private_driver->ConnectionGetTableSchema(
connection, catalog, db_schema, table_name, schema, error);
}
@@ -407,7 +1120,10 @@ AdbcStatusCode AdbcConnectionGetTableTypes(struct AdbcConnection* connection,
if (!connection->private_driver) {
return ADBC_STATUS_INVALID_STATE;
}
- return connection->private_driver->ConnectionGetTableTypes(connection, stream, error);
+ INIT_ERROR(error, connection);
+ WRAP_STREAM(
+ connection->private_driver->ConnectionGetTableTypes(connection, stream, error),
+ stream, connection);
}
AdbcStatusCode AdbcConnectionInit(struct AdbcConnection* connection,
@@ -423,6 +1139,11 @@ AdbcStatusCode AdbcConnectionInit(struct AdbcConnection* connection,
TempConnection* args = reinterpret_cast(connection->private_data);
connection->private_data = nullptr;
std::unordered_map options = std::move(args->options);
+ std::unordered_map bytes_options =
+ std::move(args->bytes_options);
+ std::unordered_map int_options = std::move(args->int_options);
+ std::unordered_map double_options =
+ std::move(args->double_options);
delete args;
auto status = database->private_driver->ConnectionNew(connection, error);
@@ -434,6 +1155,24 @@ AdbcStatusCode AdbcConnectionInit(struct AdbcConnection* connection,
connection, option.first.c_str(), option.second.c_str(), error);
if (status != ADBC_STATUS_OK) return status;
}
+ for (const auto& option : bytes_options) {
+ status = database->private_driver->ConnectionSetOptionBytes(
+ connection, option.first.c_str(),
+ reinterpret_cast(option.second.data()), option.second.size(),
+ error);
+ if (status != ADBC_STATUS_OK) return status;
+ }
+ for (const auto& option : int_options) {
+ status = database->private_driver->ConnectionSetOptionInt(
+ connection, option.first.c_str(), option.second, error);
+ if (status != ADBC_STATUS_OK) return status;
+ }
+ for (const auto& option : double_options) {
+ status = database->private_driver->ConnectionSetOptionDouble(
+ connection, option.first.c_str(), option.second, error);
+ if (status != ADBC_STATUS_OK) return status;
+ }
+ INIT_ERROR(error, connection);
return connection->private_driver->ConnectionInit(connection, database, error);
}
@@ -455,8 +1194,10 @@ AdbcStatusCode AdbcConnectionReadPartition(struct AdbcConnection* connection,
if (!connection->private_driver) {
return ADBC_STATUS_INVALID_STATE;
}
- return connection->private_driver->ConnectionReadPartition(
- connection, serialized_partition, serialized_length, out, error);
+ INIT_ERROR(error, connection);
+ WRAP_STREAM(connection->private_driver->ConnectionReadPartition(
+ connection, serialized_partition, serialized_length, out, error),
+ out, connection);
}
AdbcStatusCode AdbcConnectionRelease(struct AdbcConnection* connection,
@@ -470,6 +1211,7 @@ AdbcStatusCode AdbcConnectionRelease(struct AdbcConnection* connection,
}
return ADBC_STATUS_INVALID_STATE;
}
+ INIT_ERROR(error, connection);
auto status = connection->private_driver->ConnectionRelease(connection, error);
connection->private_driver = nullptr;
return status;
@@ -480,6 +1222,7 @@ AdbcStatusCode AdbcConnectionRollback(struct AdbcConnection* connection,
if (!connection->private_driver) {
return ADBC_STATUS_INVALID_STATE;
}
+ INIT_ERROR(error, connection);
return connection->private_driver->ConnectionRollback(connection, error);
}
@@ -495,15 +1238,71 @@ AdbcStatusCode AdbcConnectionSetOption(struct AdbcConnection* connection, const
args->options[key] = value;
return ADBC_STATUS_OK;
}
+ INIT_ERROR(error, connection);
return connection->private_driver->ConnectionSetOption(connection, key, value, error);
}
+AdbcStatusCode AdbcConnectionSetOptionBytes(struct AdbcConnection* connection,
+ const char* key, const uint8_t* value,
+ size_t length, struct AdbcError* error) {
+ if (!connection->private_data) {
+ SetError(error, "AdbcConnectionSetOptionInt: must AdbcConnectionNew first");
+ return ADBC_STATUS_INVALID_STATE;
+ }
+ if (!connection->private_driver) {
+ // Init not yet called, save the option
+ TempConnection* args = reinterpret_cast(connection->private_data);
+ args->bytes_options[key] = std::string(reinterpret_cast(value), length);
+ return ADBC_STATUS_OK;
+ }
+ INIT_ERROR(error, connection);
+ return connection->private_driver->ConnectionSetOptionBytes(connection, key, value,
+ length, error);
+}
+
+AdbcStatusCode AdbcConnectionSetOptionInt(struct AdbcConnection* connection,
+ const char* key, int64_t value,
+ struct AdbcError* error) {
+ if (!connection->private_data) {
+ SetError(error, "AdbcConnectionSetOptionInt: must AdbcConnectionNew first");
+ return ADBC_STATUS_INVALID_STATE;
+ }
+ if (!connection->private_driver) {
+ // Init not yet called, save the option
+ TempConnection* args = reinterpret_cast(connection->private_data);
+ args->int_options[key] = value;
+ return ADBC_STATUS_OK;
+ }
+ INIT_ERROR(error, connection);
+ return connection->private_driver->ConnectionSetOptionInt(connection, key, value,
+ error);
+}
+
+AdbcStatusCode AdbcConnectionSetOptionDouble(struct AdbcConnection* connection,
+ const char* key, double value,
+ struct AdbcError* error) {
+ if (!connection->private_data) {
+ SetError(error, "AdbcConnectionSetOptionDouble: must AdbcConnectionNew first");
+ return ADBC_STATUS_INVALID_STATE;
+ }
+ if (!connection->private_driver) {
+ // Init not yet called, save the option
+ TempConnection* args = reinterpret_cast(connection->private_data);
+ args->double_options[key] = value;
+ return ADBC_STATUS_OK;
+ }
+ INIT_ERROR(error, connection);
+ return connection->private_driver->ConnectionSetOptionDouble(connection, key, value,
+ error);
+}
+
AdbcStatusCode AdbcStatementBind(struct AdbcStatement* statement,
struct ArrowArray* values, struct ArrowSchema* schema,
struct AdbcError* error) {
if (!statement->private_driver) {
return ADBC_STATUS_INVALID_STATE;
}
+ INIT_ERROR(error, statement);
return statement->private_driver->StatementBind(statement, values, schema, error);
}
@@ -513,9 +1312,19 @@ AdbcStatusCode AdbcStatementBindStream(struct AdbcStatement* statement,
if (!statement->private_driver) {
return ADBC_STATUS_INVALID_STATE;
}
+ INIT_ERROR(error, statement);
return statement->private_driver->StatementBindStream(statement, stream, error);
}
+AdbcStatusCode AdbcStatementCancel(struct AdbcStatement* statement,
+ struct AdbcError* error) {
+ if (!statement->private_driver) {
+ return ADBC_STATUS_INVALID_STATE;
+ }
+ INIT_ERROR(error, statement);
+ return statement->private_driver->StatementCancel(statement, error);
+}
+
// XXX: cpplint gets confused here if declared as 'struct ArrowSchema* schema'
AdbcStatusCode AdbcStatementExecutePartitions(struct AdbcStatement* statement,
ArrowSchema* schema,
@@ -525,6 +1334,7 @@ AdbcStatusCode AdbcStatementExecutePartitions(struct AdbcStatement* statement,
if (!statement->private_driver) {
return ADBC_STATUS_INVALID_STATE;
}
+ INIT_ERROR(error, statement);
return statement->private_driver->StatementExecutePartitions(
statement, schema, partitions, rows_affected, error);
}
@@ -536,8 +1346,62 @@ AdbcStatusCode AdbcStatementExecuteQuery(struct AdbcStatement* statement,
if (!statement->private_driver) {
return ADBC_STATUS_INVALID_STATE;
}
- return statement->private_driver->StatementExecuteQuery(statement, out, rows_affected,
- error);
+ INIT_ERROR(error, statement);
+ WRAP_STREAM(statement->private_driver->StatementExecuteQuery(statement, out,
+ rows_affected, error),
+ out, statement);
+}
+
+AdbcStatusCode AdbcStatementExecuteSchema(struct AdbcStatement* statement,
+ struct ArrowSchema* schema,
+ struct AdbcError* error) {
+ if (!statement->private_driver) {
+ return ADBC_STATUS_INVALID_STATE;
+ }
+ INIT_ERROR(error, statement);
+ return statement->private_driver->StatementExecuteSchema(statement, schema, error);
+}
+
+AdbcStatusCode AdbcStatementGetOption(struct AdbcStatement* statement, const char* key,
+ char* value, size_t* length,
+ struct AdbcError* error) {
+ if (!statement->private_driver) {
+ return ADBC_STATUS_INVALID_STATE;
+ }
+ INIT_ERROR(error, statement);
+ return statement->private_driver->StatementGetOption(statement, key, value, length,
+ error);
+}
+
+AdbcStatusCode AdbcStatementGetOptionBytes(struct AdbcStatement* statement,
+ const char* key, uint8_t* value,
+ size_t* length, struct AdbcError* error) {
+ if (!statement->private_driver) {
+ return ADBC_STATUS_INVALID_STATE;
+ }
+ INIT_ERROR(error, statement);
+ return statement->private_driver->StatementGetOptionBytes(statement, key, value, length,
+ error);
+}
+
+AdbcStatusCode AdbcStatementGetOptionInt(struct AdbcStatement* statement, const char* key,
+ int64_t* value, struct AdbcError* error) {
+ if (!statement->private_driver) {
+ return ADBC_STATUS_INVALID_STATE;
+ }
+ INIT_ERROR(error, statement);
+ return statement->private_driver->StatementGetOptionInt(statement, key, value, error);
+}
+
+AdbcStatusCode AdbcStatementGetOptionDouble(struct AdbcStatement* statement,
+ const char* key, double* value,
+ struct AdbcError* error) {
+ if (!statement->private_driver) {
+ return ADBC_STATUS_INVALID_STATE;
+ }
+ INIT_ERROR(error, statement);
+ return statement->private_driver->StatementGetOptionDouble(statement, key, value,
+ error);
}
AdbcStatusCode AdbcStatementGetParameterSchema(struct AdbcStatement* statement,
@@ -546,6 +1410,7 @@ AdbcStatusCode AdbcStatementGetParameterSchema(struct AdbcStatement* statement,
if (!statement->private_driver) {
return ADBC_STATUS_INVALID_STATE;
}
+ INIT_ERROR(error, statement);
return statement->private_driver->StatementGetParameterSchema(statement, schema, error);
}
@@ -555,6 +1420,7 @@ AdbcStatusCode AdbcStatementNew(struct AdbcConnection* connection,
if (!connection->private_driver) {
return ADBC_STATUS_INVALID_STATE;
}
+ INIT_ERROR(error, connection);
auto status = connection->private_driver->StatementNew(connection, statement, error);
statement->private_driver = connection->private_driver;
return status;
@@ -565,6 +1431,7 @@ AdbcStatusCode AdbcStatementPrepare(struct AdbcStatement* statement,
if (!statement->private_driver) {
return ADBC_STATUS_INVALID_STATE;
}
+ INIT_ERROR(error, statement);
return statement->private_driver->StatementPrepare(statement, error);
}
@@ -573,6 +1440,7 @@ AdbcStatusCode AdbcStatementRelease(struct AdbcStatement* statement,
if (!statement->private_driver) {
return ADBC_STATUS_INVALID_STATE;
}
+ INIT_ERROR(error, statement);
auto status = statement->private_driver->StatementRelease(statement, error);
statement->private_driver = nullptr;
return status;
@@ -583,14 +1451,47 @@ AdbcStatusCode AdbcStatementSetOption(struct AdbcStatement* statement, const cha
if (!statement->private_driver) {
return ADBC_STATUS_INVALID_STATE;
}
+ INIT_ERROR(error, statement);
return statement->private_driver->StatementSetOption(statement, key, value, error);
}
+AdbcStatusCode AdbcStatementSetOptionBytes(struct AdbcStatement* statement,
+ const char* key, const uint8_t* value,
+ size_t length, struct AdbcError* error) {
+ if (!statement->private_driver) {
+ return ADBC_STATUS_INVALID_STATE;
+ }
+ INIT_ERROR(error, statement);
+ return statement->private_driver->StatementSetOptionBytes(statement, key, value, length,
+ error);
+}
+
+AdbcStatusCode AdbcStatementSetOptionInt(struct AdbcStatement* statement, const char* key,
+ int64_t value, struct AdbcError* error) {
+ if (!statement->private_driver) {
+ return ADBC_STATUS_INVALID_STATE;
+ }
+ INIT_ERROR(error, statement);
+ return statement->private_driver->StatementSetOptionInt(statement, key, value, error);
+}
+
+AdbcStatusCode AdbcStatementSetOptionDouble(struct AdbcStatement* statement,
+ const char* key, double value,
+ struct AdbcError* error) {
+ if (!statement->private_driver) {
+ return ADBC_STATUS_INVALID_STATE;
+ }
+ INIT_ERROR(error, statement);
+ return statement->private_driver->StatementSetOptionDouble(statement, key, value,
+ error);
+}
+
AdbcStatusCode AdbcStatementSetSqlQuery(struct AdbcStatement* statement,
const char* query, struct AdbcError* error) {
if (!statement->private_driver) {
return ADBC_STATUS_INVALID_STATE;
}
+ INIT_ERROR(error, statement);
return statement->private_driver->StatementSetSqlQuery(statement, query, error);
}
@@ -600,6 +1501,7 @@ AdbcStatusCode AdbcStatementSetSubstraitPlan(struct AdbcStatement* statement,
if (!statement->private_driver) {
return ADBC_STATUS_INVALID_STATE;
}
+ INIT_ERROR(error, statement);
return statement->private_driver->StatementSetSubstraitPlan(statement, plan, length,
error);
}
@@ -636,137 +1538,80 @@ AdbcStatusCode AdbcLoadDriver(const char* driver_name, const char* entrypoint,
AdbcDriverInitFunc init_func;
std::string error_message;
- if (version != ADBC_VERSION_1_0_0) {
- SetError(error, "Only ADBC 1.0.0 is supported");
- return ADBC_STATUS_NOT_IMPLEMENTED;
- }
-
- auto* driver = reinterpret_cast(raw_driver);
-
- if (!entrypoint) {
- // Default entrypoint (see adbc.h)
- entrypoint = "AdbcDriverInit";
+ switch (version) {
+ case ADBC_VERSION_1_0_0:
+ case ADBC_VERSION_1_1_0:
+ break;
+ default:
+ SetError(error, "Only ADBC 1.0.0 and 1.1.0 are supported");
+ return ADBC_STATUS_NOT_IMPLEMENTED;
}
-#if defined(_WIN32)
-
- HMODULE handle = LoadLibraryExA(driver_name, NULL, 0);
- if (!handle) {
- error_message += driver_name;
- error_message += ": LoadLibraryExA() failed: ";
- GetWinError(&error_message);
-
- std::string full_driver_name = driver_name;
- full_driver_name += ".lib";
- handle = LoadLibraryExA(full_driver_name.c_str(), NULL, 0);
- if (!handle) {
- error_message += '\n';
- error_message += full_driver_name;
- error_message += ": LoadLibraryExA() failed: ";
- GetWinError(&error_message);
- }
- }
- if (!handle) {
- SetError(error, error_message);
- return ADBC_STATUS_INTERNAL;
+ if (!raw_driver) {
+ SetError(error, "Must provide non-NULL raw_driver");
+ return ADBC_STATUS_INVALID_ARGUMENT;
}
+ auto* driver = reinterpret_cast(raw_driver);
- void* load_handle = reinterpret_cast(GetProcAddress(handle, entrypoint));
- init_func = reinterpret_cast(load_handle);
- if (!init_func) {
- std::string message = "GetProcAddress(";
- message += entrypoint;
- message += ") failed: ";
- GetWinError(&message);
- if (!FreeLibrary(handle)) {
- message += "\nFreeLibrary() failed: ";
- GetWinError(&message);
- }
- SetError(error, message);
- return ADBC_STATUS_INTERNAL;
+ ManagedLibrary library;
+ AdbcStatusCode status = library.Load(driver_name, error);
+ if (status != ADBC_STATUS_OK) {
+ // AdbcDatabaseInit tries to call this if set
+ driver->release = nullptr;
+ return status;
}
-#else
-
-#if defined(__APPLE__)
- static const std::string kPlatformLibraryPrefix = "lib";
- static const std::string kPlatformLibrarySuffix = ".dylib";
-#else
- static const std::string kPlatformLibraryPrefix = "lib";
- static const std::string kPlatformLibrarySuffix = ".so";
-#endif // defined(__APPLE__)
-
- void* handle = dlopen(driver_name, RTLD_NOW | RTLD_LOCAL);
- if (!handle) {
- error_message = "dlopen() failed: ";
- error_message += dlerror();
-
- // If applicable, append the shared library prefix/extension and
- // try again (this way you don't have to hardcode driver names by
- // platform in the application)
- const std::string driver_str = driver_name;
-
- std::string full_driver_name;
- if (driver_str.size() < kPlatformLibraryPrefix.size() ||
- driver_str.compare(0, kPlatformLibraryPrefix.size(), kPlatformLibraryPrefix) !=
- 0) {
- full_driver_name += kPlatformLibraryPrefix;
- }
- full_driver_name += driver_name;
- if (driver_str.size() < kPlatformLibrarySuffix.size() ||
- driver_str.compare(full_driver_name.size() - kPlatformLibrarySuffix.size(),
- kPlatformLibrarySuffix.size(), kPlatformLibrarySuffix) != 0) {
- full_driver_name += kPlatformLibrarySuffix;
- }
- handle = dlopen(full_driver_name.c_str(), RTLD_NOW | RTLD_LOCAL);
- if (!handle) {
- error_message += "\ndlopen() failed: ";
- error_message += dlerror();
+ void* load_handle = nullptr;
+ if (entrypoint) {
+ status = library.Lookup(entrypoint, &load_handle, error);
+ } else {
+ auto name = AdbcDriverManagerDefaultEntrypoint(driver_name);
+ status = library.Lookup(name.c_str(), &load_handle, error);
+ if (status != ADBC_STATUS_OK) {
+ status = library.Lookup(kDefaultEntrypoint, &load_handle, error);
}
}
- if (!handle) {
- SetError(error, error_message);
- // AdbcDatabaseInit tries to call this if set
- driver->release = nullptr;
- return ADBC_STATUS_INTERNAL;
- }
- void* load_handle = dlsym(handle, entrypoint);
- if (!load_handle) {
- std::string message = "dlsym(";
- message += entrypoint;
- message += ") failed: ";
- message += dlerror();
- SetError(error, message);
- return ADBC_STATUS_INTERNAL;
+ if (status != ADBC_STATUS_OK) {
+ library.Release();
+ return status;
}
init_func = reinterpret_cast(load_handle);
-#endif // defined(_WIN32)
-
- AdbcStatusCode status = AdbcLoadDriverFromInitFunc(init_func, version, driver, error);
+ status = AdbcLoadDriverFromInitFunc(init_func, version, driver, error);
if (status == ADBC_STATUS_OK) {
ManagerDriverState* state = new ManagerDriverState;
state->driver_release = driver->release;
-#if defined(_WIN32)
- state->handle = handle;
-#endif // defined(_WIN32)
+ state->handle = std::move(library);
driver->release = &ReleaseDriver;
driver->private_manager = state;
} else {
-#if defined(_WIN32)
- if (!FreeLibrary(handle)) {
- std::string message = "FreeLibrary() failed: ";
- GetWinError(&message);
- SetError(error, message);
- }
-#endif // defined(_WIN32)
+ library.Release();
}
return status;
}
AdbcStatusCode AdbcLoadDriverFromInitFunc(AdbcDriverInitFunc init_func, int version,
void* raw_driver, struct AdbcError* error) {
+ constexpr std::array kSupportedVersions = {
+ ADBC_VERSION_1_1_0,
+ ADBC_VERSION_1_0_0,
+ };
+
+ if (!raw_driver) {
+ SetError(error, "Must provide non-NULL raw_driver");
+ return ADBC_STATUS_INVALID_ARGUMENT;
+ }
+
+ switch (version) {
+ case ADBC_VERSION_1_0_0:
+ case ADBC_VERSION_1_1_0:
+ break;
+ default:
+ SetError(error, "Only ADBC 1.0.0 and 1.1.0 are supported");
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+ }
+
#define FILL_DEFAULT(DRIVER, STUB) \
if (!DRIVER->STUB) { \
DRIVER->STUB = &STUB; \
@@ -777,12 +1622,20 @@ AdbcStatusCode AdbcLoadDriverFromInitFunc(AdbcDriverInitFunc init_func, int vers
return ADBC_STATUS_INTERNAL; \
}
- auto result = init_func(version, raw_driver, error);
+ // Starting from the passed version, try each (older) version in
+ // succession with the underlying driver until we find one that's
+ // accepted.
+ AdbcStatusCode result = ADBC_STATUS_NOT_IMPLEMENTED;
+ for (const int try_version : kSupportedVersions) {
+ if (try_version > version) continue;
+ result = init_func(try_version, raw_driver, error);
+ if (result != ADBC_STATUS_NOT_IMPLEMENTED) break;
+ }
if (result != ADBC_STATUS_OK) {
return result;
}
- if (version == ADBC_VERSION_1_0_0) {
+ if (version >= ADBC_VERSION_1_0_0) {
auto* driver = reinterpret_cast(raw_driver);
CHECK_REQUIRED(driver, DatabaseNew);
CHECK_REQUIRED(driver, DatabaseInit);
@@ -812,6 +1665,41 @@ AdbcStatusCode AdbcLoadDriverFromInitFunc(AdbcDriverInitFunc init_func, int vers
FILL_DEFAULT(driver, StatementSetSqlQuery);
FILL_DEFAULT(driver, StatementSetSubstraitPlan);
}
+ if (version >= ADBC_VERSION_1_1_0) {
+ auto* driver = reinterpret_cast(raw_driver);
+ FILL_DEFAULT(driver, ErrorGetDetailCount);
+ FILL_DEFAULT(driver, ErrorGetDetail);
+ FILL_DEFAULT(driver, ErrorFromArrayStream);
+
+ FILL_DEFAULT(driver, DatabaseGetOption);
+ FILL_DEFAULT(driver, DatabaseGetOptionBytes);
+ FILL_DEFAULT(driver, DatabaseGetOptionDouble);
+ FILL_DEFAULT(driver, DatabaseGetOptionInt);
+ FILL_DEFAULT(driver, DatabaseSetOptionBytes);
+ FILL_DEFAULT(driver, DatabaseSetOptionDouble);
+ FILL_DEFAULT(driver, DatabaseSetOptionInt);
+
+ FILL_DEFAULT(driver, ConnectionCancel);
+ FILL_DEFAULT(driver, ConnectionGetOption);
+ FILL_DEFAULT(driver, ConnectionGetOptionBytes);
+ FILL_DEFAULT(driver, ConnectionGetOptionDouble);
+ FILL_DEFAULT(driver, ConnectionGetOptionInt);
+ FILL_DEFAULT(driver, ConnectionGetStatistics);
+ FILL_DEFAULT(driver, ConnectionGetStatisticNames);
+ FILL_DEFAULT(driver, ConnectionSetOptionBytes);
+ FILL_DEFAULT(driver, ConnectionSetOptionDouble);
+ FILL_DEFAULT(driver, ConnectionSetOptionInt);
+
+ FILL_DEFAULT(driver, StatementCancel);
+ FILL_DEFAULT(driver, StatementExecuteSchema);
+ FILL_DEFAULT(driver, StatementGetOption);
+ FILL_DEFAULT(driver, StatementGetOptionBytes);
+ FILL_DEFAULT(driver, StatementGetOptionDouble);
+ FILL_DEFAULT(driver, StatementGetOptionInt);
+ FILL_DEFAULT(driver, StatementSetOptionBytes);
+ FILL_DEFAULT(driver, StatementSetOptionDouble);
+ FILL_DEFAULT(driver, StatementSetOptionInt);
+ }
return ADBC_STATUS_OK;
diff --git a/c/driver_manager/adbc_driver_manager_test.cc b/c/driver_manager/adbc_driver_manager_test.cc
index d3ff6f58e1..5e70390388 100644
--- a/c/driver_manager/adbc_driver_manager_test.cc
+++ b/c/driver_manager/adbc_driver_manager_test.cc
@@ -27,6 +27,8 @@
#include "validation/adbc_validation.h"
#include "validation/adbc_validation_util.h"
+std::string AdbcDriverManagerDefaultEntrypoint(const std::string& filename);
+
// Tests of the SQLite example driver, except using the driver manager
namespace adbc {
@@ -40,7 +42,7 @@ class DriverManager : public ::testing::Test {
std::memset(&driver, 0, sizeof(driver));
std::memset(&error, 0, sizeof(error));
- ASSERT_THAT(AdbcLoadDriver("adbc_driver_sqlite", nullptr, ADBC_VERSION_1_0_0, &driver,
+ ASSERT_THAT(AdbcLoadDriver("adbc_driver_sqlite", nullptr, ADBC_VERSION_1_1_0, &driver,
&error),
IsOkStatus(&error));
}
@@ -191,7 +193,27 @@ class SqliteQuirks : public adbc_validation::DriverQuirks {
}
}
+ bool supports_bulk_ingest(const char* mode) const override {
+ return std::strcmp(mode, ADBC_INGEST_OPTION_MODE_APPEND) == 0 ||
+ std::strcmp(mode, ADBC_INGEST_OPTION_MODE_CREATE) == 0;
+ }
bool supports_concurrent_statements() const override { return true; }
+ bool supports_get_option() const override { return false; }
+ std::optional supports_get_sql_info(
+ uint32_t info_code) const override {
+ switch (info_code) {
+ case ADBC_INFO_DRIVER_NAME:
+ return "ADBC SQLite Driver";
+ case ADBC_INFO_DRIVER_VERSION:
+ return "(unknown)";
+ case ADBC_INFO_VENDOR_NAME:
+ return "SQLite";
+ case ADBC_INFO_VENDOR_VERSION:
+ return "3.";
+ default:
+ return std::nullopt;
+ }
+ }
};
class SqliteDatabaseTest : public ::testing::Test, public adbc_validation::DatabaseTest {
@@ -242,4 +264,41 @@ class SqliteStatementTest : public ::testing::Test,
};
ADBCV_TEST_STATEMENT(SqliteStatementTest)
+TEST(AdbcDriverManagerInternal, AdbcDriverManagerDefaultEntrypoint) {
+ for (const auto& driver : {
+ "adbc_driver_sqlite",
+ "adbc_driver_sqlite.dll",
+ "driver_sqlite",
+ "libadbc_driver_sqlite",
+ "libadbc_driver_sqlite.so",
+ "libadbc_driver_sqlite.so.6.0.0",
+ "/usr/lib/libadbc_driver_sqlite.so",
+ "/usr/lib/libadbc_driver_sqlite.so.6.0.0",
+ "C:\\System32\\adbc_driver_sqlite.dll",
+ }) {
+ SCOPED_TRACE(driver);
+ EXPECT_EQ("AdbcDriverSqliteInit", ::AdbcDriverManagerDefaultEntrypoint(driver));
+ }
+
+ for (const auto& driver : {
+ "adbc_sqlite",
+ "sqlite",
+ "/usr/lib/sqlite.so",
+ "C:\\System32\\sqlite.dll",
+ }) {
+ SCOPED_TRACE(driver);
+ EXPECT_EQ("AdbcSqliteInit", ::AdbcDriverManagerDefaultEntrypoint(driver));
+ }
+
+ for (const auto& driver : {
+ "proprietary_engine",
+ "libproprietary_engine.so.6.0.0",
+ "/usr/lib/proprietary_engine.so",
+ "C:\\System32\\proprietary_engine.dll",
+ }) {
+ SCOPED_TRACE(driver);
+ EXPECT_EQ("AdbcProprietaryEngineInit", ::AdbcDriverManagerDefaultEntrypoint(driver));
+ }
+}
+
} // namespace adbc
diff --git a/c/driver_manager/adbc_version_100.c b/c/driver_manager/adbc_version_100.c
new file mode 100644
index 0000000000..48114cdb43
--- /dev/null
+++ b/c/driver_manager/adbc_version_100.c
@@ -0,0 +1,117 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include "adbc_version_100.h"
+
+#include
+
+struct Version100Database {
+ int dummy;
+};
+
+static struct Version100Database kDatabase;
+
+struct Version100Connection {
+ int dummy;
+};
+
+static struct Version100Connection kConnection;
+
+struct Version100Statement {
+ int dummy;
+};
+
+static struct Version100Statement kStatement;
+
+AdbcStatusCode Version100DatabaseInit(struct AdbcDatabase* database,
+ struct AdbcError* error) {
+ return ADBC_STATUS_OK;
+}
+
+AdbcStatusCode Version100DatabaseNew(struct AdbcDatabase* database,
+ struct AdbcError* error) {
+ database->private_data = &kDatabase;
+ return ADBC_STATUS_OK;
+}
+
+AdbcStatusCode Version100DatabaseRelease(struct AdbcDatabase* database,
+ struct AdbcError* error) {
+ database->private_data = NULL;
+ return ADBC_STATUS_OK;
+}
+
+AdbcStatusCode Version100ConnectionInit(struct AdbcConnection* connection,
+ struct AdbcDatabase* database,
+ struct AdbcError* error) {
+ return ADBC_STATUS_OK;
+}
+
+AdbcStatusCode Version100ConnectionNew(struct AdbcConnection* connection,
+ struct AdbcError* error) {
+ connection->private_data = &kConnection;
+ return ADBC_STATUS_OK;
+}
+
+AdbcStatusCode Version100StatementExecuteQuery(struct AdbcStatement* statement,
+ struct ArrowArrayStream* stream,
+ int64_t* rows_affected,
+ struct AdbcError* error) {
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+}
+
+AdbcStatusCode Version100StatementNew(struct AdbcConnection* connection,
+ struct AdbcStatement* statement,
+ struct AdbcError* error) {
+ statement->private_data = &kStatement;
+ return ADBC_STATUS_OK;
+}
+
+AdbcStatusCode Version100StatementRelease(struct AdbcStatement* statement,
+ struct AdbcError* error) {
+ statement->private_data = NULL;
+ return ADBC_STATUS_OK;
+}
+
+AdbcStatusCode Version100ConnectionRelease(struct AdbcConnection* connection,
+ struct AdbcError* error) {
+ connection->private_data = NULL;
+ return ADBC_STATUS_OK;
+}
+
+AdbcStatusCode Version100DriverInit(int version, void* raw_driver,
+ struct AdbcError* error) {
+ if (version != ADBC_VERSION_1_0_0) {
+ return ADBC_STATUS_NOT_IMPLEMENTED;
+ }
+
+ struct AdbcDriverVersion100* driver = (struct AdbcDriverVersion100*)raw_driver;
+ memset(driver, 0, sizeof(struct AdbcDriverVersion100));
+
+ driver->DatabaseInit = &Version100DatabaseInit;
+ driver->DatabaseNew = &Version100DatabaseNew;
+ driver->DatabaseRelease = &Version100DatabaseRelease;
+
+ driver->ConnectionInit = &Version100ConnectionInit;
+ driver->ConnectionNew = &Version100ConnectionNew;
+ driver->ConnectionRelease = &Version100ConnectionRelease;
+
+ driver->StatementExecuteQuery = &Version100StatementExecuteQuery;
+ driver->StatementNew = &Version100StatementNew;
+ driver->StatementRelease = &Version100StatementRelease;
+
+ return ADBC_STATUS_OK;
+}
diff --git a/c/driver_manager/adbc_version_100.h b/c/driver_manager/adbc_version_100.h
new file mode 100644
index 0000000000..b349f86f73
--- /dev/null
+++ b/c/driver_manager/adbc_version_100.h
@@ -0,0 +1,94 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// A dummy version 1.0.0 ADBC driver to test compatibility.
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct AdbcErrorVersion100 {
+ char* message;
+ int32_t vendor_code;
+ char sqlstate[5];
+ void (*release)(struct AdbcError* error);
+};
+
+struct AdbcDriverVersion100 {
+ void* private_data;
+ void* private_manager;
+ AdbcStatusCode (*release)(struct AdbcDriver* driver, struct AdbcError* error);
+
+ AdbcStatusCode (*DatabaseInit)(struct AdbcDatabase*, struct AdbcError*);
+ AdbcStatusCode (*DatabaseNew)(struct AdbcDatabase*, struct AdbcError*);
+ AdbcStatusCode (*DatabaseSetOption)(struct AdbcDatabase*, const char*, const char*,
+ struct AdbcError*);
+ AdbcStatusCode (*DatabaseRelease)(struct AdbcDatabase*, struct AdbcError*);
+
+ AdbcStatusCode (*ConnectionCommit)(struct AdbcConnection*, struct AdbcError*);
+ AdbcStatusCode (*ConnectionGetInfo)(struct AdbcConnection*, uint32_t*, size_t,
+ struct ArrowArrayStream*, struct AdbcError*);
+ AdbcStatusCode (*ConnectionGetObjects)(struct AdbcConnection*, int, const char*,
+ const char*, const char*, const char**,
+ const char*, struct ArrowArrayStream*,
+ struct AdbcError*);
+ AdbcStatusCode (*ConnectionGetTableSchema)(struct AdbcConnection*, const char*,
+ const char*, const char*,
+ struct ArrowSchema*, struct AdbcError*);
+ AdbcStatusCode (*ConnectionGetTableTypes)(struct AdbcConnection*,
+ struct ArrowArrayStream*, struct AdbcError*);
+ AdbcStatusCode (*ConnectionInit)(struct AdbcConnection*, struct AdbcDatabase*,
+ struct AdbcError*);
+ AdbcStatusCode (*ConnectionNew)(struct AdbcConnection*, struct AdbcError*);
+ AdbcStatusCode (*ConnectionSetOption)(struct AdbcConnection*, const char*, const char*,
+ struct AdbcError*);
+ AdbcStatusCode (*ConnectionReadPartition)(struct AdbcConnection*, const uint8_t*,
+ size_t, struct ArrowArrayStream*,
+ struct AdbcError*);
+ AdbcStatusCode (*ConnectionRelease)(struct AdbcConnection*, struct AdbcError*);
+ AdbcStatusCode (*ConnectionRollback)(struct AdbcConnection*, struct AdbcError*);
+
+ AdbcStatusCode (*StatementBind)(struct AdbcStatement*, struct ArrowArray*,
+ struct ArrowSchema*, struct AdbcError*);
+ AdbcStatusCode (*StatementBindStream)(struct AdbcStatement*, struct ArrowArrayStream*,
+ struct AdbcError*);
+ AdbcStatusCode (*StatementExecuteQuery)(struct AdbcStatement*, struct ArrowArrayStream*,
+ int64_t*, struct AdbcError*);
+ AdbcStatusCode (*StatementExecutePartitions)(struct AdbcStatement*, struct ArrowSchema*,
+ struct AdbcPartitions*, int64_t*,
+ struct AdbcError*);
+ AdbcStatusCode (*StatementGetParameterSchema)(struct AdbcStatement*,
+ struct ArrowSchema*, struct AdbcError*);
+ AdbcStatusCode (*StatementNew)(struct AdbcConnection*, struct AdbcStatement*,
+ struct AdbcError*);
+ AdbcStatusCode (*StatementPrepare)(struct AdbcStatement*, struct AdbcError*);
+ AdbcStatusCode (*StatementRelease)(struct AdbcStatement*, struct AdbcError*);
+ AdbcStatusCode (*StatementSetOption)(struct AdbcStatement*, const char*, const char*,
+ struct AdbcError*);
+ AdbcStatusCode (*StatementSetSqlQuery)(struct AdbcStatement*, const char*,
+ struct AdbcError*);
+ AdbcStatusCode (*StatementSetSubstraitPlan)(struct AdbcStatement*, const uint8_t*,
+ size_t, struct AdbcError*);
+};
+
+AdbcStatusCode Version100DriverInit(int version, void* driver, struct AdbcError* error);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/c/driver_manager/adbc_version_100_compatibility_test.cc b/c/driver_manager/adbc_version_100_compatibility_test.cc
new file mode 100644
index 0000000000..27e5f5d997
--- /dev/null
+++ b/c/driver_manager/adbc_version_100_compatibility_test.cc
@@ -0,0 +1,111 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include
+#include
+
+#include
+
+#include "adbc.h"
+#include "adbc_driver_manager.h"
+#include "adbc_version_100.h"
+#include "validation/adbc_validation_util.h"
+
+namespace adbc {
+
+using adbc_validation::IsOkStatus;
+using adbc_validation::IsStatus;
+
+class AdbcVersion : public ::testing::Test {
+ public:
+ void SetUp() override {
+ std::memset(&driver, 0, sizeof(driver));
+ std::memset(&error, 0, sizeof(error));
+ }
+
+ void TearDown() override {
+ if (error.release) {
+ error.release(&error);
+ }
+
+ if (driver.release) {
+ ASSERT_THAT(driver.release(&driver, &error), IsOkStatus(&error));
+ ASSERT_EQ(driver.private_data, nullptr);
+ ASSERT_EQ(driver.private_manager, nullptr);
+ }
+ }
+
+ protected:
+ struct AdbcDriver driver = {};
+ struct AdbcError error = {};
+};
+
+TEST_F(AdbcVersion, StructSize) {
+ ASSERT_EQ(sizeof(AdbcErrorVersion100), ADBC_ERROR_1_0_0_SIZE);
+ ASSERT_EQ(sizeof(AdbcError), ADBC_ERROR_1_1_0_SIZE);
+
+ ASSERT_EQ(sizeof(AdbcDriverVersion100), ADBC_DRIVER_1_0_0_SIZE);
+ ASSERT_EQ(sizeof(AdbcDriver), ADBC_DRIVER_1_1_0_SIZE);
+}
+
+// Initialize a version 1.0.0 driver with the version 1.1.0 driver struct.
+TEST_F(AdbcVersion, OldDriverNewLayout) {
+ ASSERT_THAT(Version100DriverInit(ADBC_VERSION_1_1_0, &driver, &error),
+ IsStatus(ADBC_STATUS_NOT_IMPLEMENTED, &error));
+
+ ASSERT_THAT(Version100DriverInit(ADBC_VERSION_1_0_0, &driver, &error),
+ IsOkStatus(&error));
+}
+
+// Initialize a version 1.0.0 driver with the new driver manager/new version.
+TEST_F(AdbcVersion, OldDriverNewManager) {
+ ASSERT_THAT(AdbcLoadDriverFromInitFunc(&Version100DriverInit, ADBC_VERSION_1_1_0,
+ &driver, &error),
+ IsOkStatus(&error));
+
+ EXPECT_NE(driver.ErrorGetDetailCount, nullptr);
+ EXPECT_NE(driver.ErrorGetDetail, nullptr);
+
+ EXPECT_NE(driver.DatabaseGetOption, nullptr);
+ EXPECT_NE(driver.DatabaseGetOptionBytes, nullptr);
+ EXPECT_NE(driver.DatabaseGetOptionDouble, nullptr);
+ EXPECT_NE(driver.DatabaseGetOptionInt, nullptr);
+ EXPECT_NE(driver.DatabaseSetOptionInt, nullptr);
+ EXPECT_NE(driver.DatabaseSetOptionDouble, nullptr);
+
+ EXPECT_NE(driver.ConnectionCancel, nullptr);
+ EXPECT_NE(driver.ConnectionGetOption, nullptr);
+ EXPECT_NE(driver.ConnectionGetOptionBytes, nullptr);
+ EXPECT_NE(driver.ConnectionGetOptionDouble, nullptr);
+ EXPECT_NE(driver.ConnectionGetOptionInt, nullptr);
+ EXPECT_NE(driver.ConnectionSetOptionInt, nullptr);
+ EXPECT_NE(driver.ConnectionSetOptionDouble, nullptr);
+
+ EXPECT_NE(driver.StatementCancel, nullptr);
+ EXPECT_NE(driver.StatementExecuteSchema, nullptr);
+ EXPECT_NE(driver.StatementGetOption, nullptr);
+ EXPECT_NE(driver.StatementGetOptionBytes, nullptr);
+ EXPECT_NE(driver.StatementGetOptionDouble, nullptr);
+ EXPECT_NE(driver.StatementGetOptionInt, nullptr);
+ EXPECT_NE(driver.StatementSetOptionInt, nullptr);
+ EXPECT_NE(driver.StatementSetOptionDouble, nullptr);
+}
+
+// N.B. see postgresql_test.cc for backwards compatibility test of AdbcError
+// N.B. see postgresql_test.cc for backwards compatibility test of AdbcDriver
+
+} // namespace adbc
diff --git a/c/integration/duckdb/CMakeLists.txt b/c/integration/duckdb/CMakeLists.txt
index 52fb9d0f8c..8053713f3d 100644
--- a/c/integration/duckdb/CMakeLists.txt
+++ b/c/integration/duckdb/CMakeLists.txt
@@ -49,6 +49,7 @@ if(ADBC_BUILD_TESTS)
CACHE INTERNAL "Disable UBSAN")
# Force cmake to honor our options here in the subproject
cmake_policy(SET CMP0077 NEW)
+ message(STATUS "Fetching DuckDB")
fetchcontent_makeavailable(duckdb)
include_directories(SYSTEM ${REPOSITORY_ROOT})
diff --git a/c/integration/duckdb/duckdb_test.cc b/c/integration/duckdb/duckdb_test.cc
index fd6e1984e6..c42c3813cf 100644
--- a/c/integration/duckdb/duckdb_test.cc
+++ b/c/integration/duckdb/duckdb_test.cc
@@ -46,7 +46,7 @@ class DuckDbQuirks : public adbc_validation::DriverQuirks {
std::string BindParameter(int index) const override { return "?"; }
- bool supports_bulk_ingest() const override { return false; }
+ bool supports_bulk_ingest(const char* /*mode*/) const override { return false; }
bool supports_concurrent_statements() const override { return true; }
bool supports_dynamic_parameter_binding() const override { return false; }
bool supports_get_sql_info() const override { return false; }
@@ -96,6 +96,12 @@ class DuckDbStatementTest : public ::testing::Test,
void TestSqlIngestTableEscaping() { GTEST_SKIP() << "Table escaping not implemented"; }
+ void TestSqlQueryErrors() { GTEST_SKIP() << "DuckDB does not set AdbcError.release"; }
+
+ void TestErrorCompatibility() {
+ GTEST_SKIP() << "DuckDB does not set AdbcError.release";
+ }
+
protected:
DuckDbQuirks quirks_;
};
diff --git a/c/symbols.map b/c/symbols.map
index 5e965b355e..c9464b2da4 100644
--- a/c/symbols.map
+++ b/c/symbols.map
@@ -20,6 +20,16 @@
# Only expose symbols from the ADBC API
Adbc*;
+ # Expose driver-specific initialization routines
+ FlightSQLDriverInit;
+ PostgresqlDriverInit;
+ SnowflakeDriverInit;
+ SqliteDriverInit;
+
+ extern "C++" {
+ Adbc*;
+ };
+
local:
*;
};
diff --git a/c/validation/CMakeLists.txt b/c/validation/CMakeLists.txt
index 2f6549b5e7..bab7a63b19 100644
--- a/c/validation/CMakeLists.txt
+++ b/c/validation/CMakeLists.txt
@@ -15,11 +15,24 @@
# specific language governing permissions and limitations
# under the License.
-add_library(adbc_validation OBJECT adbc_validation.cc adbc_validation_util.cc)
+add_library(adbc_validation_util STATIC adbc_validation_util.cc)
+adbc_configure_target(adbc_validation_util)
+target_compile_features(adbc_validation_util PRIVATE cxx_std_17)
+target_include_directories(adbc_validation_util SYSTEM
+ PRIVATE "${REPOSITORY_ROOT}" "${REPOSITORY_ROOT}/c/driver/"
+ "${REPOSITORY_ROOT}/c/vendor/")
+target_link_libraries(adbc_validation_util PUBLIC adbc_driver_common nanoarrow
+ GTest::gtest GTest::gmock)
+
+add_library(adbc_validation OBJECT adbc_validation.cc)
adbc_configure_target(adbc_validation)
target_compile_features(adbc_validation PRIVATE cxx_std_17)
target_include_directories(adbc_validation SYSTEM
PRIVATE "${REPOSITORY_ROOT}" "${REPOSITORY_ROOT}/c/driver/"
"${REPOSITORY_ROOT}/c/vendor/")
-target_link_libraries(adbc_validation PUBLIC adbc_driver_common nanoarrow GTest::gtest
- GTest::gmock)
+target_link_libraries(adbc_validation
+ PUBLIC adbc_driver_common
+ adbc_validation_util
+ nanoarrow
+ GTest::gtest
+ GTest::gmock)
diff --git a/c/validation/adbc_validation.cc b/c/validation/adbc_validation.cc
index 8f519b0417..afb0260a63 100644
--- a/c/validation/adbc_validation.cc
+++ b/c/validation/adbc_validation.cc
@@ -26,6 +26,7 @@
#include
#include
#include
+#include
#include
#include
@@ -33,6 +34,7 @@
#include
#include
#include
+#include
#include "adbc_validation_util.h"
@@ -101,7 +103,7 @@ AdbcStatusCode DriverQuirks::EnsureSampleTable(struct AdbcConnection* connection
AdbcStatusCode DriverQuirks::CreateSampleTable(struct AdbcConnection* connection,
const std::string& name,
struct AdbcError* error) const {
- if (!supports_bulk_ingest()) {
+ if (!supports_bulk_ingest(ADBC_INGEST_OPTION_MODE_CREATE)) {
return ADBC_STATUS_NOT_IMPLEMENTED;
}
return DoIngestSampleTable(connection, name, error);
@@ -247,6 +249,56 @@ void ConnectionTest::TestAutocommitToggle() {
//------------------------------------------------------------
// Tests of metadata
+std::optional ConnectionGetOption(struct AdbcConnection* connection,
+ std::string_view option,
+ struct AdbcError* error) {
+ char buffer[128];
+ size_t buffer_size = sizeof(buffer);
+ AdbcStatusCode status =
+ AdbcConnectionGetOption(connection, option.data(), buffer, &buffer_size, error);
+ EXPECT_THAT(status, IsOkStatus(error));
+ if (status != ADBC_STATUS_OK) return std::nullopt;
+ EXPECT_GT(buffer_size, 0);
+ if (buffer_size == 0) return std::nullopt;
+ return std::string(buffer, buffer_size - 1);
+}
+
+void ConnectionTest::TestMetadataCurrentCatalog() {
+ ASSERT_THAT(AdbcConnectionNew(&connection, &error), IsOkStatus(&error));
+ ASSERT_THAT(AdbcConnectionInit(&connection, &database, &error), IsOkStatus(&error));
+
+ if (quirks()->supports_metadata_current_catalog()) {
+ ASSERT_THAT(
+ ConnectionGetOption(&connection, ADBC_CONNECTION_OPTION_CURRENT_CATALOG, &error),
+ ::testing::Optional(quirks()->catalog()));
+ } else {
+ char buffer[128];
+ size_t buffer_size = sizeof(buffer);
+ ASSERT_THAT(
+ AdbcConnectionGetOption(&connection, ADBC_CONNECTION_OPTION_CURRENT_CATALOG,
+ buffer, &buffer_size, &error),
+ IsStatus(ADBC_STATUS_NOT_FOUND));
+ }
+}
+
+void ConnectionTest::TestMetadataCurrentDbSchema() {
+ ASSERT_THAT(AdbcConnectionNew(&connection, &error), IsOkStatus(&error));
+ ASSERT_THAT(AdbcConnectionInit(&connection, &database, &error), IsOkStatus(&error));
+
+ if (quirks()->supports_metadata_current_db_schema()) {
+ ASSERT_THAT(ConnectionGetOption(&connection, ADBC_CONNECTION_OPTION_CURRENT_DB_SCHEMA,
+ &error),
+ ::testing::Optional(quirks()->db_schema()));
+ } else {
+ char buffer[128];
+ size_t buffer_size = sizeof(buffer);
+ ASSERT_THAT(
+ AdbcConnectionGetOption(&connection, ADBC_CONNECTION_OPTION_CURRENT_DB_SCHEMA,
+ buffer, &buffer_size, &error),
+ IsStatus(ADBC_STATUS_NOT_FOUND));
+ }
+}
+
void ConnectionTest::TestMetadataGetInfo() {
ASSERT_THAT(AdbcConnectionNew(&connection, &error), IsOkStatus(&error));
ASSERT_THAT(AdbcConnectionInit(&connection, &database, &error), IsOkStatus(&error));
@@ -255,83 +307,110 @@ void ConnectionTest::TestMetadataGetInfo() {
GTEST_SKIP();
}
- StreamReader reader;
- std::vector info = {
- ADBC_INFO_DRIVER_NAME,
- ADBC_INFO_DRIVER_VERSION,
- ADBC_INFO_VENDOR_NAME,
- ADBC_INFO_VENDOR_VERSION,
- };
+ for (uint32_t info_code : {
+ ADBC_INFO_DRIVER_NAME,
+ ADBC_INFO_DRIVER_VERSION,
+ ADBC_INFO_DRIVER_ADBC_VERSION,
+ ADBC_INFO_VENDOR_NAME,
+ ADBC_INFO_VENDOR_VERSION,
+ }) {
+ SCOPED_TRACE("info_code = " + std::to_string(info_code));
+ std::optional expected = quirks()->supports_get_sql_info(info_code);
- ASSERT_THAT(AdbcConnectionGetInfo(&connection, info.data(), info.size(),
- &reader.stream.value, &error),
- IsOkStatus(&error));
- ASSERT_NO_FATAL_FAILURE(reader.GetSchema());
- ASSERT_NO_FATAL_FAILURE(CompareSchema(
- &reader.schema.value, {
- {"info_name", NANOARROW_TYPE_UINT32, NOT_NULL},
- {"info_value", NANOARROW_TYPE_DENSE_UNION, NULLABLE},
- }));
- ASSERT_NO_FATAL_FAILURE(
- CompareSchema(reader.schema->children[1],
- {
- {"string_value", NANOARROW_TYPE_STRING, NULLABLE},
- {"bool_value", NANOARROW_TYPE_BOOL, NULLABLE},
- {"int64_value", NANOARROW_TYPE_INT64, NULLABLE},
- {"int32_bitmask", NANOARROW_TYPE_INT32, NULLABLE},
- {"string_list", NANOARROW_TYPE_LIST, NULLABLE},
- {"int32_to_int32_list_map", NANOARROW_TYPE_MAP, NULLABLE},
- }));
- ASSERT_NO_FATAL_FAILURE(CompareSchema(reader.schema->children[1]->children[4],
- {
- {"item", NANOARROW_TYPE_STRING, NULLABLE},
- }));
- ASSERT_NO_FATAL_FAILURE(CompareSchema(reader.schema->children[1]->children[5],
- {
- {"entries", NANOARROW_TYPE_STRUCT, NOT_NULL},
- }));
- ASSERT_NO_FATAL_FAILURE(
- CompareSchema(reader.schema->children[1]->children[5]->children[0],
- {
- {"key", NANOARROW_TYPE_INT32, NOT_NULL},
- {"value", NANOARROW_TYPE_LIST, NULLABLE},
- }));
- ASSERT_NO_FATAL_FAILURE(
- CompareSchema(reader.schema->children[1]->children[5]->children[0]->children[1],
- {
- {"item", NANOARROW_TYPE_INT32, NULLABLE},
- }));
+ if (!expected.has_value()) continue;
- std::vector seen;
- while (true) {
- ASSERT_NO_FATAL_FAILURE(reader.Next());
- if (!reader.array->release) break;
+ uint32_t info[] = {info_code};
+
+ StreamReader reader;
+ ASSERT_THAT(AdbcConnectionGetInfo(&connection, info, 1, &reader.stream.value, &error),
+ IsOkStatus(&error));
+ ASSERT_NO_FATAL_FAILURE(reader.GetSchema());
- for (int64_t row = 0; row < reader.array->length; row++) {
- ASSERT_FALSE(ArrowArrayViewIsNull(reader.array_view->children[0], row));
- const uint32_t code =
- reader.array_view->children[0]->buffer_views[1].data.as_uint32[row];
- seen.push_back(code);
-
- switch (code) {
- case ADBC_INFO_DRIVER_NAME:
- case ADBC_INFO_DRIVER_VERSION:
- case ADBC_INFO_VENDOR_NAME:
- case ADBC_INFO_VENDOR_VERSION:
- // UTF8
- ASSERT_EQ(uint8_t(0),
- reader.array_view->children[1]->buffer_views[0].data.as_uint8[row]);
- default:
- // Ignored
- break;
+ ASSERT_NO_FATAL_FAILURE(CompareSchema(
+ &reader.schema.value, {
+ {"info_name", NANOARROW_TYPE_UINT32, NOT_NULL},
+ {"info_value", NANOARROW_TYPE_DENSE_UNION, NULLABLE},
+ }));
+ ASSERT_NO_FATAL_FAILURE(
+ CompareSchema(reader.schema->children[1],
+ {
+ {"string_value", NANOARROW_TYPE_STRING, NULLABLE},
+ {"bool_value", NANOARROW_TYPE_BOOL, NULLABLE},
+ {"int64_value", NANOARROW_TYPE_INT64, NULLABLE},
+ {"int32_bitmask", NANOARROW_TYPE_INT32, NULLABLE},
+ {"string_list", NANOARROW_TYPE_LIST, NULLABLE},
+ {"int32_to_int32_list_map", NANOARROW_TYPE_MAP, NULLABLE},
+ }));
+ ASSERT_NO_FATAL_FAILURE(CompareSchema(reader.schema->children[1]->children[4],
+ {
+ {"item", NANOARROW_TYPE_STRING, NULLABLE},
+ }));
+ ASSERT_NO_FATAL_FAILURE(
+ CompareSchema(reader.schema->children[1]->children[5],
+ {
+ {"entries", NANOARROW_TYPE_STRUCT, NOT_NULL},
+ }));
+ ASSERT_NO_FATAL_FAILURE(
+ CompareSchema(reader.schema->children[1]->children[5]->children[0],
+ {
+ {"key", NANOARROW_TYPE_INT32, NOT_NULL},
+ {"value", NANOARROW_TYPE_LIST, NULLABLE},
+ }));
+ ASSERT_NO_FATAL_FAILURE(
+ CompareSchema(reader.schema->children[1]->children[5]->children[0]->children[1],
+ {
+ {"item", NANOARROW_TYPE_INT32, NULLABLE},
+ }));
+
+ std::vector seen;
+ while (true) {
+ ASSERT_NO_FATAL_FAILURE(reader.Next());
+ if (!reader.array->release) break;
+
+ for (int64_t row = 0; row < reader.array->length; row++) {
+ ASSERT_FALSE(ArrowArrayViewIsNull(reader.array_view->children[0], row));
+ const uint32_t code =
+ reader.array_view->children[0]->buffer_views[1].data.as_uint32[row];
+ seen.push_back(code);
+ if (code != info_code) {
+ continue;
+ }
+
+ ASSERT_TRUE(expected.has_value()) << "Got unexpected info code " << code;
+
+ uint8_t type_code =
+ reader.array_view->children[1]->buffer_views[0].data.as_uint8[row];
+ int32_t offset =
+ reader.array_view->children[1]->buffer_views[1].data.as_int32[row];
+ ASSERT_NO_FATAL_FAILURE(std::visit(
+ [&](auto&& expected_value) {
+ using T = std::decay_t;
+ if constexpr (std::is_same_v) {
+ ASSERT_EQ(uint8_t(2), type_code);
+ EXPECT_EQ(expected_value,
+ ArrowArrayViewGetIntUnsafe(
+ reader.array_view->children[1]->children[2], offset));
+ } else if constexpr (std::is_same_v) {
+ ASSERT_EQ(uint8_t(0), type_code);
+ struct ArrowStringView view = ArrowArrayViewGetStringUnsafe(
+ reader.array_view->children[1]->children[0], offset);
+ EXPECT_THAT(std::string_view(static_cast(view.data),
+ view.size_bytes),
+ ::testing::HasSubstr(expected_value));
+ } else {
+ static_assert(!sizeof(T), "not yet implemented");
+ }
+ },
+ *expected))
+ << "code: " << type_code;
}
}
+ EXPECT_THAT(seen, ::testing::IsSupersetOf(info));
}
- ASSERT_THAT(seen, ::testing::UnorderedElementsAreArray(info));
}
void ConnectionTest::TestMetadataGetTableSchema() {
- if (!quirks()->supports_bulk_ingest()) {
+ if (!quirks()->supports_bulk_ingest(ADBC_INGEST_OPTION_MODE_CREATE)) {
GTEST_SKIP();
}
ASSERT_THAT(AdbcConnectionNew(&connection, &error), IsOkStatus(&error));
@@ -911,6 +990,58 @@ void ConnectionTest::TestMetadataGetObjectsPrimaryKey() {
ASSERT_EQ(constraint_column_name, "id");
}
+void ConnectionTest::TestMetadataGetObjectsCancel() {
+ if (!quirks()->supports_cancel() || !quirks()->supports_get_objects()) {
+ GTEST_SKIP();
+ }
+
+ ASSERT_THAT(AdbcConnectionNew(&connection, &error), IsOkStatus(&error));
+ ASSERT_THAT(AdbcConnectionInit(&connection, &database, &error), IsOkStatus(&error));
+
+ StreamReader reader;
+ ASSERT_THAT(
+ AdbcConnectionGetObjects(&connection, ADBC_OBJECT_DEPTH_CATALOGS, nullptr, nullptr,
+ nullptr, nullptr, nullptr, &reader.stream.value, &error),
+ IsOkStatus(&error));
+ ASSERT_NO_FATAL_FAILURE(reader.GetSchema());
+
+ ASSERT_THAT(AdbcConnectionCancel(&connection, &error), IsOkStatus(&error));
+
+ while (true) {
+ int err = reader.MaybeNext();
+ if (err != 0) {
+ ASSERT_THAT(err, ::testing::AnyOf(0, IsErrno(ECANCELED, &reader.stream.value,
+ /*ArrowError*/ nullptr)));
+ }
+ if (!reader.array->release) break;
+ }
+}
+
+void ConnectionTest::TestMetadataGetStatisticNames() {
+ if (!quirks()->supports_statistics()) {
+ GTEST_SKIP();
+ }
+
+ ASSERT_THAT(AdbcConnectionNew(&connection, &error), IsOkStatus(&error));
+ ASSERT_THAT(AdbcConnectionInit(&connection, &database, &error), IsOkStatus(&error));
+
+ StreamReader reader;
+ ASSERT_THAT(AdbcConnectionGetStatisticNames(&connection, &reader.stream.value, &error),
+ IsOkStatus(&error));
+ ASSERT_NO_FATAL_FAILURE(reader.GetSchema());
+
+ ASSERT_NO_FATAL_FAILURE(CompareSchema(
+ &reader.schema.value, {
+ {"statistic_name", NANOARROW_TYPE_STRING, NOT_NULL},
+ {"statistic_key", NANOARROW_TYPE_INT16, NOT_NULL},
+ }));
+
+ while (true) {
+ ASSERT_NO_FATAL_FAILURE(reader.Next());
+ if (!reader.array->release) break;
+ }
+}
+
//------------------------------------------------------------
// Tests of AdbcStatement
@@ -965,7 +1096,7 @@ void StatementTest::TestRelease() {
template
void StatementTest::TestSqlIngestType(ArrowType type,
const std::vector>& values) {
- if (!quirks()->supports_bulk_ingest()) {
+ if (!quirks()->supports_bulk_ingest(ADBC_INGEST_OPTION_MODE_CREATE)) {
GTEST_SKIP();
}
@@ -1105,7 +1236,7 @@ void StatementTest::TestSqlIngestDate32() {
template
void StatementTest::TestSqlIngestTimestampType(const char* timezone) {
- if (!quirks()->supports_bulk_ingest()) {
+ if (!quirks()->supports_bulk_ingest(ADBC_INGEST_OPTION_MODE_CREATE)) {
GTEST_SKIP();
}
@@ -1203,7 +1334,7 @@ void StatementTest::TestSqlIngestTimestampTz() {
}
void StatementTest::TestSqlIngestInterval() {
- if (!quirks()->supports_bulk_ingest()) {
+ if (!quirks()->supports_bulk_ingest(ADBC_INGEST_OPTION_MODE_CREATE)) {
GTEST_SKIP();
}
@@ -1311,7 +1442,8 @@ void StatementTest::TestSqlIngestTableEscaping() {
}
void StatementTest::TestSqlIngestAppend() {
- if (!quirks()->supports_bulk_ingest()) {
+ if (!quirks()->supports_bulk_ingest(ADBC_INGEST_OPTION_MODE_CREATE) ||
+ !quirks()->supports_bulk_ingest(ADBC_INGEST_OPTION_MODE_APPEND)) {
GTEST_SKIP();
}
@@ -1389,8 +1521,185 @@ void StatementTest::TestSqlIngestAppend() {
ASSERT_THAT(AdbcStatementRelease(&statement, &error), IsOkStatus(&error));
}
+void StatementTest::TestSqlIngestReplace() {
+ if (!quirks()->supports_bulk_ingest(ADBC_INGEST_OPTION_MODE_REPLACE)) {
+ GTEST_SKIP();
+ }
+
+ // Ingest
+
+ Handle schema;
+ Handle array;
+ struct ArrowError na_error;
+ ASSERT_THAT(MakeSchema(&schema.value, {{"int64s", NANOARROW_TYPE_INT64}}), IsOkErrno());
+ ASSERT_THAT(MakeBatch(&schema.value, &array.value, &na_error, {42}),
+ IsOkErrno());
+
+ ASSERT_THAT(AdbcStatementNew(&connection, &statement, &error), IsOkStatus(&error));
+ ASSERT_THAT(AdbcStatementSetOption(&statement, ADBC_INGEST_OPTION_TARGET_TABLE,
+ "bulk_ingest", &error),
+ IsOkStatus(&error));
+ ASSERT_THAT(AdbcStatementSetOption(&statement, ADBC_INGEST_OPTION_MODE,
+ ADBC_INGEST_OPTION_MODE_REPLACE, &error),
+ IsOkStatus(&error));
+ ASSERT_THAT(AdbcStatementBind(&statement, &array.value, &schema.value, &error),
+ IsOkStatus(&error));
+
+ int64_t rows_affected = 0;
+ ASSERT_THAT(AdbcStatementExecuteQuery(&statement, nullptr, &rows_affected, &error),
+ IsOkStatus(&error));
+ ASSERT_THAT(rows_affected, ::testing::AnyOf(::testing::Eq(1), ::testing::Eq(-1)));
+
+ // Read data back
+ ASSERT_THAT(AdbcStatementSetSqlQuery(&statement, "SELECT * FROM bulk_ingest", &error),
+ IsOkStatus(&error));
+ {
+ StreamReader reader;
+ ASSERT_THAT(AdbcStatementExecuteQuery(&statement, &reader.stream.value,
+ &reader.rows_affected, &error),
+ IsOkStatus(&error));
+ ASSERT_THAT(reader.rows_affected,
+ ::testing::AnyOf(::testing::Eq(1), ::testing::Eq(-1)));
+
+ ASSERT_NO_FATAL_FAILURE(reader.GetSchema());
+ ASSERT_NO_FATAL_FAILURE(CompareSchema(&reader.schema.value,
+ {{"int64s", NANOARROW_TYPE_INT64, NULLABLE}}));
+
+ ASSERT_NO_FATAL_FAILURE(reader.Next());
+ ASSERT_NE(nullptr, reader.array->release);
+ ASSERT_EQ(1, reader.array->length);
+ ASSERT_EQ(1, reader.array->n_children);
+
+ ASSERT_NO_FATAL_FAILURE(CompareArray(reader.array_view->children[0], {42}));
+
+ ASSERT_NO_FATAL_FAILURE(reader.Next());
+ ASSERT_EQ(nullptr, reader.array->release);
+ }
+
+ // Replace
+ // Re-initialize since Bind() should take ownership of data
+ ASSERT_THAT(MakeSchema(&schema.value, {{"int64s", NANOARROW_TYPE_INT64}}), IsOkErrno());
+ ASSERT_THAT(MakeBatch(&schema.value, &array.value, &na_error, {-42, -42}),
+ IsOkErrno());
+
+ ASSERT_THAT(AdbcStatementSetOption(&statement, ADBC_INGEST_OPTION_TARGET_TABLE,
+ "bulk_ingest", &error),
+ IsOkStatus(&error));
+ ASSERT_THAT(AdbcStatementSetOption(&statement, ADBC_INGEST_OPTION_MODE,
+ ADBC_INGEST_OPTION_MODE_REPLACE, &error),
+ IsOkStatus(&error));
+ ASSERT_THAT(AdbcStatementBind(&statement, &array.value, &schema.value, &error),
+ IsOkStatus(&error));
+
+ ASSERT_THAT(AdbcStatementExecuteQuery(&statement, nullptr, &rows_affected, &error),
+ IsOkStatus(&error));
+ ASSERT_THAT(rows_affected, ::testing::AnyOf(::testing::Eq(2), ::testing::Eq(-1)));
+
+ // Read data back
+ ASSERT_THAT(AdbcStatementSetSqlQuery(&statement, "SELECT * FROM bulk_ingest", &error),
+ IsOkStatus(&error));
+ {
+ StreamReader reader;
+ ASSERT_THAT(AdbcStatementExecuteQuery(&statement, &reader.stream.value,
+ &reader.rows_affected, &error),
+ IsOkStatus(&error));
+ ASSERT_THAT(reader.rows_affected,
+ ::testing::AnyOf(::testing::Eq(2), ::testing::Eq(-1)));
+
+ ASSERT_NO_FATAL_FAILURE(reader.GetSchema());
+ ASSERT_NO_FATAL_FAILURE(CompareSchema(&reader.schema.value,
+ {{"int64s", NANOARROW_TYPE_INT64, NULLABLE}}));
+
+ ASSERT_NO_FATAL_FAILURE(reader.Next());
+ ASSERT_NE(nullptr, reader.array->release);
+ ASSERT_EQ(2, reader.array->length);
+ ASSERT_EQ(1, reader.array->n_children);
+
+ ASSERT_NO_FATAL_FAILURE(
+ CompareArray(reader.array_view->children[0], {-42, -42}));
+
+ ASSERT_NO_FATAL_FAILURE(reader.Next());
+ ASSERT_EQ(nullptr, reader.array->release);
+ }
+}
+
+void StatementTest::TestSqlIngestCreateAppend() {
+ if (!quirks()->supports_bulk_ingest(ADBC_INGEST_OPTION_MODE_CREATE_APPEND)) {
+ GTEST_SKIP();
+ }
+
+ ASSERT_THAT(quirks()->DropTable(&connection, "bulk_ingest", &error),
+ IsOkStatus(&error));
+
+ // Ingest
+
+ Handle schema;
+ Handle array;
+ struct ArrowError na_error;
+ ASSERT_THAT(MakeSchema(&schema.value, {{"int64s", NANOARROW_TYPE_INT64}}), IsOkErrno());
+ ASSERT_THAT(MakeBatch(&schema.value, &array.value, &na_error, {42}),
+ IsOkErrno());
+
+ ASSERT_THAT(AdbcStatementNew(&connection, &statement, &error), IsOkStatus(&error));
+ ASSERT_THAT(AdbcStatementSetOption(&statement, ADBC_INGEST_OPTION_TARGET_TABLE,
+ "bulk_ingest", &error),
+ IsOkStatus(&error));
+ ASSERT_THAT(AdbcStatementSetOption(&statement, ADBC_INGEST_OPTION_MODE,
+ ADBC_INGEST_OPTION_MODE_CREATE_APPEND, &error),
+ IsOkStatus(&error));
+ ASSERT_THAT(AdbcStatementBind(&statement, &array.value, &schema.value, &error),
+ IsOkStatus(&error));
+
+ int64_t rows_affected = 0;
+ ASSERT_THAT(AdbcStatementExecuteQuery(&statement, nullptr, &rows_affected, &error),
+ IsOkStatus(&error));
+ ASSERT_THAT(rows_affected, ::testing::AnyOf(::testing::Eq(1), ::testing::Eq(-1)));
+
+ // Append
+ // Re-initialize since Bind() should take ownership of data
+ ASSERT_THAT(MakeSchema(&schema.value, {{"int64s", NANOARROW_TYPE_INT64}}), IsOkErrno());
+ ASSERT_THAT(MakeBatch(&schema.value, &array.value, &na_error, {42, 42}),
+ IsOkErrno());
+
+ ASSERT_THAT(AdbcStatementBind(&statement, &array.value, &schema.value, &error),
+ IsOkStatus(&error));
+
+ ASSERT_THAT(AdbcStatementExecuteQuery(&statement, nullptr, &rows_affected, &error),
+ IsOkStatus(&error));
+ ASSERT_THAT(rows_affected, ::testing::AnyOf(::testing::Eq(2), ::testing::Eq(-1)));
+
+ // Read data back
+ ASSERT_THAT(AdbcStatementSetSqlQuery(&statement, "SELECT * FROM bulk_ingest", &error),
+ IsOkStatus(&error));
+ {
+ StreamReader reader;
+ ASSERT_THAT(AdbcStatementExecuteQuery(&statement, &reader.stream.value,
+ &reader.rows_affected, &error),
+ IsOkStatus(&error));
+ ASSERT_THAT(reader.rows_affected,
+ ::testing::AnyOf(::testing::Eq(3), ::testing::Eq(-1)));
+
+ ASSERT_NO_FATAL_FAILURE(reader.GetSchema());
+ ASSERT_NO_FATAL_FAILURE(CompareSchema(&reader.schema.value,
+ {{"int64s", NANOARROW_TYPE_INT64, NULLABLE}}));
+
+ ASSERT_NO_FATAL_FAILURE(reader.Next());
+ ASSERT_NE(nullptr, reader.array->release);
+ ASSERT_EQ(3, reader.array->length);
+ ASSERT_EQ(1, reader.array->n_children);
+
+ ASSERT_NO_FATAL_FAILURE(
+ CompareArray(reader.array_view->children[0], {42, 42, 42}));
+
+ ASSERT_NO_FATAL_FAILURE(reader.Next());
+ ASSERT_EQ(nullptr, reader.array->release);
+ }
+
+ ASSERT_THAT(AdbcStatementRelease(&statement, &error), IsOkStatus(&error));
+}
+
void StatementTest::TestSqlIngestErrors() {
- if (!quirks()->supports_bulk_ingest()) {
+ if (!quirks()->supports_bulk_ingest(ADBC_INGEST_OPTION_MODE_CREATE)) {
GTEST_SKIP();
}
@@ -1470,7 +1779,7 @@ void StatementTest::TestSqlIngestErrors() {
}
void StatementTest::TestSqlIngestMultipleConnections() {
- if (!quirks()->supports_bulk_ingest()) {
+ if (!quirks()->supports_bulk_ingest(ADBC_INGEST_OPTION_MODE_CREATE)) {
GTEST_SKIP();
}
@@ -1540,7 +1849,7 @@ void StatementTest::TestSqlIngestMultipleConnections() {
}
void StatementTest::TestSqlIngestSample() {
- if (!quirks()->supports_bulk_ingest()) {
+ if (!quirks()->supports_bulk_ingest(ADBC_INGEST_OPTION_MODE_CREATE)) {
GTEST_SKIP();
}
@@ -1786,7 +2095,7 @@ void StatementTest::TestSqlPrepareSelectParams() {
}
void StatementTest::TestSqlPrepareUpdate() {
- if (!quirks()->supports_bulk_ingest() ||
+ if (!quirks()->supports_bulk_ingest(ADBC_INGEST_OPTION_MODE_CREATE) ||
!quirks()->supports_dynamic_parameter_binding()) {
GTEST_SKIP();
}
@@ -1865,7 +2174,7 @@ void StatementTest::TestSqlPrepareUpdateNoParams() {
}
void StatementTest::TestSqlPrepareUpdateStream() {
- if (!quirks()->supports_bulk_ingest() ||
+ if (!quirks()->supports_bulk_ingest(ADBC_INGEST_OPTION_MODE_CREATE) ||
!quirks()->supports_dynamic_parameter_binding()) {
GTEST_SKIP();
}
@@ -2140,6 +2449,36 @@ void StatementTest::TestSqlQueryStrings() {
ASSERT_THAT(AdbcStatementRelease(&statement, &error), IsOkStatus(&error));
}
+void StatementTest::TestSqlQueryCancel() {
+ if (!quirks()->supports_cancel()) {
+ GTEST_SKIP();
+ }
+
+ ASSERT_THAT(AdbcStatementNew(&connection, &statement, &error), IsOkStatus(&error));
+ ASSERT_THAT(AdbcStatementSetSqlQuery(&statement, "SELECT 'SaShiSuSeSo'", &error),
+ IsOkStatus(&error));
+
+ {
+ StreamReader reader;
+ ASSERT_THAT(AdbcStatementExecuteQuery(&statement, &reader.stream.value,
+ &reader.rows_affected, &error),
+ IsOkStatus(&error));
+ ASSERT_NO_FATAL_FAILURE(reader.GetSchema());
+
+ ASSERT_THAT(AdbcStatementCancel(&statement, &error), IsOkStatus(&error));
+ while (true) {
+ int err = reader.MaybeNext();
+ if (err != 0) {
+ ASSERT_THAT(err, ::testing::AnyOf(0, IsErrno(ECANCELED, &reader.stream.value,
+ /*ArrowError*/ nullptr)));
+ }
+ if (!reader.array->release) break;
+ }
+ }
+
+ ASSERT_THAT(AdbcStatementRelease(&statement, &error), IsOkStatus(&error));
+}
+
void StatementTest::TestSqlQueryErrors() {
// Invalid query
ASSERT_THAT(AdbcStatementNew(&connection, &statement, &error), IsOkStatus(&error));
@@ -2159,6 +2498,13 @@ void StatementTest::TestTransactions() {
ASSERT_THAT(quirks()->DropTable(&connection, "bulk_ingest", &error),
IsOkStatus(&error));
+ if (quirks()->supports_get_option()) {
+ auto autocommit =
+ ConnectionGetOption(&connection, ADBC_CONNECTION_OPTION_AUTOCOMMIT, &error);
+ ASSERT_THAT(autocommit,
+ ::testing::Optional(::testing::StrEq(ADBC_OPTION_VALUE_ENABLED)));
+ }
+
Handle connection2;
ASSERT_THAT(AdbcConnectionNew(&connection2.value, &error), IsOkStatus(&error));
ASSERT_THAT(AdbcConnectionInit(&connection2.value, &database, &error),
@@ -2168,6 +2514,13 @@ void StatementTest::TestTransactions() {
ADBC_OPTION_VALUE_DISABLED, &error),
IsOkStatus(&error));
+ if (quirks()->supports_get_option()) {
+ auto autocommit =
+ ConnectionGetOption(&connection, ADBC_CONNECTION_OPTION_AUTOCOMMIT, &error);
+ ASSERT_THAT(autocommit,
+ ::testing::Optional(::testing::StrEq(ADBC_OPTION_VALUE_DISABLED)));
+ }
+
// Uncommitted change
ASSERT_NO_FATAL_FAILURE(IngestSampleTable(&connection, &error));
@@ -2243,6 +2596,86 @@ void StatementTest::TestTransactions() {
}
}
+void StatementTest::TestSqlSchemaInts() {
+ if (!quirks()->supports_execute_schema()) {
+ GTEST_SKIP() << "Not supported";
+ }
+
+ ASSERT_THAT(AdbcStatementNew(&connection, &statement, &error), IsOkStatus(&error));
+ ASSERT_THAT(AdbcStatementSetSqlQuery(&statement, "SELECT 42", &error),
+ IsOkStatus(&error));
+
+ nanoarrow::UniqueSchema schema;
+ ASSERT_THAT(AdbcStatementExecuteSchema(&statement, schema.get(), &error),
+ IsOkStatus(&error));
+
+ ASSERT_EQ(1, schema->n_children);
+ ASSERT_THAT(schema->children[0]->format, ::testing::AnyOfArray({
+ ::testing::StrEq("i"), // int32
+ ::testing::StrEq("l"), // int64
+ }));
+
+ ASSERT_THAT(AdbcStatementRelease(&statement, &error), IsOkStatus(&error));
+}
+
+void StatementTest::TestSqlSchemaFloats() {
+ if (!quirks()->supports_execute_schema()) {
+ GTEST_SKIP() << "Not supported";
+ }
+
+ ASSERT_THAT(AdbcStatementNew(&connection, &statement, &error), IsOkStatus(&error));
+ ASSERT_THAT(AdbcStatementSetSqlQuery(&statement, "SELECT CAST(1.5 AS FLOAT)", &error),
+ IsOkStatus(&error));
+
+ nanoarrow::UniqueSchema schema;
+ ASSERT_THAT(AdbcStatementExecuteSchema(&statement, schema.get(), &error),
+ IsOkStatus(&error));
+
+ ASSERT_EQ(1, schema->n_children);
+ ASSERT_THAT(schema->children[0]->format, ::testing::AnyOfArray({
+ ::testing::StrEq("f"), // float32
+ ::testing::StrEq("g"), // float64
+ }));
+
+ ASSERT_THAT(AdbcStatementRelease(&statement, &error), IsOkStatus(&error));
+}
+
+void StatementTest::TestSqlSchemaStrings() {
+ if (!quirks()->supports_execute_schema()) {
+ GTEST_SKIP() << "Not supported";
+ }
+
+ ASSERT_THAT(AdbcStatementNew(&connection, &statement, &error), IsOkStatus(&error));
+ ASSERT_THAT(AdbcStatementSetSqlQuery(&statement, "SELECT 'hi'", &error),
+ IsOkStatus(&error));
+
+ nanoarrow::UniqueSchema schema;
+ ASSERT_THAT(AdbcStatementExecuteSchema(&statement, schema.get(), &error),
+ IsOkStatus(&error));
+
+ ASSERT_EQ(1, schema->n_children);
+ ASSERT_THAT(schema->children[0]->format, ::testing::AnyOfArray({
+ ::testing::StrEq("u"), // string
+ ::testing::StrEq("U"), // large_string
+ }));
+
+ ASSERT_THAT(AdbcStatementRelease(&statement, &error), IsOkStatus(&error));
+}
+
+void StatementTest::TestSqlSchemaErrors() {
+ if (!quirks()->supports_execute_schema()) {
+ GTEST_SKIP() << "Not supported";
+ }
+
+ ASSERT_THAT(AdbcStatementNew(&connection, &statement, &error), IsOkStatus(&error));
+
+ nanoarrow::UniqueSchema schema;
+ ASSERT_THAT(AdbcStatementExecuteSchema(&statement, schema.get(), &error),
+ IsStatus(ADBC_STATUS_INVALID_STATE, &error));
+
+ ASSERT_THAT(AdbcStatementRelease(&statement, &error), IsOkStatus(&error));
+}
+
void StatementTest::TestConcurrentStatements() {
Handle statement1;
Handle statement2;
@@ -2278,6 +2711,24 @@ void StatementTest::TestConcurrentStatements() {
ASSERT_NO_FATAL_FAILURE(reader1.GetSchema());
}
+// Test that an ADBC 1.0.0-sized error still works
+void StatementTest::TestErrorCompatibility() {
+ // XXX: sketchy cast
+ auto* error = static_cast(malloc(ADBC_ERROR_1_0_0_SIZE));
+ std::memset(error, 0, ADBC_ERROR_1_0_0_SIZE);
+
+ ASSERT_THAT(AdbcStatementNew(&connection, &statement, error), IsOkStatus(error));
+ ASSERT_THAT(
+ AdbcStatementSetSqlQuery(&statement, "SELECT * FROM thistabledoesnotexist", error),
+ IsOkStatus(error));
+ adbc_validation::StreamReader reader;
+ ASSERT_THAT(AdbcStatementExecuteQuery(&statement, &reader.stream.value,
+ &reader.rows_affected, error),
+ ::testing::Not(IsOkStatus(error)));
+ error->release(error);
+ free(error);
+}
+
void StatementTest::TestResultInvalidation() {
// Start reading from a statement, then overwrite it
ASSERT_THAT(AdbcStatementNew(&connection, &statement, &error), IsOkStatus(&error));
@@ -2296,8 +2747,8 @@ void StatementTest::TestResultInvalidation() {
IsOkStatus(&error));
ASSERT_NO_FATAL_FAILURE(reader2.GetSchema());
- // First reader should not fail, but may give no data
- ASSERT_NO_FATAL_FAILURE(reader1.Next());
+ // First reader may fail, or may succeed but give no data
+ reader1.MaybeNext();
}
#undef NOT_NULL
diff --git a/c/validation/adbc_validation.h b/c/validation/adbc_validation.h
index 23dacb7f4b..9bedc6a376 100644
--- a/c/validation/adbc_validation.h
+++ b/c/validation/adbc_validation.h
@@ -20,6 +20,7 @@
#include
#include
+#include
#include
#include
@@ -31,6 +32,8 @@ namespace adbc_validation {
#define ADBCV_STRINGIFY(s) #s
#define ADBCV_STRINGIFY_VALUE(s) ADBCV_STRINGIFY(s)
+using SqlInfoValue = std::variant