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

paleolimbot 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 8d1578000 refactor(c/driver/framework): Remove fmt as required 
dependency of the driver framework (#2081)
8d1578000 is described below

commit 8d1578000d07c9eb99a90bc1235ea3486b521a7e
Author: Dewey Dunnington <[email protected]>
AuthorDate: Tue Aug 20 11:50:21 2024 -0300

    refactor(c/driver/framework): Remove fmt as required dependency of the 
driver framework (#2081)
    
    This PR removes fmt as a required component of a driver based on the
    driver framework. This is so that we can eliminate
    `c/driver/common/driver_base.h` in favour of the much better
    `c/driver/framework`.
    
    The fmt library is great and easy to use and we should absolutely use it
    in our own drivers; however, I don't know of any libraries that would
    want to export an ADBC driver that don't already have a scheme for error
    generation (e.g., Arrow's Status or GDAL's `cpl_error.h`). In the R
    package we don't generate any fancy errors but we do need to make a
    driver to check that the wires are plugged in. I think I've done this in
    such a way that we keep all the benefits for our own drivers but drop
    the requirement for all users.
    
    This PR leans in to the `Status/Result`. I take back everything I ever
    said about it...it's awesome and we should keep it (closes #1663). I
    think it will be particularly nice for the consuming end as well if or
    when we get there.
    
    This PR also limits the use of nanoarrow to .cc files (i.e., nanoarrow
    is not exposed in the headers). This mostly just seemed cleaner, if not
    strictly necessary (/there are other problems that need solving like
    namespacing if the internal nanoarrow were to be completely isolated). I
    also removed references to any headers outside `driver/framework`
    (mostly to the old utility library).
    
    This is not quite far enough to do what I need to do in the R package,
    where I basically just want an empty driver that does nothing. We could
    fence the bits that rely on the other framework components with `#if
    !defined(ADBC_FRAMEWORK_MINIMAL)` or separate `driver::BaseDatabase`
    (contains no `XXXImpl()` methods) from `driver::Database` (what the
    current BaseDatabase is).
---
 c/driver/framework/CMakeLists.txt      |  35 ++++----
 c/driver/framework/base_connection.h   | 102 ++++++-----------------
 c/driver/framework/base_database.h     |   4 +-
 c/driver/framework/base_driver.cc      |  32 ++++++--
 c/driver/framework/base_driver.h       |  29 +------
 c/driver/framework/base_driver_test.cc | 142 +++++++++++++++++++++++++++++++++
 c/driver/framework/base_statement.h    | 116 +++++++++------------------
 c/driver/framework/catalog.cc          |  67 +++++++++++++++-
 c/driver/framework/catalog.h           |   8 ++
 c/driver/framework/objects.cc          |  15 +++-
 c/driver/framework/objects.h           |   4 +-
 c/driver/framework/status.h            |  67 +++++++++++-----
 c/driver/sqlite/sqlite.cc              |  95 +++++++++++-----------
 c/vendor/nanoarrow/nanoarrow.hpp       |  39 +++++----
 14 files changed, 463 insertions(+), 292 deletions(-)

diff --git a/c/driver/framework/CMakeLists.txt 
b/c/driver/framework/CMakeLists.txt
index d6891aedb..1dd81f78a 100644
--- a/c/driver/framework/CMakeLists.txt
+++ b/c/driver/framework/CMakeLists.txt
@@ -25,20 +25,21 @@ target_include_directories(adbc_driver_framework
                                    "${REPOSITORY_ROOT}/c/vendor")
 target_link_libraries(adbc_driver_framework PUBLIC adbc_driver_common fmt::fmt)
 
-# if(ADBC_BUILD_TESTS)
-#   add_test_case(driver_framework_test
-#                 PREFIX
-#                 adbc
-#                 EXTRA_LABELS
-#                 driver-framework
-#                 SOURCES
-#                 utils_test.cc
-#                 driver_test.cc
-#                 EXTRA_LINK_LIBS
-#                 adbc_driver_framework
-#                 nanoarrow)
-#   target_compile_features(adbc-driver-framework-test PRIVATE cxx_std_17)
-#   target_include_directories(adbc-driver-framework-test
-#                              PRIVATE "${REPOSITORY_ROOT}" 
"${REPOSITORY_ROOT}/c/vendor")
-#   adbc_configure_target(adbc-driver-framework-test)
-# endif()
+if(ADBC_BUILD_TESTS)
+  add_test_case(driver_framework_test
+                PREFIX
+                adbc
+                EXTRA_LABELS
+                driver-framework
+                SOURCES
+                base_driver_test.cc
+                EXTRA_LINK_LIBS
+                adbc_driver_framework
+                nanoarrow)
+  target_compile_features(adbc-driver-framework-test PRIVATE cxx_std_17)
+  target_include_directories(adbc-driver-framework-test
+                             PRIVATE "${REPOSITORY_ROOT}/c/"
+                                     "${REPOSITORY_ROOT}/c/include"
+                                     "${REPOSITORY_ROOT}/c/vendor")
+  adbc_configure_target(adbc-driver-framework-test)
+endif()
diff --git a/c/driver/framework/base_connection.h 
b/c/driver/framework/base_connection.h
index 0271639c6..d75265bde 100644
--- a/c/driver/framework/base_connection.h
+++ b/c/driver/framework/base_connection.h
@@ -25,11 +25,7 @@
 #include <vector>
 
 #include <arrow-adbc/adbc.h>
-#include <nanoarrow/nanoarrow.h>
-#include <nanoarrow/nanoarrow.hpp>
 
-#include "driver/common/options.h"
-#include "driver/common/utils.h"
 #include "driver/framework/base_driver.h"
 #include "driver/framework/catalog.h"
 #include "driver/framework/objects.h"
@@ -71,8 +67,8 @@ class ConnectionBase : public ObjectBase {
   AdbcStatusCode Commit(AdbcError* error) {
     switch (autocommit_) {
       case AutocommitState::kAutocommit:
-        return status::InvalidState("{} No active transaction, cannot commit",
-                                    Derived::kErrorPrefix)
+        return status::InvalidState(Derived::kErrorPrefix,
+                                    " No active transaction, cannot commit")
             .ToAdbc(error);
       case AutocommitState::kTransaction:
         return impl().CommitImpl().ToAdbc(error);
@@ -84,36 +80,14 @@ class ConnectionBase : public ObjectBase {
   /// \internal
   AdbcStatusCode GetInfo(const uint32_t* info_codes, size_t info_codes_length,
                          ArrowArrayStream* out, AdbcError* error) {
-    std::vector<uint32_t> codes(info_codes, info_codes + info_codes_length);
-    RAISE_RESULT(error, auto infos, impl().InfoImpl(codes));
-
-    nanoarrow::UniqueSchema schema;
-    nanoarrow::UniqueArray array;
-    RAISE_STATUS(error, AdbcInitConnectionGetInfoSchema(schema.get(), 
array.get()));
-
-    for (const auto& info : infos) {
-      RAISE_STATUS(
-          error,
-          std::visit(
-              [&](auto&& value) -> Status {
-                using T = std::decay_t<decltype(value)>;
-                if constexpr (std::is_same_v<T, std::string>) {
-                  return AdbcConnectionGetInfoAppendString(array.get(), 
info.code, value);
-                } else if constexpr (std::is_same_v<T, int64_t>) {
-                  return AdbcConnectionGetInfoAppendInt(array.get(), 
info.code, value);
-                } else {
-                  static_assert(!sizeof(T), "info value type not implemented");
-                }
-                return status::Ok();
-              },
-              info.value));
-      CHECK_NA(INTERNAL, ArrowArrayFinishElement(array.get()), error);
+    if (!out) {
+      RAISE_STATUS(error, status::InvalidArgument("out must be non-null"));
     }
 
-    struct ArrowError na_error = {0};
-    CHECK_NA_DETAIL(INTERNAL, ArrowArrayFinishBuildingDefault(array.get(), 
&na_error),
-                    &na_error, error);
-    return BatchToArrayStream(array.get(), schema.get(), out, error);
+    std::vector<uint32_t> codes(info_codes, info_codes + info_codes_length);
+    RAISE_RESULT(error, auto infos, impl().InfoImpl(codes));
+    RAISE_STATUS(error, AdbcGetInfo(infos, out));
+    return ADBC_STATUS_OK;
   }
 
   /// \internal
@@ -152,20 +126,17 @@ class ConnectionBase : public ObjectBase {
         depth = GetObjectsDepth::kTables;
         break;
       default:
-        return status::InvalidArgument("{} GetObjects: invalid depth {}",
-                                       Derived::kErrorPrefix, c_depth)
+        return status::InvalidArgument(Derived::kErrorPrefix,
+                                       " GetObjects: invalid depth ", c_depth)
             .ToAdbc(error);
     }
 
     RAISE_RESULT(error, auto helper, impl().GetObjectsImpl());
-    nanoarrow::UniqueSchema schema;
-    nanoarrow::UniqueArray array;
-    auto status =
-        BuildGetObjects(helper.get(), depth, catalog_filter, schema_filter, 
table_filter,
-                        column_filter, table_type_filter, schema.get(), 
array.get());
+    auto status = BuildGetObjects(helper.get(), depth, catalog_filter, 
schema_filter,
+                                  table_filter, column_filter, 
table_type_filter, out);
     RAISE_STATUS(error, helper->Close());
     RAISE_STATUS(error, status);
-    return BatchToArrayStream(array.get(), schema.get(), out, error);
+    return ADBC_STATUS_OK;
   }
 
   /// \internal
@@ -210,8 +181,8 @@ class ConnectionBase : public ObjectBase {
                                 const char* table_name, ArrowSchema* schema,
                                 AdbcError* error) {
     if (!table_name) {
-      return status::InvalidArgument("{} GetTableSchema: must provide 
table_name",
-                                     Derived::kErrorPrefix)
+      return status::InvalidArgument(Derived::kErrorPrefix,
+                                     " GetTableSchema: must provide 
table_name")
           .ToAdbc(error);
     }
     std::memset(schema, 0, sizeof(*schema));
@@ -228,36 +199,13 @@ class ConnectionBase : public ObjectBase {
 
   /// \internal
   AdbcStatusCode GetTableTypes(ArrowArrayStream* out, AdbcError* error) {
-    RAISE_RESULT(error, std::vector<std::string> table_types, 
impl().GetTableTypesImpl());
-
-    nanoarrow::UniqueArray array;
-    nanoarrow::UniqueSchema schema;
-    ArrowSchemaInit(schema.get());
-
-    CHECK_NA(INTERNAL, ArrowSchemaSetType(schema.get(), 
NANOARROW_TYPE_STRUCT), error);
-    CHECK_NA(INTERNAL, ArrowSchemaAllocateChildren(schema.get(), 
/*num_columns=*/1),
-             error);
-    ArrowSchemaInit(schema.get()->children[0]);
-    CHECK_NA(INTERNAL,
-             ArrowSchemaSetType(schema.get()->children[0], 
NANOARROW_TYPE_STRING), error);
-    CHECK_NA(INTERNAL, ArrowSchemaSetName(schema.get()->children[0], 
"table_type"),
-             error);
-    schema.get()->children[0]->flags &= ~ARROW_FLAG_NULLABLE;
-
-    CHECK_NA(INTERNAL, ArrowArrayInitFromSchema(array.get(), schema.get(), 
NULL), error);
-    CHECK_NA(INTERNAL, ArrowArrayStartAppending(array.get()), error);
-
-    for (std::string const& table_type : table_types) {
-      CHECK_NA(
-          INTERNAL,
-          ArrowArrayAppendString(array->children[0], 
ArrowCharView(table_type.c_str())),
-          error);
-      CHECK_NA(INTERNAL, ArrowArrayFinishElement(array.get()), error);
+    if (!out) {
+      RAISE_STATUS(error, status::InvalidArgument("out must be non-null"));
     }
 
-    CHECK_NA(INTERNAL, ArrowArrayFinishBuildingDefault(array.get(), NULL), 
error);
-
-    return BatchToArrayStream(array.get(), schema.get(), out, error);
+    RAISE_RESULT(error, std::vector<std::string> table_types, 
impl().GetTableTypesImpl());
+    RAISE_STATUS(error, AdbcGetTableTypes(table_types, out));
+    return ADBC_STATUS_OK;
   }
 
   /// \internal
@@ -276,8 +224,8 @@ class ConnectionBase : public ObjectBase {
   AdbcStatusCode Rollback(AdbcError* error) {
     switch (autocommit_) {
       case AutocommitState::kAutocommit:
-        return status::InvalidState("{} No active transaction, cannot 
rollback",
-                                    Derived::kErrorPrefix)
+        return status::InvalidState(Derived::kErrorPrefix,
+                                    " No active transaction, cannot rollback")
             .ToAdbc(error);
       case AutocommitState::kTransaction:
         return impl().RollbackImpl().ToAdbc(error);
@@ -352,12 +300,12 @@ class ConnectionBase : public ObjectBase {
       }
       return status::Ok();
     }
-    return status::NotImplemented("{} Unknown connection option {}={}",
-                                  Derived::kErrorPrefix, key, value);
+    return status::NotImplemented(Derived::kErrorPrefix, " Unknown connection 
option ",
+                                  key, "=", value.Format());
   }
 
   Status ToggleAutocommitImpl(bool enable_autocommit) {
-    return status::NotImplemented("{} Cannot change autocommit", 
Derived::kErrorPrefix);
+    return status::NotImplemented(Derived::kErrorPrefix, " Cannot change 
autocommit");
   }
 
  protected:
diff --git a/c/driver/framework/base_database.h 
b/c/driver/framework/base_database.h
index 5d043fc7e..28201b055 100644
--- a/c/driver/framework/base_database.h
+++ b/c/driver/framework/base_database.h
@@ -68,8 +68,8 @@ class DatabaseBase : public ObjectBase {
 
   /// \brief Set an option.  May be called prior to InitImpl.
   virtual Status SetOptionImpl(std::string_view key, Option value) {
-    return status::NotImplemented("{} Unknown database option {}={}",
-                                  Derived::kErrorPrefix, key, value);
+    return status::NotImplemented(Derived::kErrorPrefix, " Unknown database 
option ", key,
+                                  "=", value.Format());
   }
 
  private:
diff --git a/c/driver/framework/base_driver.cc 
b/c/driver/framework/base_driver.cc
index cebaae628..49b2406c7 100644
--- a/c/driver/framework/base_driver.cc
+++ b/c/driver/framework/base_driver.cc
@@ -29,7 +29,7 @@ Result<bool> Option::AsBool() const {
             return false;
           }
         }
-        return status::InvalidArgument("Invalid boolean value {}", *this);
+        return status::InvalidArgument("Invalid boolean value ", 
this->Format());
       },
       value_);
 }
@@ -46,15 +46,15 @@ Result<int64_t> Option::AsInt() const {
           auto end = value.data() + value.size();
           auto result = std::from_chars(begin, end, parsed);
           if (result.ec != std::errc()) {
-            return status::InvalidArgument("Invalid integer value '{}': not an 
integer",
-                                           value);
+            return status::InvalidArgument("Invalid integer value '", value,
+                                           "': not an integer", value);
           } else if (result.ptr != end) {
-            return status::InvalidArgument("Invalid integer value '{}': 
trailing data",
-                                           value);
+            return status::InvalidArgument("Invalid integer value '", value,
+                                           "': trailing data", value);
           }
           return parsed;
         }
-        return status::InvalidArgument("Invalid integer value {}", *this);
+        return status::InvalidArgument("Invalid integer value ", 
this->Format());
       },
       value_);
 }
@@ -66,7 +66,24 @@ Result<std::string_view> Option::AsString() const {
         if constexpr (std::is_same_v<T, std::string>) {
           return value;
         }
-        return status::InvalidArgument("Invalid string value {}", *this);
+        return status::InvalidArgument("Invalid string value {}", 
this->Format());
+      },
+      value_);
+}
+
+std::string Option::Format() const {
+  return std::visit(
+      [&](auto&& value) -> std::string {
+        using T = std::decay_t<decltype(value)>;
+        if constexpr (std::is_same_v<T, adbc::driver::Option::Unset>) {
+          return "(NULL)";
+        } else if constexpr (std::is_same_v<T, std::string>) {
+          return std::string("'") + value + "'";
+        } else if constexpr (std::is_same_v<T, std::vector<uint8_t>>) {
+          return std::string("(") + std::to_string(value.size()) + " bytes)";
+        } else {
+          return std::to_string(value);
+        }
       },
       value_);
 }
@@ -157,4 +174,5 @@ AdbcStatusCode Option::CGet(double* out, AdbcError* error) 
const {
       },
       value_);
 }
+
 }  // namespace adbc::driver
