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 38c21c231 refactor(c/driver/postgresql): Use GetObjectsHelper from 
framework to build objects (#2189)
38c21c231 is described below

commit 38c21c2311a59803559cb0091b3f34180c28b25f
Author: Dewey Dunnington <[email protected]>
AuthorDate: Fri Oct 18 02:35:09 2024 +0000

    refactor(c/driver/postgresql): Use GetObjectsHelper from framework to build 
objects (#2189)
    
    This PR migrates the PostgreSQL driver to use the same
    `GetObjectsHelper` as the SQLite driver (the one provided by the
    framework). This involved a few updates to the helper (notably: ensure
    the filter is passed to the `LoadXXX()` methods), and an update to the
    result helper to make it easier to store iteration state along a result
    as a member of the helper.
---
 c/driver/framework/objects.cc       |   8 +-
 c/driver/framework/objects.h        |  12 +-
 c/driver/postgresql/connection.cc   | 863 +++++++++++++++---------------------
 c/driver/postgresql/result_helper.h |  45 +-
 c/driver/sqlite/sqlite.cc           |  21 +-
 r/adbcpostgresql/configure          |   4 +-
 6 files changed, 424 insertions(+), 529 deletions(-)

diff --git a/c/driver/framework/objects.cc b/c/driver/framework/objects.cc
index eaf62351d..691f6e414 100644
--- a/c/driver/framework/objects.cc
+++ b/c/driver/framework/objects.cc
@@ -284,7 +284,7 @@ struct GetObjectsBuilder {
   }
 
   Status AppendCatalogs() {
-    UNWRAP_STATUS(helper->LoadCatalogs());
+    UNWRAP_STATUS(helper->LoadCatalogs(catalog_filter));
     while (true) {
       UNWRAP_RESULT(auto maybe_catalog, helper->NextCatalog());
       if (!maybe_catalog.has_value()) break;
@@ -302,7 +302,7 @@ struct GetObjectsBuilder {
   }
 
   Status AppendSchemas(std::string_view catalog) {
-    UNWRAP_STATUS(helper->LoadSchemas(catalog));
+    UNWRAP_STATUS(helper->LoadSchemas(catalog, schema_filter));
     while (true) {
       UNWRAP_RESULT(auto maybe_schema, helper->NextSchema());
       if (!maybe_schema.has_value()) break;
@@ -323,7 +323,7 @@ struct GetObjectsBuilder {
   }
 
   Status AppendTables(std::string_view catalog, std::string_view schema) {
-    UNWRAP_STATUS(helper->LoadTables(catalog, schema));
+    UNWRAP_STATUS(helper->LoadTables(catalog, schema, table_filter, 
table_types));
     while (true) {
       UNWRAP_RESULT(auto maybe_table, helper->NextTable());
       if (!maybe_table.has_value()) break;
@@ -348,7 +348,7 @@ struct GetObjectsBuilder {
 
   Status AppendColumns(std::string_view catalog, std::string_view schema,
                        std::string_view table) {
-    UNWRAP_STATUS(helper->LoadColumns(catalog, schema, table));
+    UNWRAP_STATUS(helper->LoadColumns(catalog, schema, table, column_filter));
     while (true) {
       UNWRAP_RESULT(auto maybe_column, helper->NextColumn());
       if (!maybe_column.has_value()) break;
diff --git a/c/driver/framework/objects.h b/c/driver/framework/objects.h
index a855265ca..3e74e7883 100644
--- a/c/driver/framework/objects.h
+++ b/c/driver/framework/objects.h
@@ -108,26 +108,30 @@ struct GetObjectsHelper {
     return status::NotImplemented("GetObjects");
   }
 
-  virtual Status LoadCatalogs() {
+  virtual Status LoadCatalogs(std::optional<std::string_view> catalog_filter) {
     return status::NotImplemented("GetObjects at depth = catalog");
   };
 
   virtual Result<std::optional<std::string_view>> NextCatalog() { return 
std::nullopt; }
 
-  virtual Status LoadSchemas(std::string_view catalog) {
+  virtual Status LoadSchemas(std::string_view catalog,
+                             std::optional<std::string_view> schema_filter) {
     return status::NotImplemented("GetObjects at depth = schema");
   };
 
   virtual Result<std::optional<std::string_view>> NextSchema() { return 
std::nullopt; }
 
-  virtual Status LoadTables(std::string_view catalog, std::string_view schema) 
{
+  virtual Status LoadTables(std::string_view catalog, std::string_view schema,
+                            std::optional<std::string_view> table_filter,
+                            const std::vector<std::string_view>& table_types) {
     return status::NotImplemented("GetObjects at depth = table");
   };
 
   virtual Result<std::optional<Table>> NextTable() { return std::nullopt; }
 
   virtual Status LoadColumns(std::string_view catalog, std::string_view schema,
-                             std::string_view table) {
+                             std::string_view table,
+                             std::optional<std::string_view> column_filter) {
     return status::NotImplemented("GetObjects at depth = column");
   };
 
diff --git a/c/driver/postgresql/connection.cc 
b/c/driver/postgresql/connection.cc
index ca88c0a4e..17732810f 100644
--- a/c/driver/postgresql/connection.cc
+++ b/c/driver/postgresql/connection.cc
@@ -22,8 +22,10 @@
 #include <cmath>
 #include <cstring>
 #include <memory>
+#include <optional>
 #include <sstream>
 #include <string>
+#include <string_view>
 #include <unordered_map>
 #include <utility>
 #include <vector>
@@ -38,6 +40,9 @@
 #include "error.h"
 #include "result_helper.h"
 
+using adbc::driver::Result;
+using adbc::driver::Status;
+
 namespace adbcpq {
 namespace {
 
@@ -51,551 +56,374 @@ static const std::unordered_map<std::string, std::string> 
kPgTableTypes = {
     {"table", "r"},       {"view", "v"},          {"materialized_view", "m"},
     {"toast_table", "t"}, {"foreign_table", "f"}, {"partitioned_table", "p"}};
 
-class PqGetObjectsHelper {
+static const char* kCatalogQueryAll = "SELECT datname FROM 
pg_catalog.pg_database";
+
+// catalog_name is not a parameter here or on any other queries
+// because it will always be the currently connected database.
+static const char* kSchemaQueryAll =
+    "SELECT nspname FROM pg_catalog.pg_namespace WHERE "
+    "nspname !~ '^pg_' AND nspname <> 'information_schema'";
+
+// Parameterized on schema_name, relkind
+// Note that when binding relkind as a string it must look like {"r", "v", ...}
+// (i.e., double quotes). Binding a binary list<string> element also works.
+static const char* kTablesQueryAll =
+    "SELECT c.relname, CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 
'view' "
+    "WHEN 'm' THEN 'materialized view' WHEN 't' THEN 'TOAST table' "
+    "WHEN 'f' THEN 'foreign table' WHEN 'p' THEN 'partitioned table' END "
+    "AS reltype FROM pg_catalog.pg_class c "
+    "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace "
+    "WHERE pg_catalog.pg_table_is_visible(c.oid) AND n.nspname = $1 AND 
c.relkind = "
+    "ANY($2)";
+
+// Parameterized on schema_name, table_name
+static const char* kColumnsQueryAll =
+    "SELECT attr.attname, attr.attnum, "
+    "pg_catalog.col_description(cls.oid, attr.attnum) "
+    "FROM pg_catalog.pg_attribute AS attr "
+    "INNER JOIN pg_catalog.pg_class AS cls ON attr.attrelid = cls.oid "
+    "INNER JOIN pg_catalog.pg_namespace AS nsp ON nsp.oid = cls.relnamespace "
+    "WHERE attr.attnum > 0 AND NOT attr.attisdropped "
+    "AND nsp.nspname LIKE $1 AND cls.relname LIKE $2";
+
+// Parameterized on schema_name, table_name
+static const char* kConstraintsQueryAll =
+    "WITH fk_unnest AS ( "
+    "    SELECT "
+    "        con.conname, "
+    "        'FOREIGN KEY' AS contype, "
+    "        conrelid, "
+    "        UNNEST(con.conkey) AS conkey, "
+    "        confrelid, "
+    "        UNNEST(con.confkey) AS confkey "
+    "    FROM pg_catalog.pg_constraint AS con "
+    "    INNER JOIN pg_catalog.pg_class AS cls ON cls.oid = conrelid "
+    "    INNER JOIN pg_catalog.pg_namespace AS nsp ON nsp.oid = 
cls.relnamespace "
+    "    WHERE con.contype = 'f' AND nsp.nspname = $1 "
+    "    AND cls.relname = $2 "
+    "), "
+    "fk_names AS ( "
+    "    SELECT "
+    "        fk_unnest.conname, "
+    "        fk_unnest.contype, "
+    "        fk_unnest.conkey, "
+    "        fk_unnest.confkey, "
+    "        attr.attname, "
+    "        fnsp.nspname AS fschema, "
+    "        fcls.relname AS ftable, "
+    "        fattr.attname AS fattname "
+    "    FROM fk_unnest "
+    "    INNER JOIN pg_catalog.pg_class AS cls ON cls.oid = fk_unnest.conrelid 
"
+    "    INNER JOIN pg_catalog.pg_class AS fcls ON fcls.oid = 
fk_unnest.confrelid "
+    "    INNER JOIN pg_catalog.pg_namespace AS fnsp ON fnsp.oid = 
fcls.relnamespace"
+    "    INNER JOIN pg_catalog.pg_attribute AS attr ON attr.attnum = "
+    "fk_unnest.conkey "
+    "        AND attr.attrelid = fk_unnest.conrelid "
+    "    LEFT JOIN pg_catalog.pg_attribute AS fattr ON fattr.attnum =  "
+    "fk_unnest.confkey "
+    "        AND fattr.attrelid = fk_unnest.confrelid "
+    "), "
+    "fkeys AS ( "
+    "    SELECT "
+    "        conname, "
+    "        contype, "
+    "        ARRAY_AGG(attname ORDER BY conkey) AS colnames, "
+    "        fschema, "
+    "        ftable, "
+    "        ARRAY_AGG(fattname ORDER BY confkey) AS fcolnames "
+    "    FROM fk_names "
+    "    GROUP BY "
+    "        conname, "
+    "        contype, "
+    "        fschema, "
+    "        ftable "
+    "), "
+    "other_constraints AS ( "
+    "    SELECT con.conname, CASE con.contype WHEN 'c' THEN 'CHECK' WHEN 'u' 
THEN  "
+    "    'UNIQUE' WHEN 'p' THEN 'PRIMARY KEY' END AS contype, "
+    "    ARRAY_AGG(attr.attname) AS colnames "
+    "    FROM pg_catalog.pg_constraint AS con  "
+    "    CROSS JOIN UNNEST(conkey) AS conkeys  "
+    "    INNER JOIN pg_catalog.pg_class AS cls ON cls.oid = con.conrelid  "
+    "    INNER JOIN pg_catalog.pg_namespace AS nsp ON nsp.oid = 
cls.relnamespace  "
+    "    INNER JOIN pg_catalog.pg_attribute AS attr ON attr.attnum = conkeys  "
+    "    AND cls.oid = attr.attrelid  "
+    "    WHERE con.contype IN ('c', 'u', 'p') AND nsp.nspname = $1 "
+    "    AND cls.relname = $2 "
+    "    GROUP BY conname, contype "
+    ") "
+    "SELECT "
+    "    conname, contype, colnames, fschema, ftable, fcolnames "
+    "FROM fkeys "
+    "UNION ALL "
+    "SELECT "
+    "    conname, contype, colnames, NULL, NULL, NULL "
+    "FROM other_constraints";
+
+class PostgresGetObjectsHelper : public adbc::driver::GetObjectsHelper {
  public:
-  PqGetObjectsHelper(PGconn* conn, int depth, const char* catalog, const char* 
db_schema,
-                     const char* table_name, const char** table_types,
-                     const char* column_name, struct ArrowSchema* schema,
-                     struct ArrowArray* array, struct AdbcError* error)
-      : conn_(conn),
-        depth_(depth),
-        catalog_(catalog),
-        db_schema_(db_schema),
-        table_name_(table_name),
-        table_types_(table_types),
-        column_name_(column_name),
-        schema_(schema),
-        array_(array),
-        error_(error) {
-    na_error_ = {0};
-  }
-
-  AdbcStatusCode GetObjects() {
-    RAISE_ADBC(InitArrowArray());
-
-    catalog_name_col_ = array_->children[0];
-    catalog_db_schemas_col_ = array_->children[1];
-    catalog_db_schemas_items_ = catalog_db_schemas_col_->children[0];
-    db_schema_name_col_ = catalog_db_schemas_items_->children[0];
-    db_schema_tables_col_ = catalog_db_schemas_items_->children[1];
-    schema_table_items_ = db_schema_tables_col_->children[0];
-    table_name_col_ = schema_table_items_->children[0];
-    table_type_col_ = schema_table_items_->children[1];
-
-    table_columns_col_ = schema_table_items_->children[2];
-    table_columns_items_ = table_columns_col_->children[0];
-    column_name_col_ = table_columns_items_->children[0];
-    column_position_col_ = table_columns_items_->children[1];
-    column_remarks_col_ = table_columns_items_->children[2];
-
-    table_constraints_col_ = schema_table_items_->children[3];
-    table_constraints_items_ = table_constraints_col_->children[0];
-    constraint_name_col_ = table_constraints_items_->children[0];
-    constraint_type_col_ = table_constraints_items_->children[1];
-
-    constraint_column_names_col_ = table_constraints_items_->children[2];
-    constraint_column_name_col_ = constraint_column_names_col_->children[0];
-
-    constraint_column_usages_col_ = table_constraints_items_->children[3];
-    constraint_column_usage_items_ = 
constraint_column_usages_col_->children[0];
-    fk_catalog_col_ = constraint_column_usage_items_->children[0];
-    fk_db_schema_col_ = constraint_column_usage_items_->children[1];
-    fk_table_col_ = constraint_column_usage_items_->children[2];
-    fk_column_name_col_ = constraint_column_usage_items_->children[3];
-
-    RAISE_ADBC(AppendCatalogs());
-    RAISE_ADBC(FinishArrowArray());
-    return ADBC_STATUS_OK;
-  }
-
- private:
-  AdbcStatusCode InitArrowArray() {
-    RAISE_ADBC(adbc::driver::MakeGetObjectsSchema(schema_).ToAdbc(error_));
-
-    CHECK_NA_DETAIL(INTERNAL, ArrowArrayInitFromSchema(array_, schema_, 
&na_error_),
-                    &na_error_, error_);
-
-    CHECK_NA(INTERNAL, ArrowArrayStartAppending(array_), error_);
-    return ADBC_STATUS_OK;
+  explicit PostgresGetObjectsHelper(PGconn* conn)
+      : current_database_(PQdb(conn)),
+        all_catalogs_(conn, kCatalogQueryAll),
+        some_catalogs_(conn, CatalogQuery()),
+        all_schemas_(conn, kSchemaQueryAll),
+        some_schemas_(conn, SchemaQuery()),
+        all_tables_(conn, kTablesQueryAll),
+        some_tables_(conn, TablesQuery()),
+        all_columns_(conn, kColumnsQueryAll),
+        some_columns_(conn, ColumnsQuery()),
+        all_constraints_(conn, kConstraintsQueryAll),
+        some_constraints_(conn, ConstraintsQuery()) {}
+
+  Status Load(adbc::driver::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) override {
+    return Status::Ok();
   }
 
-  AdbcStatusCode AppendSchemas(std::string db_name) {
-    // postgres only allows you to list schemas for the currently connected db
-    if (!strcmp(db_name.c_str(), PQdb(conn_))) {
-      struct StringBuilder query;
-      std::memset(&query, 0, sizeof(query));
-      if (StringBuilderInit(&query, /*initial_size*/ 256)) {
-        return ADBC_STATUS_INTERNAL;
-      }
-
-      const char* stmt =
-          "SELECT nspname FROM pg_catalog.pg_namespace WHERE "
-          "nspname !~ '^pg_' AND nspname <> 'information_schema'";
-
-      if (StringBuilderAppend(&query, "%s", stmt)) {
-        StringBuilderReset(&query);
-        return ADBC_STATUS_INTERNAL;
-      }
-
-      std::vector<std::string> params;
-      if (db_schema_ != NULL) {
-        if (StringBuilderAppend(&query, "%s", " AND nspname = $1")) {
-          StringBuilderReset(&query);
-          return ADBC_STATUS_INTERNAL;
-        }
-        params.push_back(db_schema_);
-      }
-
-      auto result_helper = PqResultHelper{conn_, std::string(query.buffer)};
-      StringBuilderReset(&query);
+  Status LoadCatalogs(std::optional<std::string_view> catalog_filter) override 
{
+    if (catalog_filter.has_value()) {
+      UNWRAP_STATUS(some_catalogs_.Execute({std::string(*catalog_filter)}));
+      next_catalog_ = some_catalogs_.Row(-1);
+    } else {
+      UNWRAP_STATUS(all_catalogs_.Execute());
+      next_catalog_ = all_catalogs_.Row(-1);
+    }
 
-      RAISE_STATUS(error_, result_helper.Execute(params));
+    return Status::Ok();
+  };
 
-      for (PqResultRow row : result_helper) {
-        const char* schema_name = row[0].data;
-        CHECK_NA(INTERNAL,
-                 ArrowArrayAppendString(db_schema_name_col_, 
ArrowCharView(schema_name)),
-                 error_);
-        if (depth_ == ADBC_OBJECT_DEPTH_DB_SCHEMAS) {
-          CHECK_NA(INTERNAL, ArrowArrayAppendNull(db_schema_tables_col_, 1), 
error_);
-        } else {
-          RAISE_ADBC(AppendTables(std::string(schema_name)));
-        }
-        CHECK_NA(INTERNAL, ArrowArrayFinishElement(catalog_db_schemas_items_), 
error_);
-      }
+  Result<std::optional<std::string_view>> NextCatalog() override {
+    next_catalog_ = next_catalog_.Next();
+    if (!next_catalog_.IsValid()) {
+      return std::nullopt;
     }
 
-    CHECK_NA(INTERNAL, ArrowArrayFinishElement(catalog_db_schemas_col_), 
error_);
-    return ADBC_STATUS_OK;
+    return next_catalog_[0].value();
   }
 
-  AdbcStatusCode AppendCatalogs() {
-    struct StringBuilder query;
-    std::memset(&query, 0, sizeof(query));
-    if (StringBuilderInit(&query, /*initial_size=*/256) != 0) return 
ADBC_STATUS_INTERNAL;
-
-    if (StringBuilderAppend(&query, "%s", "SELECT datname FROM 
pg_catalog.pg_database")) {
-      return ADBC_STATUS_INTERNAL;
+  Status LoadSchemas(std::string_view catalog,
+                     std::optional<std::string_view> schema_filter) override {
+    // PostgreSQL can only list for the current database
+    if (catalog != current_database_) {
+      return Status::Ok();
     }
 
-    std::vector<std::string> params;
-    if (catalog_ != NULL) {
-      if (StringBuilderAppend(&query, "%s", " WHERE datname = $1")) {
-        StringBuilderReset(&query);
-        return ADBC_STATUS_INTERNAL;
-      }
-      params.push_back(catalog_);
+    if (schema_filter.has_value()) {
+      UNWRAP_STATUS(some_schemas_.Execute({std::string(*schema_filter)}));
+      next_schema_ = some_schemas_.Row(-1);
+    } else {
+      UNWRAP_STATUS(all_schemas_.Execute());
+      next_schema_ = all_schemas_.Row(-1);
     }
+    return Status::Ok();
+  };
 
-    PqResultHelper result_helper = PqResultHelper{conn_, 
std::string(query.buffer)};
-    StringBuilderReset(&query);
-
-    RAISE_STATUS(error_, result_helper.Execute(params));
-
-    for (PqResultRow row : result_helper) {
-      const char* db_name = row[0].data;
-      CHECK_NA(INTERNAL,
-               ArrowArrayAppendString(catalog_name_col_, 
ArrowCharView(db_name)), error_);
-      if (depth_ == ADBC_OBJECT_DEPTH_CATALOGS) {
-        CHECK_NA(INTERNAL, ArrowArrayAppendNull(catalog_db_schemas_col_, 1), 
error_);
-      } else {
-        RAISE_ADBC(AppendSchemas(std::string(db_name)));
-      }
-      CHECK_NA(INTERNAL, ArrowArrayFinishElement(array_), error_);
+  Result<std::optional<std::string_view>> NextSchema() override {
+    next_schema_ = next_schema_.Next();
+    if (!next_schema_.IsValid()) {
+      return std::nullopt;
     }
 
-    return ADBC_STATUS_OK;
+    return next_schema_[0].value();
   }
 
-  AdbcStatusCode AppendTables(std::string schema_name) {
-    struct StringBuilder query;
-    std::memset(&query, 0, sizeof(query));
-    if (StringBuilderInit(&query, /*initial_size*/ 512)) {
-      return ADBC_STATUS_INTERNAL;
-    }
+  Status LoadTables(std::string_view catalog, std::string_view schema,
+                    std::optional<std::string_view> table_filter,
+                    const std::vector<std::string_view>& table_types) override 
{
+    std::string table_types_bind = TableTypesArrayLiteral(table_types);
 
-    std::vector<std::string> params = {schema_name};
-    const char* stmt =
-        "SELECT c.relname, CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 
'view' "
-        "WHEN 'm' THEN 'materialized view' WHEN 't' THEN 'TOAST table' "
-        "WHEN 'f' THEN 'foreign table' WHEN 'p' THEN 'partitioned table' END "
-        "AS reltype FROM pg_catalog.pg_class c "
-        "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace "
-        "WHERE c.relkind IN ('r','v','m','t','f','p') "
-        "AND pg_catalog.pg_table_is_visible(c.oid) AND n.nspname = $1";
-
-    if (StringBuilderAppend(&query, "%s", stmt)) {
-      StringBuilderReset(&query);
-      return ADBC_STATUS_INTERNAL;
+    if (table_filter.has_value()) {
+      UNWRAP_STATUS(some_tables_.Execute(
+          {std::string(schema), table_types_bind, 
std::string(*table_filter)}));
+      next_table_ = some_tables_.Row(-1);
+    } else {
+      UNWRAP_STATUS(all_tables_.Execute({std::string(schema), 
table_types_bind}));
+      next_table_ = all_tables_.Row(-1);
     }
 
-    if (table_name_ != nullptr) {
-      if (StringBuilderAppend(&query, "%s", " AND c.relname LIKE $2")) {
-        StringBuilderReset(&query);
-        return ADBC_STATUS_INTERNAL;
-      }
+    return Status::Ok();
+  };
 
-      params.push_back(std::string(table_name_));
+  Result<std::optional<Table>> NextTable() override {
+    next_table_ = next_table_.Next();
+    if (!next_table_.IsValid()) {
+      return std::nullopt;
     }
 
-    if (table_types_ != nullptr) {
-      std::vector<std::string> table_type_filter;
-      const char** table_types = table_types_;
-      while (*table_types != NULL) {
-        auto table_type_str = std::string(*table_types);
-        auto search = kPgTableTypes.find(table_type_str);
-        if (search != kPgTableTypes.end()) {
-          table_type_filter.push_back(search->second);
-        }
-        table_types++;
-      }
-
-      if (!table_type_filter.empty()) {
-        std::ostringstream oss;
-        bool first = true;
-        oss << "(";
-        for (const auto& str : table_type_filter) {
-          if (!first) {
-            oss << ", ";
-          }
-          oss << "'" << str << "'";
-          first = false;
-        }
-        oss << ")";
+    return Table{next_table_[0].value(), next_table_[1].value()};
+  }
 
-        if (StringBuilderAppend(&query, "%s%s", " AND c.relkind IN ",
-                                oss.str().c_str())) {
-          StringBuilderReset(&query);
-          return ADBC_STATUS_INTERNAL;
-        }
-      } else {
-        // no matching table type means no records should come back
-        if (StringBuilderAppend(&query, "%s", " AND false")) {
-          StringBuilderReset(&query);
-          return ADBC_STATUS_INTERNAL;
-        }
-      }
+  Status LoadColumns(std::string_view catalog, std::string_view schema,
+                     std::string_view table,
+                     std::optional<std::string_view> column_filter) override {
+    if (column_filter.has_value()) {
+      UNWRAP_STATUS(some_columns_.Execute(
+          {std::string(schema), std::string(table), 
std::string(*column_filter)}))
+      UNWRAP_STATUS(some_constraints_.Execute(
+          {std::string(schema), std::string(table), 
std::string(*column_filter)}))
+      next_column_ = some_columns_.Row(-1);
+      next_constraint_ = some_constraints_.Row(-1);
+    } else {
+      UNWRAP_STATUS(all_columns_.Execute({std::string(schema), 
std::string(table)}))
+      UNWRAP_STATUS(all_constraints_.Execute({std::string(schema), 
std::string(table)}))
+      next_column_ = all_columns_.Row(-1);
+      next_constraint_ = all_constraints_.Row(-1);
     }
 
-    auto result_helper = PqResultHelper{conn_, query.buffer};
-    StringBuilderReset(&query);
+    return Status::Ok();
+  };
 
-    RAISE_STATUS(error_, result_helper.Execute(params));
-    for (PqResultRow row : result_helper) {
-      const char* table_name = row[0].data;
-      const char* table_type = row[1].data;
+  Result<std::optional<Column>> NextColumn() override {
+    next_column_ = next_column_.Next();
+    if (!next_column_.IsValid()) {
+      return std::nullopt;
+    }
 
-      CHECK_NA(INTERNAL,
-               ArrowArrayAppendString(table_name_col_, 
ArrowCharView(table_name)),
-               error_);
-      CHECK_NA(INTERNAL,
-               ArrowArrayAppendString(table_type_col_, 
ArrowCharView(table_type)),
-               error_);
-      if (depth_ == ADBC_OBJECT_DEPTH_TABLES) {
-        CHECK_NA(INTERNAL, ArrowArrayAppendNull(table_columns_col_, 1), 
error_);
-        CHECK_NA(INTERNAL, ArrowArrayAppendNull(table_constraints_col_, 1), 
error_);
-      } else {
-        auto table_name_s = std::string(table_name);
-        RAISE_ADBC(AppendColumns(schema_name, table_name_s));
-        RAISE_ADBC(AppendConstraints(schema_name, table_name_s));
-      }
-      CHECK_NA(INTERNAL, ArrowArrayFinishElement(schema_table_items_), error_);
+    Column col;
+    col.column_name = next_column_[0].value();
+    UNWRAP_RESULT(col.ordinal_position, next_column_[1].ParseInteger());
+    if (!next_column_[2].is_null) {
+      col.remarks = next_column_[2].value();
     }
 
-    CHECK_NA(INTERNAL, ArrowArrayFinishElement(db_schema_tables_col_), error_);
-    return ADBC_STATUS_OK;
+    return col;
   }
 
-  AdbcStatusCode AppendColumns(std::string schema_name, std::string 
table_name) {
-    struct StringBuilder query;
-    std::memset(&query, 0, sizeof(query));
-    if (StringBuilderInit(&query, /*initial_size*/ 512)) {
-      return ADBC_STATUS_INTERNAL;
-    }
-
-    std::vector<std::string> params = {schema_name, table_name};
-    const char* stmt =
-        "SELECT attr.attname, attr.attnum, "
-        "pg_catalog.col_description(cls.oid, attr.attnum) "
-        "FROM pg_catalog.pg_attribute AS attr "
-        "INNER JOIN pg_catalog.pg_class AS cls ON attr.attrelid = cls.oid "
-        "INNER JOIN pg_catalog.pg_namespace AS nsp ON nsp.oid = 
cls.relnamespace "
-        "WHERE attr.attnum > 0 AND NOT attr.attisdropped "
-        "AND nsp.nspname LIKE $1 AND cls.relname LIKE $2";
-
-    if (StringBuilderAppend(&query, "%s", stmt)) {
-      StringBuilderReset(&query);
-      return ADBC_STATUS_INTERNAL;
+  Result<std::optional<Constraint>> NextConstraint() override {
+    next_constraint_ = next_constraint_.Next();
+    if (!next_constraint_.IsValid()) {
+      return std::nullopt;
     }
 
-    if (column_name_ != NULL) {
-      if (StringBuilderAppend(&query, "%s", " AND attr.attname LIKE $3")) {
-        StringBuilderReset(&query);
-        return ADBC_STATUS_INTERNAL;
-      }
+    Constraint out;
+    out.name = next_constraint_[0].data;
+    out.type = next_constraint_[1].data;
 
-      params.push_back(std::string(column_name_));
+    UNWRAP_RESULT(constraint_fcolumn_names_, 
next_constraint_[2].ParseTextArray());
+    std::vector<std::string_view> fcolumn_names_view;
+    for (const std::string& item : constraint_fcolumn_names_) {
+      fcolumn_names_view.push_back(item);
     }
+    out.column_names = std::move(fcolumn_names_view);
 
-    auto result_helper = PqResultHelper{conn_, query.buffer};
-    StringBuilderReset(&query);
-
-    RAISE_STATUS(error_, result_helper.Execute(params));
+    if (out.type == "FOREIGN KEY") {
+      assert(!next_constraint_[3].is_null);
+      assert(!next_constraint_[3].is_null);
+      assert(!next_constraint_[4].is_null);
+      assert(!next_constraint_[5].is_null);
 
-    for (PqResultRow row : result_helper) {
-      const char* column_name = row[0].data;
-      const char* position = row[1].data;
+      out.usage = std::vector<ConstraintUsage>();
+      UNWRAP_RESULT(constraint_fkey_names_, 
next_constraint_[5].ParseTextArray());
 
-      CHECK_NA(INTERNAL,
-               ArrowArrayAppendString(column_name_col_, 
ArrowCharView(column_name)),
-               error_);
-      int ival = atol(position);
-      CHECK_NA(INTERNAL,
-               ArrowArrayAppendInt(column_position_col_, 
static_cast<int64_t>(ival)),
-               error_);
-      if (row[2].is_null) {
-        CHECK_NA(INTERNAL, ArrowArrayAppendNull(column_remarks_col_, 1), 
error_);
-      } else {
-        const char* remarks = row[2].data;
-        CHECK_NA(INTERNAL,
-                 ArrowArrayAppendString(column_remarks_col_, 
ArrowCharView(remarks)),
-                 error_);
-      }
+      for (const auto& item : constraint_fkey_names_) {
+        ConstraintUsage usage;
+        usage.catalog = current_database_;
+        usage.schema = next_constraint_[3].data;
+        usage.table = next_constraint_[4].data;
+        usage.column = item;
 
-      // no xdbc_ values for now
-      for (auto i = 3; i < 19; i++) {
-        CHECK_NA(INTERNAL, 
ArrowArrayAppendNull(table_columns_items_->children[i], 1),
-                 error_);
+        out.usage->push_back(usage);
       }
-
-      CHECK_NA(INTERNAL, ArrowArrayFinishElement(table_columns_items_), 
error_);
     }
 
-    CHECK_NA(INTERNAL, ArrowArrayFinishElement(table_columns_col_), error_);
-    return ADBC_STATUS_OK;
+    return out;
   }
 
-  // libpq PQexecParams can use either text or binary transfers
-  // For now we are using text transfer internally, so arrays are sent
-  // back like {element1, element2} within a const char*
-  std::vector<std::string> PqTextArrayToVector(std::string text_array) {
-    text_array.erase(0, 1);
-    text_array.erase(text_array.size() - 1);
-
-    std::vector<std::string> elements;
-    std::stringstream ss(std::move(text_array));
-    std::string tmp;
-
-    while (getline(ss, tmp, ',')) {
-      elements.push_back(std::move(tmp));
-    }
-
-    return elements;
+ private:
+  std::string current_database_;
+
+  // Ready-to-Execute() queries
+  PqResultHelper all_catalogs_;
+  PqResultHelper some_catalogs_;
+  PqResultHelper all_schemas_;
+  PqResultHelper some_schemas_;
+  PqResultHelper all_tables_;
+  PqResultHelper some_tables_;
+  PqResultHelper all_columns_;
+  PqResultHelper some_columns_;
+  PqResultHelper all_constraints_;
+  PqResultHelper some_constraints_;
+
+  // Iterator state for the catalogs/schema/table/column queries
+  PqResultRow next_catalog_;
+  PqResultRow next_schema_;
+  PqResultRow next_table_;
+  PqResultRow next_column_;
+  PqResultRow next_constraint_;
+
+  // Owning variants required because the framework versions of these
+  // are all based on string_view and the result helper can only parse arrays
+  // into std::vector<std::string>.
+  std::vector<std::string> constraint_fcolumn_names_;
+  std::vector<std::string> constraint_fkey_names_;
+
+  // Queries that are slightly modified versions of the generic queries that 
allow
+  // the filter for that level to be passed through as a parameter. Defined 
here
+  // because global strings should be const char* according to cpplint and 
using
+  // the + operator to concatenate them is the most concise way to construct 
them.
+
+  // Parameterized on catalog_name
+  static std::string CatalogQuery() {
+    return std::string(kCatalogQueryAll) + " WHERE datname = $1";
   }
 
-  AdbcStatusCode AppendConstraints(std::string schema_name, std::string 
table_name) {
-    struct StringBuilder query;
-    std::memset(&query, 0, sizeof(query));
-    if (StringBuilderInit(&query, /*initial_size*/ 4096)) {
-      return ADBC_STATUS_INTERNAL;
-    }
-
-    std::vector<std::string> params = {schema_name, table_name};
-    const char* stmt =
-        "WITH fk_unnest AS ( "
-        "    SELECT "
-        "        con.conname, "
-        "        'FOREIGN KEY' AS contype, "
-        "        conrelid, "
-        "        UNNEST(con.conkey) AS conkey, "
-        "        confrelid, "
-        "        UNNEST(con.confkey) AS confkey "
-        "    FROM pg_catalog.pg_constraint AS con "
-        "    INNER JOIN pg_catalog.pg_class AS cls ON cls.oid = conrelid "
-        "    INNER JOIN pg_catalog.pg_namespace AS nsp ON nsp.oid = 
cls.relnamespace "
-        "    WHERE con.contype = 'f' AND nsp.nspname LIKE $1 "
-        "    AND cls.relname LIKE $2 "
-        "), "
-        "fk_names AS ( "
-        "    SELECT "
-        "        fk_unnest.conname, "
-        "        fk_unnest.contype, "
-        "        fk_unnest.conkey, "
-        "        fk_unnest.confkey, "
-        "        attr.attname, "
-        "        fnsp.nspname AS fschema, "
-        "        fcls.relname AS ftable, "
-        "        fattr.attname AS fattname "
-        "    FROM fk_unnest "
-        "    INNER JOIN pg_catalog.pg_class AS cls ON cls.oid = 
fk_unnest.conrelid "
-        "    INNER JOIN pg_catalog.pg_class AS fcls ON fcls.oid = 
fk_unnest.confrelid "
-        "    INNER JOIN pg_catalog.pg_namespace AS fnsp ON fnsp.oid = 
fcls.relnamespace"
-        "    INNER JOIN pg_catalog.pg_attribute AS attr ON attr.attnum = "
-        "fk_unnest.conkey "
-        "        AND attr.attrelid = fk_unnest.conrelid "
-        "    LEFT JOIN pg_catalog.pg_attribute AS fattr ON fattr.attnum =  "
-        "fk_unnest.confkey "
-        "        AND fattr.attrelid = fk_unnest.confrelid "
-        "), "
-        "fkeys AS ( "
-        "    SELECT "
-        "        conname, "
-        "        contype, "
-        "        ARRAY_AGG(attname ORDER BY conkey) AS colnames, "
-        "        fschema, "
-        "        ftable, "
-        "        ARRAY_AGG(fattname ORDER BY confkey) AS fcolnames "
-        "    FROM fk_names "
-        "    GROUP BY "
-        "        conname, "
-        "        contype, "
-        "        fschema, "
-        "        ftable "
-        "), "
-        "other_constraints AS ( "
-        "    SELECT con.conname, CASE con.contype WHEN 'c' THEN 'CHECK' WHEN 
'u' THEN  "
-        "    'UNIQUE' WHEN 'p' THEN 'PRIMARY KEY' END AS contype, "
-        "    ARRAY_AGG(attr.attname) AS colnames "
-        "    FROM pg_catalog.pg_constraint AS con  "
-        "    CROSS JOIN UNNEST(conkey) AS conkeys  "
-        "    INNER JOIN pg_catalog.pg_class AS cls ON cls.oid = con.conrelid  "
-        "    INNER JOIN pg_catalog.pg_namespace AS nsp ON nsp.oid = 
cls.relnamespace  "
-        "    INNER JOIN pg_catalog.pg_attribute AS attr ON attr.attnum = 
conkeys  "
-        "    AND cls.oid = attr.attrelid  "
-        "    WHERE con.contype IN ('c', 'u', 'p') AND nsp.nspname LIKE $1 "
-        "    AND cls.relname LIKE $2 "
-        "    GROUP BY conname, contype "
-        ") "
-        "SELECT "
-        "    conname, contype, colnames, fschema, ftable, fcolnames "
-        "FROM fkeys "
-        "UNION ALL "
-        "SELECT "
-        "    conname, contype, colnames, NULL, NULL, NULL "
-        "FROM other_constraints";
-
-    if (StringBuilderAppend(&query, "%s", stmt)) {
-      StringBuilderReset(&query);
-      return ADBC_STATUS_INTERNAL;
-    }
-
-    if (column_name_ != NULL) {
-      if (StringBuilderAppend(&query, "%s", " WHERE conname LIKE $3")) {
-        StringBuilderReset(&query);
-        return ADBC_STATUS_INTERNAL;
-      }
-
-      params.push_back(std::string(column_name_));
-    }
+  // Parameterized on schema_name
+  static std::string SchemaQuery() {
+    return std::string(kSchemaQueryAll) + " AND nspname = $1";
+  }
 
-    auto result_helper = PqResultHelper{conn_, query.buffer};
-    StringBuilderReset(&query);
+  // Parameterized on schema_name, relkind, table_name
+  static std::string TablesQuery() {
+    return std::string(kTablesQueryAll) + " AND c.relname LIKE $3";
+  }
 
-    RAISE_STATUS(error_, result_helper.Execute(params));
+  // Parameterized on schema_name, table_name, column_name
+  static std::string ColumnsQuery() {
+    return std::string(kColumnsQueryAll) + " AND attr.attname LIKE $3";
+  }
 
-    for (PqResultRow row : result_helper) {
-      const char* constraint_name = row[0].data;
-      const char* constraint_type = row[1].data;
+  // Parameterized on schema_name, table_name, column_name
+  static std::string ConstraintsQuery() {
+    return std::string(kConstraintsQueryAll) + " WHERE conname LIKE $3";
+  }
 
-      CHECK_NA(
-          INTERNAL,
-          ArrowArrayAppendString(constraint_name_col_, 
ArrowCharView(constraint_name)),
-          error_);
+  std::string TableTypesArrayLiteral(const std::vector<std::string_view>& 
table_types) {
+    std::stringstream table_types_bind;
+    table_types_bind << "{";
+    int table_types_bind_len = 0;
 
-      CHECK_NA(
-          INTERNAL,
-          ArrowArrayAppendString(constraint_type_col_, 
ArrowCharView(constraint_type)),
-          error_);
+    if (table_types.empty()) {
+      for (const auto& item : kPgTableTypes) {
+        if (table_types_bind_len > 0) {
+          table_types_bind << ", ";
+        }
 
-      auto constraint_column_names = 
PqTextArrayToVector(std::string(row[2].data));
-      for (const auto& constraint_column_name : constraint_column_names) {
-        CHECK_NA(INTERNAL,
-                 ArrowArrayAppendString(constraint_column_name_col_,
-                                        
ArrowCharView(constraint_column_name.c_str())),
-                 error_);
+        table_types_bind << "\"" << item.second << "\"";
+        table_types_bind_len++;
       }
-      CHECK_NA(INTERNAL, 
ArrowArrayFinishElement(constraint_column_names_col_), error_);
-
-      if (!strcmp(constraint_type, "FOREIGN KEY")) {
-        assert(!row[3].is_null);
-        assert(!row[4].is_null);
-        assert(!row[5].is_null);
-
-        const char* constraint_ftable_schema = row[3].data;
-        const char* constraint_ftable_name = row[4].data;
-        auto constraint_fcolumn_names = 
PqTextArrayToVector(std::string(row[5].data));
-        for (const auto& constraint_fcolumn_name : constraint_fcolumn_names) {
-          CHECK_NA(INTERNAL,
-                   ArrowArrayAppendString(fk_catalog_col_, 
ArrowCharView(PQdb(conn_))),
-                   error_);
-          CHECK_NA(INTERNAL,
-                   ArrowArrayAppendString(fk_db_schema_col_,
-                                          
ArrowCharView(constraint_ftable_schema)),
-                   error_);
-          CHECK_NA(INTERNAL,
-                   ArrowArrayAppendString(fk_table_col_,
-                                          
ArrowCharView(constraint_ftable_name)),
-                   error_);
-          CHECK_NA(INTERNAL,
-                   ArrowArrayAppendString(fk_column_name_col_,
-                                          
ArrowCharView(constraint_fcolumn_name.c_str())),
-                   error_);
-
-          CHECK_NA(INTERNAL, 
ArrowArrayFinishElement(constraint_column_usage_items_),
-                   error_);
+    } else {
+      for (auto type : table_types) {
+        const auto maybe_item = kPgTableTypes.find(std::string(type));
+        if (maybe_item == kPgTableTypes.end()) {
+          continue;
         }
-      }
-      CHECK_NA(INTERNAL, 
ArrowArrayFinishElement(constraint_column_usages_col_), error_);
-      CHECK_NA(INTERNAL, ArrowArrayFinishElement(table_constraints_items_), 
error_);
-    }
 
-    CHECK_NA(INTERNAL, ArrowArrayFinishElement(table_constraints_col_), 
error_);
-    return ADBC_STATUS_OK;
-  }
+        if (table_types_bind_len > 0) {
+          table_types_bind << ", ";
+        }
 
-  AdbcStatusCode FinishArrowArray() {
-    CHECK_NA_DETAIL(INTERNAL, ArrowArrayFinishBuildingDefault(array_, 
&na_error_),
-                    &na_error_, error_);
+        table_types_bind << "\"" << maybe_item->second << "\"";
+        table_types_bind_len++;
+      }
+    }
 
-    return ADBC_STATUS_OK;
+    table_types_bind << "}";
+    return table_types_bind.str();
   }
-
-  PGconn* conn_ = nullptr;
-  int depth_;
-  const char* catalog_ = nullptr;
-  const char* db_schema_ = nullptr;
-  const char* table_name_ = nullptr;
-  const char** table_types_ = nullptr;
-  const char* column_name_ = nullptr;
-  struct ArrowSchema* schema_ = nullptr;
-  struct ArrowArray* array_ = nullptr;
-  struct AdbcError* error_ = nullptr;
-  struct ArrowError na_error_;
-  struct ArrowArray* catalog_name_col_ = nullptr;
-  struct ArrowArray* catalog_db_schemas_col_ = nullptr;
-  struct ArrowArray* catalog_db_schemas_items_ = nullptr;
-  struct ArrowArray* db_schema_name_col_ = nullptr;
-  struct ArrowArray* db_schema_tables_col_ = nullptr;
-  struct ArrowArray* schema_table_items_ = nullptr;
-  struct ArrowArray* table_name_col_ = nullptr;
-  struct ArrowArray* table_type_col_ = nullptr;
-  struct ArrowArray* table_columns_col_ = nullptr;
-  struct ArrowArray* table_columns_items_ = nullptr;
-  struct ArrowArray* column_name_col_ = nullptr;
-  struct ArrowArray* column_position_col_ = nullptr;
-  struct ArrowArray* column_remarks_col_ = nullptr;
-  struct ArrowArray* table_constraints_col_ = nullptr;
-  struct ArrowArray* table_constraints_items_ = nullptr;
-  struct ArrowArray* constraint_name_col_ = nullptr;
-  struct ArrowArray* constraint_type_col_ = nullptr;
-  struct ArrowArray* constraint_column_names_col_ = nullptr;
-  struct ArrowArray* constraint_column_name_col_ = nullptr;
-  struct ArrowArray* constraint_column_usages_col_ = nullptr;
-  struct ArrowArray* constraint_column_usage_items_ = nullptr;
-  struct ArrowArray* fk_catalog_col_ = nullptr;
-  struct ArrowArray* fk_db_schema_col_ = nullptr;
-  struct ArrowArray* fk_table_col_ = nullptr;
-  struct ArrowArray* fk_column_name_col_ = nullptr;
 };
 
 // A notice processor that does nothing with notices. In the future we can log
@@ -689,26 +517,53 @@ AdbcStatusCode PostgresConnection::GetInfo(struct 
AdbcConnection* connection,
 }
 
 AdbcStatusCode PostgresConnection::GetObjects(
-    struct AdbcConnection* connection, int depth, const char* catalog,
-    const char* db_schema, const char* table_name, const char** table_types,
+    struct AdbcConnection* connection, int c_depth, const char* catalog,
+    const char* db_schema, const char* table_name, const char** table_type,
     const char* column_name, struct ArrowArrayStream* out, struct AdbcError* 
error) {
-  struct ArrowSchema schema;
-  std::memset(&schema, 0, sizeof(schema));
-  struct ArrowArray array;
-  std::memset(&array, 0, sizeof(array));
+  PostgresGetObjectsHelper new_helper(conn_);
+
+  const auto catalog_filter =
+      catalog ? std::make_optional(std::string_view(catalog)) : std::nullopt;
+  const auto schema_filter =
+      db_schema ? std::make_optional(std::string_view(db_schema)) : 
std::nullopt;
+  const auto table_filter =
+      table_name ? std::make_optional(std::string_view(table_name)) : 
std::nullopt;
+  const auto column_filter =
+      column_name ? std::make_optional(std::string_view(column_name)) : 
std::nullopt;
+  std::vector<std::string_view> table_type_filter;
+  while (table_type && *table_type) {
+    if (*table_type) {
+      table_type_filter.push_back(std::string_view(*table_type));
+    }
+    table_type++;
+  }
 
-  PqGetObjectsHelper helper =
-      PqGetObjectsHelper(conn_, depth, catalog, db_schema, table_name, 
table_types,
-                         column_name, &schema, &array, error);
-  AdbcStatusCode status = helper.GetObjects();
+  using adbc::driver::GetObjectsDepth;
 
-  if (status != ADBC_STATUS_OK) {
-    if (schema.release) schema.release(&schema);
-    if (array.release) array.release(&array);
-    return status;
+  GetObjectsDepth depth = GetObjectsDepth::kColumns;
+  switch (c_depth) {
+    case ADBC_OBJECT_DEPTH_CATALOGS:
+      depth = GetObjectsDepth::kCatalogs;
+      break;
+    case ADBC_OBJECT_DEPTH_COLUMNS:
+      depth = GetObjectsDepth::kColumns;
+      break;
+    case ADBC_OBJECT_DEPTH_DB_SCHEMAS:
+      depth = GetObjectsDepth::kSchemas;
+      break;
+    case ADBC_OBJECT_DEPTH_TABLES:
+      depth = GetObjectsDepth::kTables;
+      break;
+    default:
+      return Status::InvalidArgument("[libpq] GetObjects: invalid depth ", 
c_depth)
+          .ToAdbc(error);
   }
 
-  adbc::driver::MakeArrayStream(&schema, &array, out);
+  auto status = BuildGetObjects(&new_helper, depth, catalog_filter, 
schema_filter,
+                                table_filter, column_filter, 
table_type_filter, out);
+  RAISE_STATUS(error, new_helper.Close());
+  RAISE_STATUS(error, status);
+
   return ADBC_STATUS_OK;
 }
 
@@ -1250,7 +1105,11 @@ AdbcStatusCode PostgresConnection::SetOption(const char* 
key, const char* value,
     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};
+    char* value_esc = PQescapeIdentifier(conn_, value, strlen(value));
+    std::string query = std::string("SET search_path TO ") + value_esc;
+    PQfreemem(value_esc);
+
+    PqResultHelper result_helper{conn_, query};
     RAISE_STATUS(error, result_helper.Execute());
     return ADBC_STATUS_OK;
   }
diff --git a/c/driver/postgresql/result_helper.h 
b/c/driver/postgresql/result_helper.h
index 6dc7debf3..612573eda 100644
--- a/c/driver/postgresql/result_helper.h
+++ b/c/driver/postgresql/result_helper.h
@@ -18,6 +18,8 @@
 #pragma once
 
 #include <cassert>
+#include <charconv>
+#include <cstdint>
 #include <memory>
 #include <optional>
 #include <string>
@@ -30,6 +32,7 @@
 #include "copy/reader.h"
 #include "driver/framework/status.h"
 
+using adbc::driver::Result;
 using adbc::driver::Status;
 
 namespace adbcpq {
@@ -49,6 +52,33 @@ struct PqRecord {
     return result;
   }
 
+  Result<int64_t> ParseInteger() const {
+    const char* last = data + len;
+    int64_t value = 0;
+    auto result = std::from_chars(data, last, value, 10);
+    if (result.ec == std::errc() && result.ptr == last) {
+      return value;
+    } else {
+      return Status::Internal("Can't parse '", data, "' as integer");
+    }
+  }
+
+  Result<std::vector<std::string>> ParseTextArray() {
+    std::string text_array(data, len);
+    text_array.erase(0, 1);
+    text_array.erase(text_array.size() - 1);
+
+    std::vector<std::string> elements;
+    std::stringstream ss(std::move(text_array));
+    std::string tmp;
+
+    while (getline(ss, tmp, ',')) {
+      elements.push_back(std::move(tmp));
+    }
+
+    return elements;
+  }
+
   std::string_view value() { return std::string_view(data, len); }
 };
 
@@ -56,12 +86,11 @@ struct PqRecord {
 // row of a PGresult
 class PqResultRow {
  public:
-  PqResultRow(PGresult* result, int row_num) : result_(result), 
row_num_(row_num) {
-    ncols_ = PQnfields(result);
-  }
+  PqResultRow() : result_(nullptr), row_num_(-1) {}
+  PqResultRow(PGresult* result, int row_num) : result_(result), 
row_num_(row_num) {}
 
-  PqRecord operator[](const int& col_num) {
-    assert(col_num < ncols_);
+  PqRecord operator[](const int& col_num) const {
+    assert(col_num < PQnfields(result_));
     const char* data = PQgetvalue(result_, row_num_, col_num);
     const int len = PQgetlength(result_, row_num_, col_num);
     const bool is_null = PQgetisnull(result_, row_num_, col_num);
@@ -69,10 +98,13 @@ class PqResultRow {
     return PqRecord{data, len, is_null};
   }
 
+  bool IsValid() { return result_ && row_num_ >= 0 && row_num_ < 
PQntuples(result_); }
+
+  PqResultRow Next() { return PqResultRow(result_, row_num_ + 1); }
+
  private:
   PGresult* result_ = nullptr;
   int row_num_;
-  int ncols_;
 };
 
 // Helper to manager the lifecycle of a PQResult. The query argument
@@ -135,6 +167,7 @@ class PqResultHelper {
     return PQfname(result_, column_number);
   }
   Oid FieldType(int column_number) const { return PQftype(result_, 
column_number); }
+  PqResultRow Row(int i) { return PqResultRow(result_, i); }
 
   class iterator {
     const PqResultHelper& outer_;
diff --git a/c/driver/sqlite/sqlite.cc b/c/driver/sqlite/sqlite.cc
index c6277fb62..a5186d00b 100644
--- a/c/driver/sqlite/sqlite.cc
+++ b/c/driver/sqlite/sqlite.cc
@@ -218,10 +218,6 @@ struct SqliteGetObjectsHelper : public 
driver::GetObjectsHelper {
     std::string query =
         "SELECT DISTINCT name FROM pragma_database_list() WHERE name LIKE ?";
 
-    this->table_filter = table_filter;
-    this->column_filter = column_filter;
-    this->table_types = table_types;
-
     UNWRAP_STATUS(SqliteQuery::Scan(
         conn, query,
         [&](sqlite3_stmt* stmt) {
@@ -245,14 +241,17 @@ struct SqliteGetObjectsHelper : public 
driver::GetObjectsHelper {
     return status::Ok();
   }
 
-  Status LoadCatalogs() override { return status::Ok(); };
+  Status LoadCatalogs(std::optional<std::string_view> catalog_filter) override 
{
+    return status::Ok();
+  };
 
   Result<std::optional<std::string_view>> NextCatalog() override {
     if (next_catalog >= catalogs.size()) return std::nullopt;
     return catalogs[next_catalog++];
   }
 
-  Status LoadSchemas(std::string_view catalog) override {
+  Status LoadSchemas(std::string_view catalog,
+                     std::optional<std::string_view> schema_filter) override {
     next_schema = 0;
     return status::Ok();
   };
@@ -262,7 +261,9 @@ struct SqliteGetObjectsHelper : public 
driver::GetObjectsHelper {
     return schemas[next_schema++];
   }
 
-  Status LoadTables(std::string_view catalog, std::string_view schema) 
override {
+  Status LoadTables(std::string_view catalog, std::string_view schema,
+                    std::optional<std::string_view> table_filter,
+                    const std::vector<std::string_view>& table_types) override 
{
     next_table = 0;
     tables.clear();
     if (!schema.empty()) return status::Ok();
@@ -305,7 +306,8 @@ struct SqliteGetObjectsHelper : public 
driver::GetObjectsHelper {
   }
 
   Status LoadColumns(std::string_view catalog, std::string_view schema,
-                     std::string_view table) override {
+                     std::string_view table,
+                     std::optional<std::string_view> column_filter) override {
     // XXX: pragma_table_info doesn't appear to work with bind parameters
     // XXX: because we're saving the SqliteQuery, we also need to save the 
string builder
     columns_query.Reset();
@@ -482,9 +484,6 @@ struct SqliteGetObjectsHelper : public 
driver::GetObjectsHelper {
   };
 
   sqlite3* conn = nullptr;
-  std::optional<std::string_view> table_filter;
-  std::optional<std::string_view> column_filter;
-  std::vector<std::string_view> table_types;
   std::vector<std::string> catalogs;
   std::vector<std::string> schemas;
   std::vector<std::pair<std::string, std::string>> tables;
diff --git a/r/adbcpostgresql/configure b/r/adbcpostgresql/configure
index 0e4eb0014..e2abe82d5 100755
--- a/r/adbcpostgresql/configure
+++ b/r/adbcpostgresql/configure
@@ -53,8 +53,8 @@ elif [ ! -z "$HAS_PKG_CONFIG" ]; then
   PKG_LIBS="`pkg-config libpq --libs` $PKG_LIBS"
 elif [ ! -z "$HAS_PG_CONFIG" ]; then
   echo "Using pg_config to locate libpq headers/libs"
-  PKG_CPPFLAGS="-I`pg_config --includedir` $PKG_CPPFLAGS"
-  PKG_LIBS="-L`pg_config --libdir` -lpq $PKG_LIBS"
+  PKG_CPPFLAGS="-I`pg_config --includedir` `pg_config --cppflags` 
$PKG_CPPFLAGS"
+  PKG_LIBS="-L`pg_config --libdir` -lpq `pg_config --ldflags` `pg_config 
--libs` $PKG_LIBS"
 else
   echo "INCLUDE_DIR/LIB_DIR, pkg-config, and pg_config not found; trying 
PKG_LIBS=-lpq"
   PKG_LIBS="-lpq"

Reply via email to