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.
 

Reply via email to