diff --git a/c/driver/framework/base_driver.h b/c/driver/framework/base_driver.h
index da6c39443..e241f6e83 100644
--- a/c/driver/framework/base_driver.h
+++ b/c/driver/framework/base_driver.h
@@ -29,10 +29,7 @@
 #include <vector>
 
 #include <arrow-adbc/adbc.h>
-#include <fmt/core.h>
-#include <fmt/format.h>
 
-#include "driver/common/utils.h"
 #include "driver/framework/status.h"
 
 /// \file base.h ADBC Driver Framework
@@ -91,6 +88,9 @@ class Option {
   /// \brief Get the value if it is a string.
   Result<std::string_view> AsString() const;
 
+  /// \brief Provide a human-readable summary of the value
+  std::string Format() const;
+
  private:
   Value value_;
 
@@ -623,26 +623,3 @@ class Driver {
 };
 
 }  // namespace adbc::driver
-
-/// \brief Formatter for Option values.
-template <>
-struct fmt::formatter<adbc::driver::Option> : 
fmt::nested_formatter<std::string_view> {
-  auto format(const adbc::driver::Option& option, fmt::format_context& ctx) 
const {
-    return write_padded(ctx, [=](auto out) {
-      return std::visit(
-          [&](auto&& value) {
-            using T = std::decay_t<decltype(value)>;
-            if constexpr (std::is_same_v<T, adbc::driver::Option::Unset>) {
-              return fmt::format_to(out, "(NULL)");
-            } else if constexpr (std::is_same_v<T, std::string>) {
-              return fmt::format_to(out, "'{}'", value);
-            } else if constexpr (std::is_same_v<T, std::vector<uint8_t>>) {
-              return fmt::format_to(out, "({} bytes)", value.size());
-            } else {
-              return fmt::format_to(out, "{}", value);
-            }
-          },
-          option.value());
-    });
-  }
-};
diff --git a/c/driver/framework/base_driver_test.cc 
b/c/driver/framework/base_driver_test.cc
new file mode 100644
index 000000000..dfc1e38ea
--- /dev/null
+++ b/c/driver/framework/base_driver_test.cc
@@ -0,0 +1,142 @@
+// 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 <gtest/gtest.h>
+#include <cstring>
+
+#include <arrow-adbc/adbc.h>
+#include "driver/framework/base_connection.h"
+#include "driver/framework/base_database.h"
+#include "driver/framework/base_driver.h"
+#include "driver/framework/base_statement.h"
+
+// Self-contained version of the Handle
+static inline void clean_up(AdbcDriver* ptr) { ptr->release(ptr, nullptr); }
+
+static inline void clean_up(AdbcDatabase* ptr) {
+  ptr->private_driver->DatabaseRelease(ptr, nullptr);
+}
+
+static inline void clean_up(AdbcConnection* ptr) {
+  ptr->private_driver->ConnectionRelease(ptr, nullptr);
+}
+
+static inline void clean_up(AdbcStatement* ptr) {
+  ptr->private_driver->StatementRelease(ptr, nullptr);
+}
+
+template <typename T>
+class Handle {
+ public:
+  explicit Handle(T* value) : value_(value) {}
+
+  ~Handle() { clean_up(value_); }
+
+ private:
+  T* value_;
+};
+
+namespace {
+
+class VoidDatabase : public adbc::driver::DatabaseBase<VoidDatabase> {
+ public:
+  [[maybe_unused]] constexpr static std::string_view kErrorPrefix = "[void]";
+};
+
+class VoidConnection : public adbc::driver::ConnectionBase<VoidConnection> {
+ public:
+  [[maybe_unused]] constexpr static std::string_view kErrorPrefix = "[void]";
+};
+
+class VoidStatement : public adbc::driver::StatementBase<VoidStatement> {
+ public:
+  [[maybe_unused]] constexpr static std::string_view kErrorPrefix = "[void]";
+};
+
+using VoidDriver = adbc::driver::Driver<VoidDatabase, VoidConnection, 
VoidStatement>;
+}  // namespace
+
+AdbcStatusCode VoidDriverInitFunc(int version, void* raw_driver, AdbcError* 
error) {
+  return VoidDriver::Init(version, raw_driver, error);
+}
+
+TEST(TestDriverBase, TestVoidDriverMethods) {
+  // Checks that wires are plugged in for a framework-based driver
+
+  struct AdbcDriver driver;
+  memset(&driver, 0, sizeof(driver));
+  ASSERT_EQ(VoidDriverInitFunc(ADBC_VERSION_1_1_0, &driver, nullptr), 
ADBC_STATUS_OK);
+  Handle<AdbcDriver> driver_handle(&driver);
+
+  // Database methods are only option related
+  struct AdbcDatabase database;
+  memset(&database, 0, sizeof(database));
+  ASSERT_EQ(driver.DatabaseNew(&database, nullptr), ADBC_STATUS_OK);
+  database.private_driver = &driver;
+  Handle<AdbcDatabase> database_handle(&database);
+  ASSERT_EQ(driver.DatabaseInit(&database, nullptr), ADBC_STATUS_OK);
+
+  // Test connection methods
+  struct AdbcConnection connection;
+  memset(&connection, 0, sizeof(connection));
+  ASSERT_EQ(driver.ConnectionNew(&connection, nullptr), ADBC_STATUS_OK);
+  connection.private_driver = &driver;
+  Handle<AdbcConnection> connection_handle(&connection);
+  ASSERT_EQ(driver.ConnectionInit(&connection, &database, nullptr), 
ADBC_STATUS_OK);
+
+  EXPECT_EQ(driver.ConnectionCommit(&connection, nullptr), 
ADBC_STATUS_INVALID_STATE);
+  EXPECT_EQ(driver.ConnectionGetInfo(&connection, nullptr, 0, nullptr, 
nullptr),
+            ADBC_STATUS_INVALID_ARGUMENT);
+  EXPECT_EQ(driver.ConnectionGetObjects(&connection, 0, nullptr, nullptr, 0, 
nullptr,
+                                        nullptr, nullptr, nullptr),
+            ADBC_STATUS_NOT_IMPLEMENTED);
+  EXPECT_EQ(driver.ConnectionGetTableSchema(&connection, nullptr, nullptr, 
nullptr,
+                                            nullptr, nullptr),
+            ADBC_STATUS_INVALID_ARGUMENT);
+  EXPECT_EQ(driver.ConnectionGetTableTypes(&connection, nullptr, nullptr),
+            ADBC_STATUS_INVALID_ARGUMENT);
+  EXPECT_EQ(driver.ConnectionReadPartition(&connection, nullptr, 0, nullptr, 
nullptr),
+            ADBC_STATUS_NOT_IMPLEMENTED);
+  EXPECT_EQ(driver.ConnectionRollback(&connection, nullptr), 
ADBC_STATUS_INVALID_STATE);
+  EXPECT_EQ(driver.ConnectionCancel(&connection, nullptr), 
ADBC_STATUS_NOT_IMPLEMENTED);
+  EXPECT_EQ(driver.ConnectionGetStatistics(&connection, nullptr, nullptr, 
nullptr, 0,
+                                           nullptr, nullptr),
+            ADBC_STATUS_NOT_IMPLEMENTED);
+  EXPECT_EQ(driver.ConnectionGetStatisticNames(&connection, nullptr, nullptr),
+            ADBC_STATUS_NOT_IMPLEMENTED);
+
+  // Test statement methods
+  struct AdbcStatement statement;
+  memset(&statement, 0, sizeof(statement));
+  ASSERT_EQ(driver.StatementNew(&connection, &statement, nullptr), 
ADBC_STATUS_OK);
+  statement.private_driver = &driver;
+  Handle<AdbcStatement> statement_handle(&statement);
+
+  EXPECT_EQ(driver.StatementExecuteQuery(&statement, nullptr, nullptr, 
nullptr),
+            ADBC_STATUS_INVALID_STATE);
+  EXPECT_EQ(driver.StatementExecuteSchema(&statement, nullptr, nullptr),
+            ADBC_STATUS_NOT_IMPLEMENTED);
+  EXPECT_EQ(driver.StatementPrepare(&statement, nullptr), 
ADBC_STATUS_INVALID_STATE);
+  EXPECT_EQ(driver.StatementSetSqlQuery(&statement, "", nullptr), 
ADBC_STATUS_OK);
+  EXPECT_EQ(driver.StatementSetSubstraitPlan(&statement, nullptr, 0, nullptr),
+            ADBC_STATUS_NOT_IMPLEMENTED);
+  EXPECT_EQ(driver.StatementBind(&statement, nullptr, nullptr, nullptr),
+            ADBC_STATUS_INVALID_ARGUMENT);
+  EXPECT_EQ(driver.StatementBindStream(&statement, nullptr, nullptr),
+            ADBC_STATUS_INVALID_ARGUMENT);
+  EXPECT_EQ(driver.StatementCancel(&statement, nullptr), 
ADBC_STATUS_NOT_IMPLEMENTED);
+}
diff --git a/c/driver/framework/base_statement.h 
b/c/driver/framework/base_statement.h
index 362ce2291..e3bd0fc0f 100644
--- a/c/driver/framework/base_statement.h
+++ b/c/driver/framework/base_statement.h
@@ -25,43 +25,11 @@
 #include <variant>
 #include <vector>
 
