This is an automated email from the ASF dual-hosted git repository.
lidavidm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-adbc.git
The following commit(s) were added to refs/heads/main by this push:
new eb08d357b feat(c/driver/sqlite): add support for sqlite:// URI scheme
(#4463)
eb08d357b is described below
commit eb08d357bc5b5bf7561bd18a22756fcd652779b3
Author: Mandukhai Alimaa <[email protected]>
AuthorDate: Mon Jul 6 22:29:51 2026 -0500
feat(c/driver/sqlite): add support for sqlite:// URI scheme (#4463)
Closes #4459.
---
c/driver/sqlite/sqlite.cc | 13 ++++-
c/driver/sqlite/sqlite_test.cc | 130 +++++++++++++++++++++++++++++++++++++++++
docs/source/driver/sqlite.rst | 23 +++++++-
3 files changed, 163 insertions(+), 3 deletions(-)
diff --git a/c/driver/sqlite/sqlite.cc b/c/driver/sqlite/sqlite.cc
index d970d76a0..62dec5ab2 100644
--- a/c/driver/sqlite/sqlite.cc
+++ b/c/driver/sqlite/sqlite.cc
@@ -43,6 +43,8 @@ namespace status = adbc::driver::status;
namespace {
constexpr std::string_view kDefaultUri =
"file:adbc_driver_sqlite?mode=memory&cache=shared";
+constexpr std::string_view kUriWrapperPrefix = "sqlite://";
+constexpr std::string_view kSqliteUriPrefix = "file:";
constexpr std::string_view kConnectionOptionEnableLoadExtension =
"adbc.sqlite.load_extension.enabled";
constexpr std::string_view kConnectionOptionLoadExtensionPath =
@@ -61,6 +63,14 @@ std::string_view GetColumnText(sqlite3_stmt* stmt, int
index) {
};
}
+std::string NormalizeUriForOpen(std::string_view uri) {
+ if (uri.rfind(kUriWrapperPrefix, 0) == 0) {
+ return std::string(kSqliteUriPrefix) +
+ std::string(uri.substr(kUriWrapperPrefix.size()));
+ }
+ return std::string{uri};
+}
+
class SqliteMutexGuard {
public:
explicit SqliteMutexGuard(sqlite3* conn) : conn_(conn) {
@@ -514,8 +524,9 @@ class SqliteDatabase : public
driver::Database<SqliteDatabase> {
[[maybe_unused]] constexpr static std::string_view kErrorPrefix = "[SQLite]";
Result<sqlite3*> OpenConnection() {
+ std::string normalized_uri = NormalizeUriForOpen(uri_);
sqlite3* conn;
- int rc = sqlite3_open_v2(uri_.c_str(), &conn,
+ int rc = sqlite3_open_v2(normalized_uri.c_str(), &conn,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
SQLITE_OPEN_URI,
/*zVfs=*/nullptr);
if (rc != SQLITE_OK) {
diff --git a/c/driver/sqlite/sqlite_test.cc b/c/driver/sqlite/sqlite_test.cc
index 70240a30b..243449eb0 100644
--- a/c/driver/sqlite/sqlite_test.cc
+++ b/c/driver/sqlite/sqlite_test.cc
@@ -16,6 +16,7 @@
// under the License.
#include <cstring>
+#include <filesystem> // NOLINT [build/c++17]
#include <limits>
#include <optional>
#include <string>
@@ -324,6 +325,135 @@ TEST_F(SqliteConnectionTest, GetInfoMetadata) {
ASSERT_THAT(seen, ::testing::UnorderedElementsAreArray(info));
}
+TEST(SqliteUriWrapper, InMemory) {
+ struct AdbcError error = {};
+ adbc_validation::Handle<struct AdbcDatabase> database;
+ adbc_validation::Handle<struct AdbcConnection> connection;
+
+ constexpr std::string_view kUri = "sqlite://:memory:";
+
+ ASSERT_THAT(AdbcDatabaseNew(&database.value, &error),
+ adbc_validation::IsOkStatus(&error));
+ ASSERT_THAT(AdbcDatabaseSetOption(&database.value, "uri", kUri.data(),
&error),
+ adbc_validation::IsOkStatus(&error));
+
+ char value[sizeof("sqlite://:memory:")] = {};
+ size_t length = sizeof(value);
+ ASSERT_THAT(AdbcDatabaseGetOption(&database.value, "uri", value, &length,
&error),
+ adbc_validation::IsOkStatus(&error));
+ ASSERT_EQ(kUri.size() + 1, length);
+ EXPECT_STREQ(kUri.data(), value);
+
+ ASSERT_THAT(AdbcDatabaseInit(&database.value, &error),
+ adbc_validation::IsOkStatus(&error));
+ ASSERT_THAT(AdbcConnectionNew(&connection.value, &error),
+ adbc_validation::IsOkStatus(&error));
+ ASSERT_THAT(AdbcConnectionInit(&connection.value, &database.value, &error),
+ adbc_validation::IsOkStatus(&error));
+
+ adbc_validation::Handle<struct AdbcStatement> statement;
+ ASSERT_THAT(AdbcStatementNew(&connection.value, &statement.value, &error),
+ adbc_validation::IsOkStatus(&error));
+ ASSERT_THAT(AdbcStatementSetSqlQuery(
+ &statement.value, "CREATE TABLE uri_wrapper_test (x
INTEGER)", &error),
+ adbc_validation::IsOkStatus(&error));
+ ASSERT_THAT(AdbcStatementExecuteQuery(&statement.value, nullptr, nullptr,
&error),
+ adbc_validation::IsOkStatus(&error));
+}
+
+TEST(SqliteUriWrapper, EmptyPayload) {
+ struct AdbcError error = {};
+ adbc_validation::Handle<struct AdbcDatabase> database;
+ adbc_validation::Handle<struct AdbcConnection> connection;
+
+ ASSERT_THAT(AdbcDatabaseNew(&database.value, &error),
+ adbc_validation::IsOkStatus(&error));
+ ASSERT_THAT(AdbcDatabaseSetOption(&database.value, "uri", "sqlite://",
&error),
+ adbc_validation::IsOkStatus(&error));
+ ASSERT_THAT(AdbcDatabaseInit(&database.value, &error),
+ adbc_validation::IsOkStatus(&error));
+ ASSERT_THAT(AdbcConnectionNew(&connection.value, &error),
+ adbc_validation::IsOkStatus(&error));
+ ASSERT_THAT(AdbcConnectionInit(&connection.value, &database.value, &error),
+ adbc_validation::IsOkStatus(&error));
+
+ adbc_validation::Handle<struct AdbcStatement> statement;
+ ASSERT_THAT(AdbcStatementNew(&connection.value, &statement.value, &error),
+ adbc_validation::IsOkStatus(&error));
+ ASSERT_THAT(AdbcStatementSetSqlQuery(
+ &statement.value, "CREATE TABLE uri_wrapper_test (x
INTEGER)", &error),
+ adbc_validation::IsOkStatus(&error));
+ ASSERT_THAT(AdbcStatementExecuteQuery(&statement.value, nullptr, nullptr,
&error),
+ adbc_validation::IsOkStatus(&error));
+}
+
+TEST(SqliteUriWrapper, AbsolutePath) {
+ const std::filesystem::path db_path =
+ std::filesystem::temp_directory_path() /
"sqlite_uri_wrapper_absolute_test.db";
+ std::error_code ec;
+ std::filesystem::remove(db_path, ec);
+ ASSERT_FALSE(std::filesystem::exists(db_path));
+
+ {
+ struct AdbcError error = {};
+ adbc_validation::Handle<struct AdbcDatabase> database;
+ adbc_validation::Handle<struct AdbcConnection> connection;
+
+ const std::string uri = "sqlite://" + db_path.string();
+
+ ASSERT_THAT(AdbcDatabaseNew(&database.value, &error),
+ adbc_validation::IsOkStatus(&error));
+ ASSERT_THAT(AdbcDatabaseSetOption(&database.value, "uri", uri.c_str(),
&error),
+ adbc_validation::IsOkStatus(&error));
+ ASSERT_THAT(AdbcDatabaseInit(&database.value, &error),
+ adbc_validation::IsOkStatus(&error));
+ ASSERT_THAT(AdbcConnectionNew(&connection.value, &error),
+ adbc_validation::IsOkStatus(&error));
+ ASSERT_THAT(AdbcConnectionInit(&connection.value, &database.value, &error),
+ adbc_validation::IsOkStatus(&error));
+
+ adbc_validation::Handle<struct AdbcStatement> statement;
+ ASSERT_THAT(AdbcStatementNew(&connection.value, &statement.value, &error),
+ adbc_validation::IsOkStatus(&error));
+ ASSERT_THAT(
+ AdbcStatementSetSqlQuery(&statement.value,
+ "CREATE TABLE uri_wrapper_test (x INTEGER)",
&error),
+ adbc_validation::IsOkStatus(&error));
+ ASSERT_THAT(AdbcStatementExecuteQuery(&statement.value, nullptr, nullptr,
&error),
+ adbc_validation::IsOkStatus(&error));
+ }
+
+ EXPECT_TRUE(std::filesystem::exists(db_path));
+ std::filesystem::remove(db_path, ec);
+}
+
+TEST(SqliteUriWrapper, SqliteUriFilename) {
+ struct AdbcError error = {};
+ adbc_validation::Handle<struct AdbcDatabase> database;
+ adbc_validation::Handle<struct AdbcConnection> connection;
+
+ ASSERT_THAT(AdbcDatabaseNew(&database.value, &error),
+ adbc_validation::IsOkStatus(&error));
+ ASSERT_THAT(AdbcDatabaseSetOption(&database.value, "uri",
+ "sqlite://:memory:?cache=shared", &error),
+ adbc_validation::IsOkStatus(&error));
+ ASSERT_THAT(AdbcDatabaseInit(&database.value, &error),
+ adbc_validation::IsOkStatus(&error));
+ ASSERT_THAT(AdbcConnectionNew(&connection.value, &error),
+ adbc_validation::IsOkStatus(&error));
+ ASSERT_THAT(AdbcConnectionInit(&connection.value, &database.value, &error),
+ adbc_validation::IsOkStatus(&error));
+
+ adbc_validation::Handle<struct AdbcStatement> statement;
+ ASSERT_THAT(AdbcStatementNew(&connection.value, &statement.value, &error),
+ adbc_validation::IsOkStatus(&error));
+ ASSERT_THAT(AdbcStatementSetSqlQuery(
+ &statement.value, "CREATE TABLE uri_wrapper_test (x
INTEGER)", &error),
+ adbc_validation::IsOkStatus(&error));
+ ASSERT_THAT(AdbcStatementExecuteQuery(&statement.value, nullptr, nullptr,
&error),
+ adbc_validation::IsOkStatus(&error));
+}
+
class SqliteStatementTest : public ::testing::Test,
public adbc_validation::StatementTest {
public:
diff --git a/docs/source/driver/sqlite.rst b/docs/source/driver/sqlite.rst
index 3bb8241e0..bb5f524bb 100644
--- a/docs/source/driver/sqlite.rst
+++ b/docs/source/driver/sqlite.rst
@@ -36,8 +36,27 @@ Usage
=====
To connect to a database, supply the "uri" parameter when constructing
-the :c:struct:`AdbcDatabase`. This should be a filename or `URI
-filename <https://www.sqlite.org/c3ref/open.html#urifilenamesinsqlite3open>`_.
+the :c:struct:`AdbcDatabase`. This should be a filename, `URI
+filename <https://www.sqlite.org/c3ref/open.html#urifilenamesinsqlite3open>`_,
+or SQLite URI filename syntax with ``sqlite://`` instead of ``file:``.
+For example, ``sqlite://``, ``sqlite://:memory:``,
+``sqlite://example.db``, and ``sqlite://:memory:?cache=shared`` are all valid.
+Please note that when using the ``sqlite://`` scheme, the remainder of the
+URI must not itself start with ``file:``.
+
+Valid examples:
+
+- ``sqlite://``
+- ``sqlite://:memory:``
+- ``sqlite://example.db``
+- ``sqlite:///tmp/example.db``
+- ``sqlite://:memory:?cache=shared``
+
+Invalid examples:
+
+- ``sqlite://file:example.db``
+- ``sqlite://file::memory:?cache=shared``
+
If omitted, it will default to an in-memory database, but one that is
shared across all connections.