-#include "driver/common/options.h"
 #include "driver/framework/base_driver.h"
 #include "driver/framework/status.h"
 
 namespace adbc::driver {
 
-/// One-value ArrowArrayStream used to unify the implementations of Bind
-struct OneValueStream {
-  struct ArrowSchema schema;
-  struct ArrowArray array;
-
-  static int GetSchema(struct ArrowArrayStream* self, struct ArrowSchema* out) 
{
-    OneValueStream* stream = static_cast<OneValueStream*>(self->private_data);
-    return ArrowSchemaDeepCopy(&stream->schema, out);
-  }
-  static int GetNext(struct ArrowArrayStream* self, struct ArrowArray* out) {
-    OneValueStream* stream = static_cast<OneValueStream*>(self->private_data);
-    *out = stream->array;
-    stream->array.release = nullptr;
-    return 0;
-  }
-  static const char* GetLastError(struct ArrowArrayStream* self) { return 
NULL; }
-  static void Release(struct ArrowArrayStream* self) {
-    OneValueStream* stream = static_cast<OneValueStream*>(self->private_data);
-    if (stream->schema.release) {
-      stream->schema.release(&stream->schema);
-      stream->schema.release = nullptr;
-    }
-    if (stream->array.release) {
-      stream->array.release(&stream->array);
-      stream->array.release = nullptr;
-    }
-    delete stream;
-    self->release = nullptr;
-  }
-};
-
 /// \brief A base implementation of a statement.
 template <typename Derived>
 class StatementBase : public ObjectBase {
@@ -110,30 +78,23 @@ class StatementBase : public ObjectBase {
 
   AdbcStatusCode Bind(ArrowArray* values, ArrowSchema* schema, AdbcError* 
error) {
     if (!values || !values->release) {
-      return status::InvalidArgument("{} Bind: must provide non-NULL array",
-                                     Derived::kErrorPrefix)
+      return status::InvalidArgument(Derived::kErrorPrefix,
+                                     " Bind: must provide non-NULL array")
           .ToAdbc(error);
     } else if (!schema || !schema->release) {
-      return status::InvalidArgument("{} Bind: must provide non-NULL stream",
-                                     Derived::kErrorPrefix)
+      return status::InvalidArgument(Derived::kErrorPrefix,
+                                     " Bind: must provide non-NULL stream")
           .ToAdbc(error);
     }
     if (bind_parameters_.release) bind_parameters_.release(&bind_parameters_);
-    // Make a one-value stream
-    bind_parameters_.private_data = new OneValueStream{*schema, *values};
-    bind_parameters_.get_schema = &OneValueStream::GetSchema;
-    bind_parameters_.get_next = &OneValueStream::GetNext;
-    bind_parameters_.get_last_error = &OneValueStream::GetLastError;
-    bind_parameters_.release = &OneValueStream::Release;
-    std::memset(values, 0, sizeof(*values));
-    std::memset(schema, 0, sizeof(*schema));
+    AdbcMakeArrayStream(schema, values, &bind_parameters_);
     return ADBC_STATUS_OK;
   }
 
   AdbcStatusCode BindStream(ArrowArrayStream* stream, AdbcError* error) {
     if (!stream || !stream->release) {
-      return status::InvalidArgument("{} BindStream: must provide non-NULL 
stream",
-                                     Derived::kErrorPrefix)
+      return status::InvalidArgument(Derived::kErrorPrefix,
+                                     " BindStream: must provide non-NULL 
stream")
           .ToAdbc(error);
     }
     if (bind_parameters_.release) bind_parameters_.release(&bind_parameters_);
@@ -157,14 +118,13 @@ class StatementBase : public ObjectBase {
         [&](auto&& state) -> AdbcStatusCode {
           using T = std::decay_t<decltype(state)>;
           if constexpr (std::is_same_v<T, EmptyState>) {
-            return status::InvalidState(
-                       "{} Cannot ExecuteQuery without setting the query",
-                       Derived::kErrorPrefix)
+            return status::InvalidState(Derived::kErrorPrefix,
+                                        " Cannot ExecuteQuery without setting 
the query")
                 .ToAdbc(error);
           } else if constexpr (std::is_same_v<T, IngestState>) {
             if (stream) {
-              return status::InvalidState("{} Cannot ingest with result set",
-                                          Derived::kErrorPrefix)
+              return status::InvalidState(Derived::kErrorPrefix,
+                                          " Cannot ingest with result set")
                   .ToAdbc(error);
             }
             RAISE_RESULT(error, int64_t rows, impl().ExecuteIngestImpl(state));
@@ -201,19 +161,19 @@ class StatementBase : public ObjectBase {
           using T = std::decay_t<decltype(state)>;
           if constexpr (std::is_same_v<T, EmptyState>) {
             return status::InvalidState(
-                       "{} Cannot GetParameterSchema without setting the 
query",
-                       Derived::kErrorPrefix)
+                       Derived::kErrorPrefix,
+                       " Cannot GetParameterSchema without setting the query")
                 .ToAdbc(error);
           } else if constexpr (std::is_same_v<T, IngestState>) {
-            return status::InvalidState("{} Cannot GetParameterSchema in bulk 
ingestion",
-                                        Derived::kErrorPrefix)
+            return status::InvalidState(Derived::kErrorPrefix,
+                                        " Cannot GetParameterSchema in bulk 
ingestion")
                 .ToAdbc(error);
           } else if constexpr (std::is_same_v<T, PreparedState>) {
             return impl().GetParameterSchemaImpl(state, schema).ToAdbc(error);
           } else if constexpr (std::is_same_v<T, QueryState>) {
             return status::InvalidState(
-                       "{} Cannot GetParameterSchema without calling Prepare",
-                       Derived::kErrorPrefix)
+                       Derived::kErrorPrefix,
+                       " Cannot GetParameterSchema without calling Prepare")
                 .ToAdbc(error);
           } else {
             static_assert(!sizeof(T), "case not implemented");
@@ -236,12 +196,12 @@ class StatementBase : public ObjectBase {
                               using T = std::decay_t<decltype(state)>;
                               if constexpr (std::is_same_v<T, EmptyState>) {
                                 return status::InvalidState(
-                                    "{} Cannot Prepare without setting the 
query",
-                                    Derived::kErrorPrefix);
+                                    Derived::kErrorPrefix,
+                                    " Cannot Prepare without setting the 
query");
                               } else if constexpr (std::is_same_v<T, 
IngestState>) {
                                 return status::InvalidState(
-                                    "{} Cannot Prepare without setting the 
query",
-                                    Derived::kErrorPrefix);
+                                    Derived::kErrorPrefix,
+                                    " Cannot Prepare without setting the 
query");
                               } else if constexpr (std::is_same_v<T, 
PreparedState>) {
                                 // No-op
                                 return status::Ok();
@@ -291,8 +251,8 @@ class StatementBase : public ObjectBase {
         state.table_does_not_exist_ = TableDoesNotExist::kCreate;
         state.table_exists_ = TableExists::kReplace;
       } else {
-        return status::InvalidArgument("{} Invalid ingest mode '{}'",
-                                       Derived::kErrorPrefix, key, value)
+        return status::InvalidArgument(Derived::kErrorPrefix, " Invalid ingest 
mode '",
+                                       key, "': ", value.Format())
             .ToAdbc(error);
       }
       return ADBC_STATUS_OK;
@@ -360,46 +320,46 @@ class StatementBase : public ObjectBase {
   }
 
   Result<int64_t> ExecuteIngestImpl(IngestState& state) {
-    return status::NotImplemented("{} Bulk ingest is not implemented",
-                                  Derived::kErrorPrefix);
+    return status::NotImplemented(Derived::kErrorPrefix,
+                                  " Bulk ingest is not implemented");
   }
 
   Result<int64_t> ExecuteQueryImpl(PreparedState& state, ArrowArrayStream* 
stream) {
-    return status::NotImplemented("{} ExecuteQuery is not implemented",
-                                  Derived::kErrorPrefix);
+    return status::NotImplemented(Derived::kErrorPrefix,
+                                  " ExecuteQuery is not implemented");
   }
 
   Result<int64_t> ExecuteQueryImpl(QueryState& state, ArrowArrayStream* 
stream) {
-    return status::NotImplemented("{} ExecuteQuery is not implemented",
-                                  Derived::kErrorPrefix);
+    return status::NotImplemented(Derived::kErrorPrefix,
+                                  " ExecuteQuery is not implemented");
   }
 
   Result<int64_t> ExecuteUpdateImpl(PreparedState& state) {
-    return status::NotImplemented("{} ExecuteQuery (update) is not 
implemented",
-                                  Derived::kErrorPrefix);
+    return status::NotImplemented(Derived::kErrorPrefix,
+                                  " ExecuteQuery (update) is not implemented");
   }
 
   Result<int64_t> ExecuteUpdateImpl(QueryState& state) {
-    return status::NotImplemented("{} ExecuteQuery (update) is not 
implemented",
-                                  Derived::kErrorPrefix);
+    return status::NotImplemented(Derived::kErrorPrefix,
+                                  " ExecuteQuery (update) is not implemented");
   }
 
   Status GetParameterSchemaImpl(PreparedState& state, ArrowSchema* schema) {
-    return status::NotImplemented("{} GetParameterSchema is not implemented",
-                                  Derived::kErrorPrefix);
+    return status::NotImplemented(Derived::kErrorPrefix,
+                                  " GetParameterSchema is not implemented");
   }
 
   Status InitImpl(void* parent) { return status::Ok(); }
 
   Status PrepareImpl(QueryState& state) {
-    return status::NotImplemented("{} Prepare is not implemented", 
Derived::kErrorPrefix);
+    return status::NotImplemented(Derived::kErrorPrefix, " Prepare is not 
implemented");
   }
 
   Status ReleaseImpl() { return status::Ok(); }
 
   Status SetOptionImpl(std::string_view key, Option value) {
-    return status::NotImplemented("{} Unknown statement option {}={}",
-                                  Derived::kErrorPrefix, key, value);
+    return status::NotImplemented(Derived::kErrorPrefix, " Unknown statement 
option ",
+                                  key, "=", value.Format());
   }
 
  protected:
diff --git a/c/driver/framework/catalog.cc b/c/driver/framework/catalog.cc
index 3860ebb9e..d5b89ea88 100644
--- a/c/driver/framework/catalog.cc
+++ b/c/driver/framework/catalog.cc
@@ -17,9 +17,15 @@
 
 #include "driver/framework/catalog.h"
 
-#include <nanoarrow/nanoarrow.h>
+#include <nanoarrow/nanoarrow.hpp>
 
 namespace adbc::driver {
+
+void AdbcMakeArrayStream(struct ArrowSchema* schema, struct ArrowArray* array,
+                         struct ArrowArrayStream* out) {
+  nanoarrow::VectorArrayStream(schema, array).ToArrayStream(out);
+}
+
 Status AdbcInitConnectionGetInfoSchema(struct ArrowSchema* schema,
                                        struct ArrowArray* array) {
   ArrowSchemaInit(schema);
@@ -260,4 +266,63 @@ Status AdbcInitConnectionObjectsSchema(struct ArrowSchema* 
schema) {
 
   return status::Ok();
 }
+
+Status AdbcGetInfo(std::vector<InfoValue> infos, struct ArrowArrayStream* out) 
{
+  nanoarrow::UniqueSchema schema;
+  nanoarrow::UniqueArray array;
+
+  UNWRAP_STATUS(AdbcInitConnectionGetInfoSchema(schema.get(), array.get()));
+
+  for (const auto& info : infos) {
+    UNWRAP_STATUS(std::visit(
+        [&](auto&& value) -> Status {
+          using T = std::decay_t<decltype(value)>;
+          if constexpr (std::is_same_v<T, std::string>) {
+            return AdbcConnectionGetInfoAppendString(array.get(), info.code, 
value);
+          } else if constexpr (std::is_same_v<T, int64_t>) {
+            return AdbcConnectionGetInfoAppendInt(array.get(), info.code, 
value);
+          } else {
+            static_assert(!sizeof(T), "info value type not implemented");
+          }
+          return status::Ok();
+        },
+        info.value));
+    UNWRAP_ERRNO(Internal, ArrowArrayFinishElement(array.get()));
+  }
+
+  struct ArrowError na_error = {0};
+  UNWRAP_NANOARROW(na_error, Internal,
+                   ArrowArrayFinishBuildingDefault(array.get(), &na_error));
+  nanoarrow::VectorArrayStream(schema.get(), array.get()).ToArrayStream(out);
+  return status::Ok();
+}
+
+Status AdbcGetTableTypes(const std::vector<std::string>& table_types,
+                         struct ArrowArrayStream* out) {
+  nanoarrow::UniqueArray array;
+  nanoarrow::UniqueSchema schema;
+  ArrowSchemaInit(schema.get());
+
+  UNWRAP_ERRNO(Internal, ArrowSchemaSetType(schema.get(), 
NANOARROW_TYPE_STRUCT));
+  UNWRAP_ERRNO(Internal, ArrowSchemaAllocateChildren(schema.get(), 
/*num_columns=*/1));
+  ArrowSchemaInit(schema.get()->children[0]);
+  UNWRAP_ERRNO(Internal,
+               ArrowSchemaSetType(schema.get()->children[0], 
NANOARROW_TYPE_STRING));
+  UNWRAP_ERRNO(Internal, ArrowSchemaSetName(schema.get()->children[0], 
"table_type"));
+  schema.get()->children[0]->flags &= ~ARROW_FLAG_NULLABLE;
+
+  UNWRAP_ERRNO(Internal, ArrowArrayInitFromSchema(array.get(), schema.get(), 
NULL));
+  UNWRAP_ERRNO(Internal, ArrowArrayStartAppending(array.get()));
+
+  for (std::string const& table_type : table_types) {
+    UNWRAP_ERRNO(Internal, ArrowArrayAppendString(array->children[0],
+                                                  
ArrowCharView(table_type.c_str())));
+    UNWRAP_ERRNO(Internal, ArrowArrayFinishElement(array.get()));
+  }
+
+  UNWRAP_ERRNO(Internal, ArrowArrayFinishBuildingDefault(array.get(), 
nullptr));
+  nanoarrow::VectorArrayStream(schema.get(), array.get()).ToArrayStream(out);
+  return status::Ok();
+}
+
 }  // namespace adbc::driver
diff --git a/c/driver/framework/catalog.h b/c/driver/framework/catalog.h
index 39dcdcd73..8c0eff17e 100644
--- a/c/driver/framework/catalog.h
+++ b/c/driver/framework/catalog.h
@@ -142,6 +142,14 @@ struct InfoValue {
       : code(code), value(std::move(value)) {}
 };
 
+void AdbcMakeArrayStream(struct ArrowSchema* schema, struct ArrowArray* array,
+                         struct ArrowArrayStream* out);
+
+Status AdbcGetInfo(std::vector<InfoValue> infos, struct ArrowArrayStream* out);
+
+Status AdbcGetTableTypes(const std::vector<std::string>& table_types,
+                         struct ArrowArrayStream* out);
+
 Status AdbcInitConnectionGetInfoSchema(struct ArrowSchema* schema,
                                        struct ArrowArray* array);
 Status AdbcConnectionGetInfoAppendString(struct ArrowArray* array, uint32_t 
info_code,
diff --git a/c/driver/framework/objects.cc b/c/driver/framework/objects.cc
index c5098e07b..1d5e9105f 100644
--- a/c/driver/framework/objects.cc
+++ b/c/driver/framework/objects.cc
@@ -19,6 +19,8 @@
 
 #include <string_view>
 
+#include "nanoarrow/nanoarrow.hpp"
+
 #include "driver/framework/catalog.h"
 #include "driver/framework/status.h"
 
@@ -356,9 +358,14 @@ Status BuildGetObjects(GetObjectsHelper* helper, 
GetObjectsDepth depth,
                        std::optional<std::string_view> table_filter,
                        std::optional<std::string_view> column_filter,
                        const std::vector<std::string_view>& table_types,
-                       struct ArrowSchema* schema, struct ArrowArray* array) {
-  return GetObjectsBuilder(helper, depth, catalog_filter, schema_filter, 
table_filter,
-                           column_filter, table_types, schema, array)
-      .Build();
+                       struct ArrowArrayStream* out) {
+  nanoarrow::UniqueSchema schema;
+  nanoarrow::UniqueArray array;
+  UNWRAP_STATUS(GetObjectsBuilder(helper, depth, catalog_filter, schema_filter,
+                                  table_filter, column_filter, table_types, 
schema.get(),
+                                  array.get())
+                    .Build());
+  nanoarrow::VectorArrayStream(schema.get(), array.get()).ToArrayStream(out);
+  return status::Ok();
 }
 }  // namespace adbc::driver
diff --git a/c/driver/framework/objects.h b/c/driver/framework/objects.h
index 0d14c5c6b..ffd2004e0 100644
--- a/c/driver/framework/objects.h
+++ b/c/driver/framework/objects.h
@@ -29,12 +29,12 @@
 
 namespace adbc::driver {
 /// \brief A helper that implements GetObjects.
-/// The schema/array/helper lifetime are caller-managed.
+/// The out/helper lifetime are caller-managed.
 Status BuildGetObjects(GetObjectsHelper* helper, GetObjectsDepth depth,
                        std::optional<std::string_view> catalog_filter,
                        std::optional<std::string_view> schema_filter,
                        std::optional<std::string_view> table_filter,
                        std::optional<std::string_view> column_filter,
                        const std::vector<std::string_view>& table_types,
-                       struct ArrowSchema* schema, struct ArrowArray* array);
+                       struct ArrowArrayStream* out);
 }  // namespace adbc::driver
diff --git a/c/driver/framework/status.h b/c/driver/framework/status.h
index ef81838d9..d7952a69f 100644
--- a/c/driver/framework/status.h
+++ b/c/driver/framework/status.h
@@ -20,13 +20,17 @@
 #include <cassert>
 #include <cstring>
 #include <memory>
+#include <sstream>
 #include <string>
 #include <utility>
 #include <variant>
 #include <vector>
 
-#include <arrow-adbc/adbc.h>
+#if defined(ADBC_FRAMEWORK_USE_FMT)
 #include <fmt/core.h>
+#endif
+
+#include <arrow-adbc/adbc.h>
 
 /// \file status.h
 
@@ -248,11 +252,14 @@ class Result {
 
 namespace adbc::driver::status {
 
-#define STATUS_CTOR(NAME, CODE)                                                
 \
-  template <typename... Args>                                                  
 \
-  static Status NAME(std::string_view format_string, Args&&... args) {         
 \
-    auto message = fmt::vformat(format_string, 
fmt::make_format_args(args...)); \
-    return Status(ADBC_STATUS_##CODE, std::move(message));                     
 \
+inline driver::Status Ok() { return driver::Status(); }
+
+#define STATUS_CTOR(NAME, CODE)                  \
+  template <typename... Args>                    \
+  static Status NAME(Args&&... args) {           \
+    std::stringstream ss;                        \
+    ([&] { ss << args; }(), ...);                \
+    return Status(ADBC_STATUS_##CODE, ss.str()); \
   }
 
 // TODO: unit tests for internal utilities
@@ -266,27 +273,49 @@ STATUS_CTOR(Unknown, UNKNOWN)
 
 #undef STATUS_CTOR
 
-inline driver::Status Ok() { return driver::Status(); }
+}  // namespace adbc::driver::status
 
-#define UNWRAP_ERRNO_IMPL(NAME, CODE, RHS)                                     
          \
-  auto&& NAME = (RHS);                                                         
          \
-  if (NAME != 0) {                                                             
          \
-    return adbc::driver::status::CODE("Nanoarrow call failed: {} = ({}) {}", 
#RHS, NAME, \
-                                      std::strerror(NAME));                    
          \
+#if defined(ADBC_FRAMEWORK_USE_FMT)
+namespace adbc::driver::status::fmt {
+
+#define STATUS_CTOR(NAME, CODE)                                                
     \
+  template <typename... Args>                                                  
     \
+  static Status NAME(std::string_view format_string, Args&&... args) {         
     \
+    auto message = ::fmt::vformat(format_string, 
::fmt::make_format_args(args...)); \
+    return Status(ADBC_STATUS_##CODE, std::move(message));                     
     \
   }
 
-#define UNWRAP_ERRNO(CODE, RHS) \
-  UNWRAP_ERRNO_IMPL(UNWRAP_RESULT_NAME(driver_errno, __COUNTER__), CODE, RHS)
+// TODO: unit tests for internal utilities
+STATUS_CTOR(Internal, INTERNAL)
+STATUS_CTOR(InvalidArgument, INVALID_ARGUMENT)
+STATUS_CTOR(InvalidState, INVALID_STATE)
+STATUS_CTOR(IO, IO)
+STATUS_CTOR(NotFound, NOT_FOUND)
+STATUS_CTOR(NotImplemented, NOT_IMPLEMENTED)
+STATUS_CTOR(Unknown, UNKNOWN)
 
-#define UNWRAP_NANOARROW_IMPL(NAME, ERROR, CODE, RHS)                          
        \
+#undef STATUS_CTOR
+
+}  // namespace adbc::driver::status::fmt
+#endif
+
+#define UNWRAP_ERRNO_IMPL(NAME, CODE, RHS)                                     
        \
   auto&& NAME = (RHS);                                                         
        \
   if (NAME != 0) {                                                             
        \
-    return adbc::driver::status::CODE("Nanoarrow call failed: {} = ({}) {}. 
{}", #RHS, \
-                                      NAME, std::strerror(NAME), 
(ERROR).message);     \
+    return adbc::driver::status::CODE("Call failed: ", #RHS, " = (errno ", 
NAME, ") ", \
+                                      std::strerror(NAME));                    
        \
+  }
+
+#define UNWRAP_ERRNO(CODE, RHS) \
+  UNWRAP_ERRNO_IMPL(UNWRAP_RESULT_NAME(driver_errno, __COUNTER__), CODE, RHS)
+
+#define UNWRAP_NANOARROW_IMPL(NAME, ERROR, CODE, RHS)                          
          \
+  auto&& NAME = (RHS);                                                         
          \
+  if (NAME != 0) {                                                             
          \
+    return adbc::driver::status::CODE("nanoarrow call failed: ", #RHS, " = (", 
NAME,     \
+                                      ") ", std::strerror(NAME), ". ", 
(ERROR).message); \
   }
 
 #define UNWRAP_NANOARROW(ERROR, CODE, RHS)                                     
        \
   UNWRAP_NANOARROW_IMPL(UNWRAP_RESULT_NAME(driver_errno_na, __COUNTER__), 
ERROR, CODE, \
                         RHS)
-
-}  // namespace adbc::driver::status
diff --git a/c/driver/sqlite/sqlite.cc b/c/driver/sqlite/sqlite.cc
index c288ba625..debec7193 100644
--- a/c/driver/sqlite/sqlite.cc
+++ b/c/driver/sqlite/sqlite.cc
@@ -23,6 +23,7 @@
 #include <sqlite3.h>
 #include <nanoarrow/nanoarrow.hpp>
 
+#define ADBC_FRAMEWORK_USE_FMT
 #include "driver/common/options.h"
 #include "driver/common/utils.h"
 #include "driver/framework/base_connection.h"
@@ -112,7 +113,7 @@ class SqliteStringBuilder {
       } else if (rc == SQLITE_TOOBIG) {
         return status::Internal("query too long");
       } else if (rc != SQLITE_OK) {
-        return status::Internal("unknown SQLite error ({})", rc);
+        return status::fmt::Internal("unknown SQLite error ({})", rc);
       }
       len = sqlite3_str_length(str_);
       result_ = sqlite3_str_finish(str_);
@@ -142,7 +143,7 @@ class SqliteQuery {
 
   Result<bool> Next() {
     if (!stmt_) {
-      return status::Internal(
+      return status::fmt::Internal(
           "query already finished or never initialized\nquery was: {}", 
query_);
     }
     int rc = sqlite3_step(stmt_);
@@ -159,12 +160,12 @@ class SqliteQuery {
       int rc = sqlite3_finalize(stmt_);
       stmt_ = nullptr;
       if (rc != SQLITE_OK && rc != SQLITE_DONE) {
-        return status::Internal("failed to execute: {}\nquery was: {}",
-                                sqlite3_errmsg(conn_), query_);
+        return status::fmt::Internal("failed to execute: {}\nquery was: {}",
+                                     sqlite3_errmsg(conn_), query_);
       }
     } else if (rc != SQLITE_OK) {
-      return status::Internal("failed to execute: {}\nquery was: {}",
-                              sqlite3_errmsg(conn_), query_);
+      return status::fmt::Internal("failed to execute: {}\nquery was: {}",
+                                   sqlite3_errmsg(conn_), query_);
     }
     return status::Ok();
   }
@@ -513,9 +514,9 @@ class SqliteDatabase : public 
driver::DatabaseBase<SqliteDatabase> {
     if (rc != SQLITE_OK) {
       Status status;
       if (conn_) {
-        status = status::IO("failed to open '{}': {}", uri_, 
sqlite3_errmsg(conn));
+        status = status::fmt::IO("failed to open '{}': {}", uri_, 
sqlite3_errmsg(conn));
       } else {
-        status = status::IO("failed to open '{}': failed to allocate memory", 
uri_);
+        status = status::fmt::IO("failed to open '{}': failed to allocate 
memory", uri_);
       }
       (void)sqlite3_close(conn);
       return status;
@@ -532,8 +533,8 @@ class SqliteDatabase : public 
driver::DatabaseBase<SqliteDatabase> {
     if (conn_) {
       int rc = sqlite3_close_v2(conn_);
       if (rc != SQLITE_OK) {
-        return status::IO("failed to close connection: ({}) {}", rc,
-                          sqlite3_errmsg(conn_));
+        return status::fmt::IO("failed to close connection: ({}) {}", rc,
+                               sqlite3_errmsg(conn_));
       }
       conn_ = nullptr;
     }
@@ -594,7 +595,7 @@ class SqliteConnection : public 
driver::ConnectionBase<SqliteConnection> {
                            /*pzTail=*/nullptr);
     if (rc != SQLITE_OK) {
       (void)sqlite3_finalize(stmt);
-      return status::NotFound("GetTableSchema: {}", sqlite3_errmsg(conn_));
+      return status::fmt::NotFound("GetTableSchema: {}", 
sqlite3_errmsg(conn_));
     }
 
     nanoarrow::UniqueArrayStream stream;
@@ -606,7 +607,8 @@ class SqliteConnection : public 
driver::ConnectionBase<SqliteConnection> {
       int code = stream->get_schema(stream.get(), schema);
       if (code != 0) {
         (void)sqlite3_finalize(stmt);
-        return status::IO("failed to get schema: ({}) {}", code, 
std::strerror(code));
+        return status::fmt::IO("failed to get schema: ({}) {}", code,
+                               std::strerror(code));
       }
     }
     (void)sqlite3_finalize(stmt);
@@ -665,8 +667,8 @@ class SqliteConnection : public 
driver::ConnectionBase<SqliteConnection> {
     if (conn_) {
       int rc = sqlite3_close_v2(conn_);
       if (rc != SQLITE_OK) {
-        return status::IO("failed to close connection: ({}) {}", rc,
-                          sqlite3_errmsg(conn_));
+        return status::fmt::IO("failed to close connection: ({}) {}", rc,
+                               sqlite3_errmsg(conn_));
       }
       conn_ = nullptr;
     }
@@ -689,7 +691,8 @@ class SqliteConnection : public 
driver::ConnectionBase<SqliteConnection> {
       int rc = sqlite3_db_config(conn_, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION,
                                  enabled ? 1 : 0, nullptr);
       if (rc != SQLITE_OK) {
-        return status::IO("cannot enable extension loading: {}", 
sqlite3_errmsg(conn_));
+        return status::fmt::IO("cannot enable extension loading: {}",
+                               sqlite3_errmsg(conn_));
       }
       return status::Ok();
     } else if (key == kConnectionOptionLoadExtensionPath) {
@@ -702,9 +705,9 @@ class SqliteConnection : public 
driver::ConnectionBase<SqliteConnection> {
     } else if (key == kConnectionOptionLoadExtensionEntrypoint) {
 #if !defined(ADBC_SQLITE_WITH_NO_LOAD_EXTENSION)
       if (extension_path_.empty()) {
-        return status::InvalidState("{} can only be set after {}",
-                                    kConnectionOptionLoadExtensionEntrypoint,
-                                    kConnectionOptionLoadExtensionPath);
+        return status::fmt::InvalidState("{} can only be set after {}",
+                                         
kConnectionOptionLoadExtensionEntrypoint,
+                                         kConnectionOptionLoadExtensionPath);
       }
       const char* extension_entrypoint = nullptr;
       if (value.has_value()) {
@@ -716,7 +719,7 @@ class SqliteConnection : public 
driver::ConnectionBase<SqliteConnection> {
       int rc = sqlite3_load_extension(conn_, extension_path_.c_str(),
                                       extension_entrypoint, &message);
       if (rc != SQLITE_OK) {
-        auto status = status::Unknown(
+        auto status = status::fmt::Unknown(
             "failed to load extension {} (entrypoint {}): {}", extension_path_,
             extension_entrypoint ? extension_entrypoint : "(NULL)",
             message ? message : "(unknown error)");
@@ -782,15 +785,15 @@ class SqliteStatement : public 
driver::StatementBase<SqliteStatement> {
     // Parameter validation
 
     if (state.target_catalog && state.temporary) {
-      return status::InvalidState("{} Cannot set both {} and {}", kErrorPrefix,
-                                  ADBC_INGEST_OPTION_TARGET_CATALOG,
-                                  ADBC_INGEST_OPTION_TEMPORARY);
+      return status::fmt::InvalidState("{} Cannot set both {} and {}", 
kErrorPrefix,
+                                       ADBC_INGEST_OPTION_TARGET_CATALOG,
+                                       ADBC_INGEST_OPTION_TEMPORARY);
     } else if (state.target_schema) {
-      return status::NotImplemented("{} {} not supported", kErrorPrefix,
-                                    ADBC_INGEST_OPTION_TARGET_DB_SCHEMA);
+      return status::fmt::NotImplemented("{} {} not supported", kErrorPrefix,
+                                         ADBC_INGEST_OPTION_TARGET_DB_SCHEMA);
     } else if (!state.target_table) {
-      return status::InvalidState("{} Must set {}", kErrorPrefix,
-                                  ADBC_INGEST_OPTION_TARGET_TABLE);
+      return status::fmt::InvalidState("{} Must set {}", kErrorPrefix,
+                                       ADBC_INGEST_OPTION_TARGET_TABLE);
     }
 
     // Create statements for creating the table, inserting a row, and the 
table name
@@ -844,8 +847,9 @@ class SqliteStatement : public 
driver::StatementBase<SqliteStatement> {
 
       int status = ArrowSchemaViewInit(&view, binder_.schema.children[i], 
&arrow_error);
       if (status != 0) {
-        return status::Internal("failed to parse schema for column {}: {} 
({}): {}", i,
-                                std::strerror(status), status, 
arrow_error.message);
+        return status::fmt::Internal("failed to parse schema for column {}: {} 
({}): {}",
+                                     i, std::strerror(status), status,
+                                     arrow_error.message);
       }
 
       switch (view.type) {
@@ -924,8 +928,8 @@ class SqliteStatement : public 
driver::StatementBase<SqliteStatement> {
                                   &stmt, /*pzTail=*/nullptr);
       if (rc != SQLITE_OK) {
         std::ignore = sqlite3_finalize(stmt);
-        return status::Internal("failed to prepare: {}\nquery was: {}",
-                                sqlite3_errmsg(conn_), insert);
+        return status::fmt::Internal("failed to prepare: {}\nquery was: {}",
+                                     sqlite3_errmsg(conn_), insert);
       }
     }
     assert(stmt != nullptr);
@@ -972,8 +976,8 @@ class SqliteStatement : public 
driver::StatementBase<SqliteStatement> {
     const int64_t expected = sqlite3_bind_parameter_count(stmt_);
     const int64_t actual = binder_.schema.n_children;
     if (actual != expected) {
-      return status::InvalidState("parameter count mismatch: expected {} but 
found {}",
-                                  expected, actual);
+      return status::fmt::InvalidState(
+          "parameter count mismatch: expected {} but found {}", expected, 
actual);
     }
 
     auto status =
@@ -1000,8 +1004,8 @@ class SqliteStatement : public 
driver::StatementBase<SqliteStatement> {
     const int64_t expected = sqlite3_bind_parameter_count(stmt_);
     const int64_t actual = binder_.schema.n_children;
     if (actual != expected) {
-      return status::InvalidState("parameter count mismatch: expected {} but 
found {}",
-                                  expected, actual);
+      return status::fmt::InvalidState(
+          "parameter count mismatch: expected {} but found {}", expected, 
actual);
     }
 
     int64_t rows = 0;
@@ -1032,7 +1036,8 @@ class SqliteStatement : public 
driver::StatementBase<SqliteStatement> {
 
     if (sqlite3_reset(stmt_) != SQLITE_OK) {
       const char* msg = sqlite3_errmsg(conn_);
-      return status::IO("failed to execute query: {}", msg ? msg : "(unknown 
error)");
+      return status::fmt::IO("failed to execute query: {}",
+                             msg ? msg : "(unknown error)");
     }
 
     if (sqlite3_column_count(stmt_) == 0) {
@@ -1052,8 +1057,8 @@ class SqliteStatement : public 
driver::StatementBase<SqliteStatement> {
     int num_params = sqlite3_bind_parameter_count(stmt_);
     if (num_params < 0) {
       // Should not happen
-      return status::Internal("{} SQLite returned negative parameter count",
-                              kErrorPrefix);
+      return status::fmt::Internal("{} SQLite returned negative parameter 
count",
+                                   kErrorPrefix);
     }
 
     nanoarrow::UniqueSchema uschema;
@@ -1086,8 +1091,8 @@ class SqliteStatement : public 
driver::StatementBase<SqliteStatement> {
       int rc = sqlite3_finalize(stmt_);
       stmt_ = nullptr;
       if (rc != SQLITE_OK) {
-        return status::IO("{} Failed to finalize previous statement: ({}) {}",
-                          kErrorPrefix, rc, sqlite3_errmsg(conn_));
+        return status::fmt::IO("{} Failed to finalize previous statement: ({}) 
{}",
+                               kErrorPrefix, rc, sqlite3_errmsg(conn_));
       }
     }
 
@@ -1098,8 +1103,8 @@ class SqliteStatement : public 
driver::StatementBase<SqliteStatement> {
       std::string msg = sqlite3_errmsg(conn_);
       std::ignore = sqlite3_finalize(stmt_);
       stmt_ = NULL;
-      return status::InvalidArgument("{} Failed to prepare query: {}\nquery: 
{}",
-                                     kErrorPrefix, msg, state.query);
+      return status::fmt::InvalidArgument("{} Failed to prepare query: 
{}\nquery: {}",
+                                          kErrorPrefix, msg, state.query);
     }
     return status::Ok();
   }
@@ -1109,8 +1114,8 @@ class SqliteStatement : public 
driver::StatementBase<SqliteStatement> {
       int rc = sqlite3_finalize(stmt_);
       stmt_ = nullptr;
       if (rc != SQLITE_OK) {
-        return status::IO("{} Failed to finalize statement: ({}) {}", 
kErrorPrefix, rc,
-                          sqlite3_errmsg(conn_));
+        return status::fmt::IO("{} Failed to finalize statement: ({}) {}", 
kErrorPrefix,
+                               rc, sqlite3_errmsg(conn_));
       }
     }
     AdbcSqliteBinderRelease(&binder_);
@@ -1121,10 +1126,10 @@ class SqliteStatement : public 
driver::StatementBase<SqliteStatement> {
     if (key == kStatementOptionBatchRows) {
       UNWRAP_RESULT(int64_t batch_size, value.AsInt());
       if (batch_size >= std::numeric_limits<int>::max() || batch_size <= 0) {
-        return status::InvalidArgument(
+        return status::fmt::InvalidArgument(
             "{} Invalid statement option value {}={} (value is non-positive or 
out of "
             "range of int)",
-            kErrorPrefix, key, value);
+            kErrorPrefix, key, value.Format());
       }
       batch_size_ = static_cast<int>(batch_size);
       return status::Ok();
diff --git a/c/vendor/nanoarrow/nanoarrow.hpp b/c/vendor/nanoarrow/nanoarrow.hpp
index 5f8aabbac..49ba38f0d 100644
--- a/c/vendor/nanoarrow/nanoarrow.hpp
+++ b/c/vendor/nanoarrow/nanoarrow.hpp
@@ -19,7 +19,7 @@
 #include <string>
 #include <vector>
 
-#include "nanoarrow.h"
+#include "nanoarrow/nanoarrow.h"
 
 #ifndef NANOARROW_HPP_INCLUDED
 #define NANOARROW_HPP_INCLUDED
@@ -82,6 +82,23 @@ class Exception : public std::exception {
 
 /// @}
 
+namespace literals {
+
+/// \defgroup nanoarrow_hpp-string_view_helpers ArrowStringView helpers
+///
+/// Factories and equality comparison for ArrowStringView.
+///
+/// @{
+
+/// \brief User literal operator allowing ArrowStringView construction like 
"str"_asv
+inline ArrowStringView operator"" _asv(const char* data, std::size_t 
size_bytes) {
+  return {data, static_cast<int64_t>(size_bytes)};
+}
+
+// @}
+
+}  // namespace literals
+
 namespace internal {
 
 /// \defgroup nanoarrow_hpp-unique_base Base classes for Unique wrappers
@@ -826,7 +843,12 @@ class ViewArrayAsFixedSizeBytes {
 class ViewArrayStream {
  public:
   ViewArrayStream(ArrowArrayStream* stream, ArrowErrorCode* code, ArrowError* 
error)
-      : range_{Next{this, stream, UniqueArray()}}, code_{code}, error_{error} 
{}
+      : code_{code}, error_{error} {
+    // Using a slightly more verbose constructor to silence a warning that 
occurs
+    // on some versions of MSVC.
+    range_.next.self = this;
+    range_.next.stream = stream;
+  }
 
   ViewArrayStream(ArrowArrayStream* stream, ArrowError* error)
       : ViewArrayStream{stream, &internal_code_, error} {}
@@ -893,22 +915,11 @@ class ViewArrayStream {
 
 }  // namespace nanoarrow
 
-/// \defgroup nanoarrow_hpp-string_view_helpers ArrowStringView helpers
-///
-/// Factories and equality comparison for ArrowStringView.
-///
-/// @{
-
 /// \brief Equality comparison operator between ArrowStringView
+/// \ingroup nanoarrow_hpp-string_view_helpers
 inline bool operator==(ArrowStringView l, ArrowStringView r) {
   if (l.size_bytes != r.size_bytes) return false;
   return memcmp(l.data, r.data, l.size_bytes) == 0;
 }
 
-/// \brief User literal operator allowing ArrowStringView construction like 
"str"_sv
-inline ArrowStringView operator"" _v(const char* data, std::size_t size_bytes) 
{
-  return {data, static_cast<int64_t>(size_bytes)};
-}
-/// @}
-
 #endif

Reply via email to