From e129c975d5f5ad28fee33c028186b9a949ee4d29 Mon Sep 17 00:00:00 2001 From: Arun Sharma Date: Wed, 21 Oct 2026 09:52:30 -0700 Subject: [PATCH 01/14] fix(ci): download arch-specific DuckDB macOS builds DuckDB 2.0.0 split libduckdb-osx-universal.zip into libduckdb-osx-amd64.zip and libduckdb-osx-arm64.zip. Select DUCKDB_ARCH based on runner.arch (X64|AMD64 -> osx-amd64, ARM64 -> osx-arm64) instead of the removed osx-universal archive. --- .github/workflows/ci.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fa7a39f4..da90bfae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -171,7 +171,10 @@ jobs: ;; macOS) echo "DUCKDB_LIB=libduckdb.dylib" >> "$GITHUB_ENV" - echo "DUCKDB_ARCH=osx-universal" >> "$GITHUB_ENV" + case "${{ runner.arch }}" in + X64|AMD64) echo "DUCKDB_ARCH=osx-amd64" >> "$GITHUB_ENV" ;; + ARM64) echo "DUCKDB_ARCH=osx-arm64" >> "$GITHUB_ENV" ;; + esac ;; Windows) echo "DUCKDB_LIB=duckdb.dll" >> "$GITHUB_ENV" From cf70bef6aa4522c7db2bb094880dd213d961cebc Mon Sep 17 00:00:00 2001 From: Arun Sharma Date: Wed, 21 Oct 2026 10:45:22 -0700 Subject: [PATCH 02/14] fix(ci): move to DuckDB 2.0.0 renamed shared-libs assets 2.0.0 removed libduckdb-*.zip (and latest now resolves to 2.0.0, breaking every download with 404). Use the renamed duckdb-shared-libs--.tar.gz, pinned to v2.0.0. 2.0.0 also dropped the amalgamated duckdb.hpp from release assets, so fetch the C++ headers from a sparse source checkout at the same tag (verified the C headers there are byte-identical to the tarball ones) and install the same file set as before: duckdb*.h, duckdb.hpp, duckdb/ tree, and the shared lib. --- .github/workflows/ci.yml | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index da90bfae..b59f117f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -185,6 +185,11 @@ jobs: ;; esac + # Pinned: 2.0.0 renamed release assets (libduckdb-*.zip -> + # duckdb-shared-libs-*.tar.gz) and dropped the amalgamated + # duckdb.hpp, whose headers now come from a source checkout. + echo "DUCKDB_VERSION=v2.0.0" >> "$GITHUB_ENV" + # --- Ladybug artifact arch (maps runner arch to release names) --- case "${{ runner.arch }}" in X64|AMD64) echo "LBUG_ARTIFACT_ARCH=x86_64" >> "$GITHUB_ENV" ;; @@ -274,10 +279,16 @@ jobs: - name: Install DuckDB (Linux) if: runner.os == 'Linux' run: | - wget "https://github.com/duckdb/duckdb/releases/latest/download/libduckdb-${{ env.DUCKDB_ARCH }}.zip" - unzip "libduckdb-${{ env.DUCKDB_ARCH }}.zip" -d duckdb - cp duckdb/duckdb.h /usr/local/include/ - cp duckdb/duckdb.hpp /usr/local/include/ + wget "https://github.com/duckdb/duckdb/releases/download/${{ env.DUCKDB_VERSION }}/duckdb-shared-libs-${{ env.DUCKDB_ARCH }}.tar.gz" + mkdir -p duckdb && tar xzf "duckdb-shared-libs-${{ env.DUCKDB_ARCH }}.tar.gz" -C duckdb + # 2.0.0 dropped the amalgamated duckdb.hpp from release assets; + # fetch the C++ headers from source (C headers there are identical + # to the ones shipped in the tarball - verified by diff). + git clone -c advice.detachedHead=false --filter=blob:none --sparse --depth 1 --branch "${{ env.DUCKDB_VERSION }}" https://github.com/duckdb/duckdb duckdb-src + git -C duckdb-src sparse-checkout set src/include + cp -r duckdb-src/src/include/. duckdb/ + cp duckdb/duckdb*.h duckdb/duckdb.hpp /usr/local/include/ + cp -r duckdb/duckdb /usr/local/include/ cp duckdb/${{ env.DUCKDB_LIB }} /usr/local/lib/ ldconfig # CMake config so find_package(DuckDB) works From 0d129a7eba8adc83da159b318e62bb815f286eef Mon Sep 17 00:00:00 2001 From: Arun Sharma Date: Wed, 21 Oct 2026 11:25:24 -0700 Subject: [PATCH 03/14] fix(duckdb): migrate to DuckDB 2.0.0 QueryResult API 2.0.0 removed MaterializedQueryResult; Connection::Query now returns unique_ptr, a forward-only stream. Replace indexed GetValue(col, row) access with chunk Fetch() / row iterators, fetch the first chunk once and thread it through the placeholder-schema check (fetching consumes the stream), and convert the now vector names to strings via GetIdentifierName(). --- duckdb/src/catalog/duckdb_catalog.cpp | 50 +++++++++++++------ duckdb/src/connector/duckdb_connector.cpp | 2 +- duckdb/src/function/duckdb_scan.cpp | 3 +- .../src/include/catalog/duckdb_schema_utils.h | 18 ++++--- .../src/include/connector/duckdb_connector.h | 2 +- duckdb/src/include/function/duckdb_scan.h | 4 +- .../storage/attached_duckdb_database.h | 39 +++++++++++---- .../storage/attached_postgres_database.h | 2 +- 8 files changed, 81 insertions(+), 39 deletions(-) diff --git a/duckdb/src/catalog/duckdb_catalog.cpp b/duckdb/src/catalog/duckdb_catalog.cpp index 1eb7e910..ae975d6e 100644 --- a/duckdb/src/catalog/duckdb_catalog.cpp +++ b/duckdb/src/catalog/duckdb_catalog.cpp @@ -182,9 +182,9 @@ void DuckDBCatalog::createForeignRelTable(const std::string& tableName, bool int std::string srcTableName, dstTableName; std::string srcColName, dstColName; - for (auto i = 0u; i < fkResult->RowCount(); i++) { - auto colName = fkResult->GetValue(0, i).GetValue(); - auto refTable = fkResult->GetValue(1, i).GetValue(); + for (auto& row : *fkResult) { + auto colName = row.GetValue(0); + auto refTable = row.GetValue(1); auto lowerCol = colName; common::StringUtils::toLower(lowerCol); if (lowerCol == "src_id" || lowerCol.find("src") == 0) { @@ -322,8 +322,12 @@ static bool getTableInfo(const DuckDBConnector& connector, const std::string& ta if (result->RowCount() == 0) { return false; } - if (isPlaceholderSchema(*result)) { - std::unique_ptr schemaResult; + auto firstChunk = result->Fetch(); + if (!firstChunk || firstChunk->size() == 0) { + return false; + } + if (isPlaceholderSchema(result->names, *firstChunk, result->RowCount())) { + std::unique_ptr schemaResult; try { schemaResult = connector.executeQuery( std::format("SELECT * FROM {}.{}.{} LIMIT 0", quoteDuckDBIdentifier(catalogName), @@ -335,7 +339,10 @@ static bool getTableInfo(const DuckDBConnector& connector, const std::string& ta throw; } columnTypes.reserve(schemaResult->types.size()); - columnNames = schemaResult->names; + columnNames.reserve(schemaResult->names.size()); + for (auto& name : schemaResult->names) { + columnNames.push_back(name.GetIdentifierName()); + } for (auto& type : schemaResult->types) { try { columnTypes.push_back(DuckDBTypeConverter::convertDuckDBType(type.ToString())); @@ -350,17 +357,30 @@ static bool getTableInfo(const DuckDBConnector& connector, const std::string& ta } columnTypes.reserve(result->RowCount()); columnNames.reserve(result->RowCount()); - for (auto i = 0u; i < result->RowCount(); i++) { - try { - columnTypes.push_back(DuckDBTypeConverter::convertDuckDBType( - result->GetValue(0, i).GetValue())); - } catch (common::BinderException& e) { - if (skipUnsupportedTable) { - return false; + // QueryResult is a forward-only stream: the first chunk was already + // fetched above for the placeholder check, so process it first. + auto processChunk = [&](const duckdb::DataChunk& chunk) { + for (auto i = 0u; i < chunk.size(); i++) { + try { + columnTypes.push_back(DuckDBTypeConverter::convertDuckDBType( + chunk.GetValue(0, i).GetValue())); + } catch (common::BinderException& e) { + if (skipUnsupportedTable) { + return false; + } + throw; } - throw; + columnNames.push_back(chunk.GetValue(1, i).GetValue()); + } + return true; + }; + if (!processChunk(*firstChunk)) { + return false; + } + while (auto chunk = result->Fetch()) { + if (chunk->size() != 0 && !processChunk(*chunk)) { + return false; } - columnNames.push_back(result->GetValue(1, i).GetValue()); } return true; } diff --git a/duckdb/src/connector/duckdb_connector.cpp b/duckdb/src/connector/duckdb_connector.cpp index cab388e1..16154512 100644 --- a/duckdb/src/connector/duckdb_connector.cpp +++ b/duckdb/src/connector/duckdb_connector.cpp @@ -3,7 +3,7 @@ namespace lbug { namespace duckdb_extension { -std::unique_ptr DuckDBConnector::executeQuery( +std::unique_ptr DuckDBConnector::executeQuery( std::string query) const { DASSERT(instance != nullptr && connection != nullptr); auto result = connection->Query(query); diff --git a/duckdb/src/function/duckdb_scan.cpp b/duckdb/src/function/duckdb_scan.cpp index 67939ce0..0edb3c23 100644 --- a/duckdb/src/function/duckdb_scan.cpp +++ b/duckdb/src/function/duckdb_scan.cpp @@ -80,8 +80,7 @@ std::string DuckDBScanBindData::getDescription() const { return getSQL(); } -DuckDBScanSharedState::DuckDBScanSharedState( - std::shared_ptr queryResult) +DuckDBScanSharedState::DuckDBScanSharedState(std::shared_ptr queryResult) : function::TableFuncSharedState{queryResult->RowCount()}, queryResult{std::move(queryResult)} { } diff --git a/duckdb/src/include/catalog/duckdb_schema_utils.h b/duckdb/src/include/catalog/duckdb_schema_utils.h index cbd7ba17..72fe215e 100644 --- a/duckdb/src/include/catalog/duckdb_schema_utils.h +++ b/duckdb/src/include/catalog/duckdb_schema_utils.h @@ -34,18 +34,22 @@ inline std::string quoteDuckDBIdentifier(const std::string& value) { return result; } -inline bool isPlaceholderSchema(duckdb::MaterializedQueryResult& result) { - if (result.RowCount() != 1) { +// In DuckDB 2.0.0 QueryResult is a forward-only stream: Fetch() consumes. +// Take the already-fetched first chunk (plus names/row count) instead of +// fetching again so callers can keep streaming the remaining chunks. +inline bool isPlaceholderSchema(const std::vector& names, + const duckdb::DataChunk& firstChunk, duckdb::idx_t totalRowCount) { + if (totalRowCount != 1 || firstChunk.size() != 1) { return false; } - const auto nameColumn = std::find(result.names.begin(), result.names.end(), "column_name"); - const auto typeColumn = std::find(result.names.begin(), result.names.end(), "data_type"); - if (nameColumn == result.names.end() || typeColumn == result.names.end()) { + const auto nameColumn = std::find(names.begin(), names.end(), "column_name"); + const auto typeColumn = std::find(names.begin(), names.end(), "data_type"); + if (nameColumn == names.end() || typeColumn == names.end()) { return false; } - return result.GetValue(std::distance(result.names.begin(), nameColumn), 0) + return firstChunk.GetValue(std::distance(names.begin(), nameColumn), 0) .GetValue() == "__" && - result.GetValue(std::distance(result.names.begin(), typeColumn), 0) + firstChunk.GetValue(std::distance(names.begin(), typeColumn), 0) .GetValue() == "UNKNOWN"; } diff --git a/duckdb/src/include/connector/duckdb_connector.h b/duckdb/src/include/connector/duckdb_connector.h index 53427f0f..542f0b88 100644 --- a/duckdb/src/include/connector/duckdb_connector.h +++ b/duckdb/src/include/connector/duckdb_connector.h @@ -37,7 +37,7 @@ class DuckDBConnector { std::move(columnNames), *this); } - std::unique_ptr executeQuery(std::string query) const; + std::unique_ptr executeQuery(std::string query) const; void initRemoteFSSecrets(main::ClientContext* context) const { for (auto& fsConfig : httpfs_extension::S3FileSystemConfig::getAvailableConfigs()) { diff --git a/duckdb/src/include/function/duckdb_scan.h b/duckdb/src/include/function/duckdb_scan.h index a9eab22c..f82115fa 100644 --- a/duckdb/src/include/function/duckdb_scan.h +++ b/duckdb/src/include/function/duckdb_scan.h @@ -106,9 +106,9 @@ struct DuckDBScanBindData : function::TableFuncBindData { }; struct DuckDBScanSharedState final : function::TableFuncSharedState { - explicit DuckDBScanSharedState(std::shared_ptr queryResult); + explicit DuckDBScanSharedState(std::shared_ptr queryResult); - std::shared_ptr queryResult; + std::shared_ptr queryResult; }; function::TableFunction getScanFunction(std::shared_ptr scanInfo); diff --git a/duckdb/src/include/storage/attached_duckdb_database.h b/duckdb/src/include/storage/attached_duckdb_database.h index 81925068..2e5efe7b 100644 --- a/duckdb/src/include/storage/attached_duckdb_database.h +++ b/duckdb/src/include/storage/attached_duckdb_database.h @@ -55,8 +55,7 @@ class AttachedDuckDBDatabase : public main::AttachedDatabase { const DuckDBConnector& getConnector() const { return *connector; } - std::unique_ptr executeQuery( - const std::string& query) const override { + std::unique_ptr executeQuery(const std::string& query) const override { return connector->executeQuery(query); } @@ -94,24 +93,44 @@ class AttachedDuckDBDatabase : public main::AttachedDatabase { escapeSingleQuotes(unqualified), filters); auto result = connector->executeQuery(query); if (result && result->RowCount() != 0) { - if (isPlaceholderSchema(*result)) { + // QueryResult is a forward-only stream: fetch the first chunk + // once and thread it through instead of re-reading the result. + auto chunk = result->Fetch(); + if (!chunk || chunk->size() == 0) { + continue; + } + if (isPlaceholderSchema(result->names, *chunk, result->RowCount())) { // Use the catalog and schema of the matched table, including // for two-part references outside DuckDB's search path. const auto qualifiedName = std::format("{}.{}.{}", - quoteDuckDBIdentifier(result->GetValue(2, 0).GetValue()), - quoteDuckDBIdentifier(result->GetValue(3, 0).GetValue()), + quoteDuckDBIdentifier(chunk->GetValue(2, 0).GetValue()), + quoteDuckDBIdentifier(chunk->GetValue(3, 0).GetValue()), quoteDuckDBIdentifier(unqualified)); try { - return connector - ->executeQuery(std::format("SELECT * FROM {} LIMIT 0", qualifiedName)) - ->names; + auto schemaResult = connector->executeQuery( + std::format("SELECT * FROM {} LIMIT 0", qualifiedName)); + std::vector schemaColumnNames; + schemaColumnNames.reserve(schemaResult->names.size()); + for (auto& name : schemaResult->names) { + schemaColumnNames.push_back(name.GetIdentifierName()); + } + return schemaColumnNames; } catch (const common::Exception&) { return {}; } } std::vector columnNames; - for (auto i = 0u; i < result->RowCount(); i++) { - columnNames.push_back(result->GetValue(0, i).GetValue()); + columnNames.reserve(result->RowCount()); + auto appendFirstColumn = [&](const duckdb::DataChunk& c) { + for (auto i = 0u; i < c.size(); i++) { + columnNames.push_back(c.GetValue(0, i).GetValue()); + } + }; + appendFirstColumn(*chunk); + while (auto next = result->Fetch()) { + if (next->size() != 0) { + appendFirstColumn(*next); + } } return columnNames; } diff --git a/postgres/src/include/storage/attached_postgres_database.h b/postgres/src/include/storage/attached_postgres_database.h index 7d26d778..4619cbd2 100644 --- a/postgres/src/include/storage/attached_postgres_database.h +++ b/postgres/src/include/storage/attached_postgres_database.h @@ -15,7 +15,7 @@ class AttachedPostgresDatabase final : public duckdb_extension::AttachedDuckDBDa std::move(catalog), std::move(connector)}, attachedCatalogNameInDuckDB{std::move(attachedCatalogNameInDuckDB)} {} - std::unique_ptr executeQuery(const std::string& query) const { + std::unique_ptr executeQuery(const std::string& query) const { return connector->executeQuery(query); } From df2795d1bcf7592ca8fee81af0ae96e9cf689b33 Mon Sep 17 00:00:00 2001 From: Arun Sharma Date: Wed, 21 Oct 2026 11:32:58 -0700 Subject: [PATCH 04/14] fix(duckdb): adapt to 2.0.0 private result metadata and vector APIs names/types are now private on QueryResult: use GetNames()/GetTypes(). Vector::GetData() is gone: use FlatVector::GetData[Mutable]. The deprecated ListVector::GetEntry would trip -Werror: use GetChildMutable. Add the now-required list_vector.hpp/struct_vector.hpp includes. --- duckdb/src/catalog/duckdb_catalog.cpp | 10 +++++----- .../src/connector/duckdb_result_converter.cpp | 17 +++++++++++------ .../include/storage/attached_duckdb_database.h | 6 +++--- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/duckdb/src/catalog/duckdb_catalog.cpp b/duckdb/src/catalog/duckdb_catalog.cpp index ae975d6e..ecaf28f5 100644 --- a/duckdb/src/catalog/duckdb_catalog.cpp +++ b/duckdb/src/catalog/duckdb_catalog.cpp @@ -326,7 +326,7 @@ static bool getTableInfo(const DuckDBConnector& connector, const std::string& ta if (!firstChunk || firstChunk->size() == 0) { return false; } - if (isPlaceholderSchema(result->names, *firstChunk, result->RowCount())) { + if (isPlaceholderSchema(result->GetNames(), *firstChunk, result->RowCount())) { std::unique_ptr schemaResult; try { schemaResult = connector.executeQuery( @@ -338,12 +338,12 @@ static bool getTableInfo(const DuckDBConnector& connector, const std::string& ta } throw; } - columnTypes.reserve(schemaResult->types.size()); - columnNames.reserve(schemaResult->names.size()); - for (auto& name : schemaResult->names) { + columnTypes.reserve(schemaResult->GetTypes().size()); + columnNames.reserve(schemaResult->GetNames().size()); + for (auto& name : schemaResult->GetNames()) { columnNames.push_back(name.GetIdentifierName()); } - for (auto& type : schemaResult->types) { + for (auto& type : schemaResult->GetTypes()) { try { columnTypes.push_back(DuckDBTypeConverter::convertDuckDBType(type.ToString())); } catch (common::BinderException&) { diff --git a/duckdb/src/connector/duckdb_result_converter.cpp b/duckdb/src/connector/duckdb_result_converter.cpp index 1415f6d0..39fb5e99 100644 --- a/duckdb/src/connector/duckdb_result_converter.cpp +++ b/duckdb/src/connector/duckdb_result_converter.cpp @@ -2,6 +2,9 @@ #include +#include "duckdb/common/vector/list_vector.hpp" +#include "duckdb/common/vector/struct_vector.hpp" + #include "common/type_utils.h" #include "common/types/types.h" @@ -13,7 +16,7 @@ using namespace common; template void convertDuckDBVectorToVector(duckdb::Vector& duckDBVector, ValueVector& result, uint64_t numValuesToCopy) { - auto duckDBData = (T*)duckDBVector.GetData(); + auto duckDBData = duckdb::FlatVector::GetDataMutable(duckDBVector); auto validityMasks = duckdb::FlatVector::Validity(duckDBVector); memcpy(result.getData(), duckDBData, numValuesToCopy * result.getNumBytesPerValue()); for (auto i = 0u; i < numValuesToCopy; i++) { @@ -31,7 +34,7 @@ void convertDuckDBVectorToVector(duckdb::Vector& duckDBVector, Val template<> void convertDuckDBVectorToVector(duckdb::Vector& duckDBVector, ValueVector& result, uint64_t numValuesToCopy) { - auto strs = reinterpret_cast(duckDBVector.GetData()); + auto strs = duckdb::FlatVector::GetDataMutable(duckDBVector); auto validityMasks = duckdb::FlatVector::Validity(duckDBVector); for (auto i = 0u; i < numValuesToCopy; i++) { result.setNull(i, !validityMasks.RowIsValid(i)); @@ -63,9 +66,9 @@ void convertDuckDBVectorToVector(duckdb::Vector& duckDBVector, Val } break; case duckdb::LogicalTypeId::MAP: case duckdb::LogicalTypeId::LIST: { - memcpy(result.getData(), duckDBVector.GetData(), + memcpy(result.getData(), duckdb::FlatVector::GetData(duckDBVector), numValuesToCopy * result.getNumBytesPerValue()); - auto listEntries = reinterpret_cast(duckDBVector.GetData()); + auto listEntries = duckdb::FlatVector::GetDataMutable(duckDBVector); for (auto i = 0u; i < numValuesToCopy; i++) { result.setNull(i, !validityMasks.RowIsValid(i)); if (!result.isNull(i)) { @@ -82,7 +85,8 @@ void convertDuckDBVectorToVector(duckdb::Vector& duckDBVector, Val duckdb_conversion_func_t conversion_func; DuckDBResultConverter::getDuckDBVectorConversionFunc(dataVec->dataType.getPhysicalType(), conversion_func); - conversion_func(duckdb::ListVector::GetEntry(duckDBVector), *dataVec, numValuesInDataVec); + conversion_func(duckdb::ListVector::GetChildMutable(duckDBVector), *dataVec, + numValuesInDataVec); } template<> @@ -93,7 +97,8 @@ void convertDuckDBVectorToVector(duckdb::Vector& duckDBVector, V if (result.dataType.getLogicalTypeID() == LogicalTypeID::UNION) { // Special handling for union datatype, since duckdb stores tagId as INT8 idx++; - auto duckDBData = (uint8_t*)duckdbChildrenVectors[0]->GetData(); + auto duckDBData = + duckdb::FlatVector::GetDataMutable(*duckdbChildrenVectors[0]); auto validityMasks = duckdb::FlatVector::Validity(duckDBVector); for (auto i = 0u; i < numValuesToCopy; i++) { UnionVector::getTagVector(&result)->setValue(i, (uint16_t)duckDBData[i]); diff --git a/duckdb/src/include/storage/attached_duckdb_database.h b/duckdb/src/include/storage/attached_duckdb_database.h index 2e5efe7b..7344a025 100644 --- a/duckdb/src/include/storage/attached_duckdb_database.h +++ b/duckdb/src/include/storage/attached_duckdb_database.h @@ -99,7 +99,7 @@ class AttachedDuckDBDatabase : public main::AttachedDatabase { if (!chunk || chunk->size() == 0) { continue; } - if (isPlaceholderSchema(result->names, *chunk, result->RowCount())) { + if (isPlaceholderSchema(result->GetNames(), *chunk, result->RowCount())) { // Use the catalog and schema of the matched table, including // for two-part references outside DuckDB's search path. const auto qualifiedName = std::format("{}.{}.{}", @@ -110,8 +110,8 @@ class AttachedDuckDBDatabase : public main::AttachedDatabase { auto schemaResult = connector->executeQuery( std::format("SELECT * FROM {} LIMIT 0", qualifiedName)); std::vector schemaColumnNames; - schemaColumnNames.reserve(schemaResult->names.size()); - for (auto& name : schemaResult->names) { + schemaColumnNames.reserve(schemaResult->GetNames().size()); + for (auto& name : schemaResult->GetNames()) { schemaColumnNames.push_back(name.GetIdentifierName()); } return schemaColumnNames; From c2c2def05b484fb4f101286fe68015b78bca9ddb Mon Sep 17 00:00:00 2001 From: Arun Sharma Date: Wed, 21 Oct 2026 11:54:01 -0700 Subject: [PATCH 05/14] fix(duckdb): StructVector entries are values in 2.0.0, temp-pin ladybug checkout GetEntries now returns vector (was pointer elements): drop the dereferences. Temporarily check out ladybug at the DuckDB-2.0.0 PR branch, which carries the AttachedDatabase::executeQuery base-class fix this build needs. Revert to the default branch once that PR merges. --- .github/workflows/ci.yml | 4 ++++ duckdb/src/connector/duckdb_result_converter.cpp | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b59f117f..d8fa9a93 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -121,10 +121,14 @@ jobs: steps: # ── Source checkouts ────────────────────────────────────────── + # TEMPORARY: track the ladybug DuckDB-2.0.0 PR branch until it merges + # to main (it carries the AttachedDatabase::executeQuery base-class + # fix this CI needs). Revert to default branch afterwards. - name: Checkout ladybug uses: actions/checkout@v4 with: repository: LadybugDB/ladybug + ref: fix-duckdb-osx-download fetch-depth: 1 path: ladybug diff --git a/duckdb/src/connector/duckdb_result_converter.cpp b/duckdb/src/connector/duckdb_result_converter.cpp index 39fb5e99..1e7bece8 100644 --- a/duckdb/src/connector/duckdb_result_converter.cpp +++ b/duckdb/src/connector/duckdb_result_converter.cpp @@ -98,7 +98,7 @@ void convertDuckDBVectorToVector(duckdb::Vector& duckDBVector, V // Special handling for union datatype, since duckdb stores tagId as INT8 idx++; auto duckDBData = - duckdb::FlatVector::GetDataMutable(*duckdbChildrenVectors[0]); + duckdb::FlatVector::GetDataMutable(duckdbChildrenVectors[0]); auto validityMasks = duckdb::FlatVector::Validity(duckDBVector); for (auto i = 0u; i < numValuesToCopy; i++) { UnionVector::getTagVector(&result)->setValue(i, (uint16_t)duckDBData[i]); @@ -111,7 +111,7 @@ void convertDuckDBVectorToVector(duckdb::Vector& duckDBVector, V auto fieldVector = StructVector::getFieldVector(&result, idx); DuckDBResultConverter::getDuckDBVectorConversionFunc( fieldVector->dataType.getPhysicalType(), conversionFunc); - conversionFunc(*duckdbChildVector, *fieldVector, numValuesToCopy); + conversionFunc(duckdbChildVector, *fieldVector, numValuesToCopy); } } From 35eecaa337d2b545985025fb858038a27db04c95 Mon Sep 17 00:00:00 2001 From: Arun Sharma Date: Wed, 21 Oct 2026 12:00:06 -0700 Subject: [PATCH 06/14] fix(postgres): adapt sql_query bind to 2.0.0 result metadata API names/types are private on QueryResult: use ColumnCount()/GetTypes(), and convert Identifier to string explicitly via GetIdentifierName(). --- postgres/src/function/sql_query.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/postgres/src/function/sql_query.cpp b/postgres/src/function/sql_query.cpp index 4dec6ff6..65638079 100644 --- a/postgres/src/function/sql_query.cpp +++ b/postgres/src/function/sql_query.cpp @@ -53,10 +53,10 @@ static std::unique_ptr bindFunc(const ClientContext* context, auto queryResult = attachedPostgresDB.executeQuery(queryToExecuteInDuckDB); std::vector returnTypes; std::vector columnNamesInPG; - for (auto i = 0u; i < queryResult->names.size(); i++) { - columnNamesInPG.push_back(queryResult->ColumnName(i)); + for (auto i = 0u; i < queryResult->ColumnCount(); i++) { + columnNamesInPG.push_back(queryResult->ColumnName(i).GetIdentifierName()); returnTypes.push_back( - DuckDBTypeConverter::convertDuckDBType(queryResult->types[i].ToString())); + DuckDBTypeConverter::convertDuckDBType(queryResult->GetTypes()[i].ToString())); } auto returnColumnNames = TableFunction::extractYieldVariables(columnNamesInPG, input->yieldVariables); From 5d1393c611db48d49a90b9dd8c87f098a680bef7 Mon Sep 17 00:00:00 2001 From: Arun Sharma Date: Wed, 21 Oct 2026 12:00:41 -0700 Subject: [PATCH 07/14] fix(delta,iceberg,azure): adapt bind metadata reads to 2.0.0 result API names/types are private on QueryResult: use GetTypes()/GetNames() and convert Identifier explicitly via GetIdentifierName(). --- azure/src/function/azure_scan.cpp | 6 +++--- delta/src/function/delta_scan.cpp | 6 +++--- iceberg/src/function/iceberg_bindfunc.cpp | 10 +++++----- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/azure/src/function/azure_scan.cpp b/azure/src/function/azure_scan.cpp index 39a8f943..7827d062 100644 --- a/azure/src/function/azure_scan.cpp +++ b/azure/src/function/azure_scan.cpp @@ -18,14 +18,14 @@ static std::unique_ptr bindFunc(main::ClientContext* context, auto result = connector->executeQuery(query + " LIMIT 1"); std::vector returnTypes; std::vector returnColumnNames = scanInput->expectedColumnNames; - for (auto type : result->types) { + for (auto type : result->GetTypes()) { returnTypes.push_back( duckdb_extension::DuckDBTypeConverter::convertDuckDBType(type.ToString())); } if (scanInput->expectedColumnNames.empty()) { - for (auto name : result->names) { - returnColumnNames.push_back(name); + for (auto name : result->GetNames()) { + returnColumnNames.push_back(name.GetIdentifierName()); } } DASSERT(returnTypes.size() == returnColumnNames.size()); diff --git a/delta/src/function/delta_scan.cpp b/delta/src/function/delta_scan.cpp index 7a457a30..02aad9ea 100644 --- a/delta/src/function/delta_scan.cpp +++ b/delta/src/function/delta_scan.cpp @@ -19,14 +19,14 @@ static std::unique_ptr bindFunc(main::ClientContext* context, auto result = connector->executeQuery(query + " LIMIT 1"); std::vector returnTypes; std::vector returnColumnNames = scanInput->expectedColumnNames; - for (auto type : result->types) { + for (auto type : result->GetTypes()) { returnTypes.push_back( duckdb_extension::DuckDBTypeConverter::convertDuckDBType(type.ToString())); } if (scanInput->expectedColumnNames.empty()) { - for (auto name : result->names) { - returnColumnNames.push_back(name); + for (auto name : result->GetNames()) { + returnColumnNames.push_back(name.GetIdentifierName()); } } DASSERT(returnTypes.size() == returnColumnNames.size()); diff --git a/iceberg/src/function/iceberg_bindfunc.cpp b/iceberg/src/function/iceberg_bindfunc.cpp index edcf0b18..00bc594d 100644 --- a/iceberg/src/function/iceberg_bindfunc.cpp +++ b/iceberg/src/function/iceberg_bindfunc.cpp @@ -171,18 +171,18 @@ std::unique_ptr bindFuncHelper(main::ClientContext* context, dynamic_cast_checked(input->extraInput.get()); returnColumnNames = scanInput->expectedColumnNames; if (scanInput->expectedColumnNames.empty()) { - for (auto name : result->names) { - returnColumnNames.push_back(name); + for (auto name : result->GetNames()) { + returnColumnNames.push_back(name.GetIdentifierName()); } } } - for (auto type : result->types) { + for (auto type : result->GetTypes()) { returnTypes.push_back( duckdb_extension::DuckDBTypeConverter::convertDuckDBType(type.ToString())); } if (functionName != "ICEBERG_SCAN") { - for (auto name : result->names) { - returnColumnNames.push_back(name); + for (auto name : result->GetNames()) { + returnColumnNames.push_back(name.GetIdentifierName()); } } DASSERT(returnTypes.size() == returnColumnNames.size()); From e48e70cebc884cf1d388253d3301e7e1818171b6 Mon Sep 17 00:00:00 2001 From: Arun Sharma Date: Wed, 21 Oct 2026 12:25:51 -0700 Subject: [PATCH 08/14] fix(duckdb): use untyped FlatVector data access in result converter The templated GetDataMutable validates T against DuckDB's own types and throws for ladybug-side types (e.g. interval_t/string_t). Use the untyped accessors plus casts exactly like the old GetData() member did. --- duckdb/src/connector/duckdb_result_converter.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/duckdb/src/connector/duckdb_result_converter.cpp b/duckdb/src/connector/duckdb_result_converter.cpp index 1e7bece8..4d2e5c03 100644 --- a/duckdb/src/connector/duckdb_result_converter.cpp +++ b/duckdb/src/connector/duckdb_result_converter.cpp @@ -16,7 +16,7 @@ using namespace common; template void convertDuckDBVectorToVector(duckdb::Vector& duckDBVector, ValueVector& result, uint64_t numValuesToCopy) { - auto duckDBData = duckdb::FlatVector::GetDataMutable(duckDBVector); + auto duckDBData = (T*)duckdb::FlatVector::GetDataMutable(duckDBVector); auto validityMasks = duckdb::FlatVector::Validity(duckDBVector); memcpy(result.getData(), duckDBData, numValuesToCopy * result.getNumBytesPerValue()); for (auto i = 0u; i < numValuesToCopy; i++) { @@ -34,7 +34,8 @@ void convertDuckDBVectorToVector(duckdb::Vector& duckDBVector, Val template<> void convertDuckDBVectorToVector(duckdb::Vector& duckDBVector, ValueVector& result, uint64_t numValuesToCopy) { - auto strs = duckdb::FlatVector::GetDataMutable(duckDBVector); + auto strs = + reinterpret_cast(duckdb::FlatVector::GetDataMutable(duckDBVector)); auto validityMasks = duckdb::FlatVector::Validity(duckDBVector); for (auto i = 0u; i < numValuesToCopy; i++) { result.setNull(i, !validityMasks.RowIsValid(i)); @@ -68,7 +69,7 @@ void convertDuckDBVectorToVector(duckdb::Vector& duckDBVector, Val case duckdb::LogicalTypeId::LIST: { memcpy(result.getData(), duckdb::FlatVector::GetData(duckDBVector), numValuesToCopy * result.getNumBytesPerValue()); - auto listEntries = duckdb::FlatVector::GetDataMutable(duckDBVector); + auto listEntries = (duckdb::list_entry_t*)duckdb::FlatVector::GetDataMutable(duckDBVector); for (auto i = 0u; i < numValuesToCopy; i++) { result.setNull(i, !validityMasks.RowIsValid(i)); if (!result.isNull(i)) { @@ -97,8 +98,7 @@ void convertDuckDBVectorToVector(duckdb::Vector& duckDBVector, V if (result.dataType.getLogicalTypeID() == LogicalTypeID::UNION) { // Special handling for union datatype, since duckdb stores tagId as INT8 idx++; - auto duckDBData = - duckdb::FlatVector::GetDataMutable(duckdbChildrenVectors[0]); + auto duckDBData = duckdb::FlatVector::GetDataMutable(duckdbChildrenVectors[0]); auto validityMasks = duckdb::FlatVector::Validity(duckDBVector); for (auto i = 0u; i < numValuesToCopy; i++) { UnionVector::getTagVector(&result)->setValue(i, (uint16_t)duckDBData[i]); From a776d420c9167b7404ac4059b537b89abe3ee681 Mon Sep 17 00:00:00 2001 From: Arun Sharma Date: Wed, 21 Oct 2026 12:41:08 -0700 Subject: [PATCH 09/14] fix(duckdb,postgres): restore 1.x bind contracts on DuckDB 2.0.0 2.0.0 executes the inner postgres_query statement at bind time, so DDL and DML no longer fail with 'No fields returned' but run (or fail confusingly, e.g. read-only errors) during planning. Validate that the inner query is a plain SELECT up front via ExtractStatements. 2.0.0 also stopped rejecting duplicate output column names, so enforce the historical contract in bind. Update remote attach-not-exist expectations to the 2.0.0 httpfs error messages. --- duckdb/src/connector/duckdb_connector.cpp | 20 +++++++++++++++++++ .../src/include/connector/duckdb_connector.h | 6 ++++++ duckdb/test/test_files/remote_duckdb.test | 4 ++-- postgres/src/function/sql_query.cpp | 15 ++++++++++++++ 4 files changed, 43 insertions(+), 2 deletions(-) diff --git a/duckdb/src/connector/duckdb_connector.cpp b/duckdb/src/connector/duckdb_connector.cpp index 16154512..07a1debe 100644 --- a/duckdb/src/connector/duckdb_connector.cpp +++ b/duckdb/src/connector/duckdb_connector.cpp @@ -1,5 +1,10 @@ #include "connector/duckdb_connector.h" +#include +#include + +#include "common/exception/binder.h" + namespace lbug { namespace duckdb_extension { @@ -13,5 +18,20 @@ std::unique_ptr DuckDBConnector::executeQuery( return result; } +void DuckDBConnector::validateSelectQuery(const std::string& query) const { + DASSERT(instance != nullptr && connection != nullptr); + auto statements = connection->ExtractStatements(query); + const auto isSelectOnly = !statements.empty() && + std::all_of(statements.begin(), statements.end(), [](const auto& stmt) { + return stmt->type == duckdb::StatementType::SELECT_STATEMENT; + }); + if (!isSelectOnly) { + throw common::BinderException{std::format("No fields returned by query \"{}\" - " + "the query must be a SELECT statement that " + "returns at least one column", + query)}; + } +} + } // namespace duckdb_extension } // namespace lbug diff --git a/duckdb/src/include/connector/duckdb_connector.h b/duckdb/src/include/connector/duckdb_connector.h index 542f0b88..03dc7cdd 100644 --- a/duckdb/src/include/connector/duckdb_connector.h +++ b/duckdb/src/include/connector/duckdb_connector.h @@ -39,6 +39,12 @@ class DuckDBConnector { std::unique_ptr executeQuery(std::string query) const; + // DuckDB 2.0 executes the inner query while binding postgres_query, so a + // DDL/DML statement would run (or fail confusingly) during planning. + // Reject anything that is not a plain SELECT up front, preserving the + // historical contract that only column-returning SELECTs are accepted. + void validateSelectQuery(const std::string& query) const; + void initRemoteFSSecrets(main::ClientContext* context) const { for (auto& fsConfig : httpfs_extension::S3FileSystemConfig::getAvailableConfigs()) { executeQuery(DuckDBSecretManager::getRemoteS3FSSecret(context, fsConfig)); diff --git a/duckdb/test/test_files/remote_duckdb.test b/duckdb/test/test_files/remote_duckdb.test index 6b2688ee..32270449 100644 --- a/duckdb/test/test_files/remote_duckdb.test +++ b/duckdb/test/test_files/remote_duckdb.test @@ -33,7 +33,7 @@ -LOAD_DYNAMIC_EXTENSION duckdb -STATEMENT ATTACH 'http://localhost/dataset/databases/duckdb_database/tinysnb132.db' as remoteTiny (dbtype duckdb, skip_unsupported_table=true); ---- error(regex) -^.+ Error: Cannot open database "http://localhost/dataset/databases/duckdb_database/tinysnb132.db" in read-only mode: database does not exist$ +^.+ Error: Could not connect to server error for HTTP HEAD to 'http://localhost/dataset/databases/duckdb_database/tinysnb132.db'$ -CASE AttachPublicS3Duckdb -SKIP @@ -53,4 +53,4 @@ Attached database successfully. -STATEMENT ATTACH 's3://duckdb-blobs/databases/stations1.duckdb' as remoteTiny (dbtype duckdb, skip_unsupported_table=true); # Error type is determined by duckdb version. ---- error(regex) -^.+ Error: Cannot open database "s3://duckdb-blobs/databases/stations1.duckdb" in read-only mode: database does not exist$ +^.+ Error: HTTP GET error reading 's3://duckdb-blobs/databases/stations1.duckdb' \(HTTP 403 Forbidden\)$ diff --git a/postgres/src/function/sql_query.cpp b/postgres/src/function/sql_query.cpp index 65638079..d539000e 100644 --- a/postgres/src/function/sql_query.cpp +++ b/postgres/src/function/sql_query.cpp @@ -11,6 +11,7 @@ #include "storage/attached_postgres_database.h" #include "storage/postgres_storage.h" #include +#include using namespace lbug::function; using namespace lbug::main; @@ -50,6 +51,9 @@ static std::unique_ptr bindFunc(const ClientContext* context, auto queryToExecuteInDuckDB = std::vformat(queryTemplate, std::make_format_args("*")) + " limit 1"; auto& attachedPostgresDB = attachedDB->constCast(); + // postgres_query executes the inner query at bind time in DuckDB 2.0+, so + // reject non-SELECT statements before any execution is attempted. + attachedPostgresDB.getConnector().validateSelectQuery(query); auto queryResult = attachedPostgresDB.executeQuery(queryToExecuteInDuckDB); std::vector returnTypes; std::vector columnNamesInPG; @@ -58,6 +62,17 @@ static std::unique_ptr bindFunc(const ClientContext* context, returnTypes.push_back( DuckDBTypeConverter::convertDuckDBType(queryResult->GetTypes()[i].ToString())); } + // DuckDB 2.0 no longer rejects duplicate output column names from + // postgres_query, so enforce the historical contract here. + std::unordered_set seenColumnNames; + for (auto& columnName : columnNamesInPG) { + auto lowerName = columnName; + StringUtils::toLower(lowerName); + if (!seenColumnNames.insert(lowerName).second) { + throw common::BinderException{std::format( + "table \"postgres_query\" has duplicate column name \"{}\"", columnName)}; + } + } auto returnColumnNames = TableFunction::extractYieldVariables(columnNamesInPG, input->yieldVariables); auto columns = input->binder->createVariables(returnColumnNames, returnTypes); From ad6c364b1dc4d6d8b4cb4b0f2fff7e6875cecfa9 Mon Sep 17 00:00:00 2001 From: Arun Sharma Date: Wed, 21 Oct 2026 12:46:14 -0700 Subject: [PATCH 10/14] fix(postgres): include string_utils.h for toLower --- postgres/src/function/sql_query.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/postgres/src/function/sql_query.cpp b/postgres/src/function/sql_query.cpp index d539000e..2d445bfe 100644 --- a/postgres/src/function/sql_query.cpp +++ b/postgres/src/function/sql_query.cpp @@ -10,6 +10,7 @@ #include "processor/execution_context.h" #include "storage/attached_postgres_database.h" #include "storage/postgres_storage.h" +#include "common/string_utils.h" #include #include From 33dc2800ad96506b7be9b7f8c45383f5d04f0c89 Mon Sep 17 00:00:00 2001 From: Arun Sharma Date: Wed, 21 Oct 2026 13:08:43 -0700 Subject: [PATCH 11/14] fix(tests): update expectations to DuckDB 2.0.0 behavior sql_query DDL rejection now comes from ladybug's binder ('Binder exception:') instead of DuckDB; duplicate output columns are auto-renamed (name_1) instead of rejected, so assert the new successful shape. Remote-attach failures now surface raw httpfs errors; match them without regex paren escapes. --- duckdb/test/test_files/remote_duckdb.test | 2 +- postgres/test/test_files/sql_query.test | 31 ++++++++++++++++++++--- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/duckdb/test/test_files/remote_duckdb.test b/duckdb/test/test_files/remote_duckdb.test index 32270449..343ee463 100644 --- a/duckdb/test/test_files/remote_duckdb.test +++ b/duckdb/test/test_files/remote_duckdb.test @@ -53,4 +53,4 @@ Attached database successfully. -STATEMENT ATTACH 's3://duckdb-blobs/databases/stations1.duckdb' as remoteTiny (dbtype duckdb, skip_unsupported_table=true); # Error type is determined by duckdb version. ---- error(regex) -^.+ Error: HTTP GET error reading 's3://duckdb-blobs/databases/stations1.duckdb' \(HTTP 403 Forbidden\)$ +^.+ Error: HTTP GET error reading 's3://duckdb-blobs/databases/stations1.duckdb' .HTTP 403 Forbidden.$ diff --git a/postgres/test/test_files/sql_query.test b/postgres/test/test_files/sql_query.test index 98f550de..9bd8967d 100644 --- a/postgres/test/test_files/sql_query.test +++ b/postgres/test/test_files/sql_query.test @@ -85,13 +85,36 @@ DEsWork|Dan ---- ok -STATEMENT CALL SQL_QUERY('tinysnb', 'create table tep(id int)') RETURN * ---- error(regex) -Binder Error: No fields returned by query "create table tep\(id int\)" - the query must be a SELECT statement that returns at least one column[\s\S]* +Binder exception: No fields returned by query "create table tep\(id int\)" - the query must be a SELECT statement that returns at least one column[\s\S]* -STATEMENT CALL SQL_QUERY('tinysnb', 'select organisation.name, person.fname as name from organisation, person') RETURN * ----- error(regex) -Binder Error: table "postgres_query" has duplicate column name "name"[\s\S]* +---- 24 +ABFsUni|Alice +ABFsUni|Hubert Blaine Wolfeschlegelsteinhausenbergerdorff +ABFsUni|Bob +ABFsUni|Carol +ABFsUni|Dan +ABFsUni|Elizabeth +ABFsUni|Farooq +ABFsUni|Greg +CsWork|Alice +CsWork|Hubert Blaine Wolfeschlegelsteinhausenbergerdorff +CsWork|Bob +CsWork|Carol +CsWork|Dan +CsWork|Elizabeth +CsWork|Farooq +CsWork|Greg +DEsWork|Alice +DEsWork|Hubert Blaine Wolfeschlegelsteinhausenbergerdorff +DEsWork|Bob +DEsWork|Carol +DEsWork|Dan +DEsWork|Elizabeth +DEsWork|Farooq +DEsWork|Greg -STATEMENT CALL SQL_QUERY('tinysnb', 'INSERT INTO PERSON VALUES(5)') RETURN * ---- error(regex) -Binder Error: No fields returned by query "INSERT INTO PERSON VALUES\(5\)" - the query must be a SELECT statement that returns at least one column[\s\S]* +Binder exception: No fields returned by query "INSERT INTO PERSON VALUES\(5\)" - the query must be a SELECT statement that returns at least one column[\s\S]* -STATEMENT CALL SQL_QUERY('tinysnb', 'SELECT * PERSON') RETURN * ---- error(regex) Binder Error: Failed to prepare query "SELECT \* PERSON": ERROR: syntax error at or near "PERSON"[\s\S]* From b019790bff15533cd480027c3fce92e71267775b Mon Sep 17 00:00:00 2001 From: Arun Sharma Date: Wed, 21 Oct 2026 13:09:14 -0700 Subject: [PATCH 12/14] fix(postgres): drop obsolete duplicate-column guard --- postgres/src/function/sql_query.cpp | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/postgres/src/function/sql_query.cpp b/postgres/src/function/sql_query.cpp index 2d445bfe..a4e8ce8f 100644 --- a/postgres/src/function/sql_query.cpp +++ b/postgres/src/function/sql_query.cpp @@ -10,9 +10,7 @@ #include "processor/execution_context.h" #include "storage/attached_postgres_database.h" #include "storage/postgres_storage.h" -#include "common/string_utils.h" #include -#include using namespace lbug::function; using namespace lbug::main; @@ -63,17 +61,6 @@ static std::unique_ptr bindFunc(const ClientContext* context, returnTypes.push_back( DuckDBTypeConverter::convertDuckDBType(queryResult->GetTypes()[i].ToString())); } - // DuckDB 2.0 no longer rejects duplicate output column names from - // postgres_query, so enforce the historical contract here. - std::unordered_set seenColumnNames; - for (auto& columnName : columnNamesInPG) { - auto lowerName = columnName; - StringUtils::toLower(lowerName); - if (!seenColumnNames.insert(lowerName).second) { - throw common::BinderException{std::format( - "table \"postgres_query\" has duplicate column name \"{}\"", columnName)}; - } - } auto returnColumnNames = TableFunction::extractYieldVariables(columnNamesInPG, input->yieldVariables); auto columns = input->binder->createVariables(returnColumnNames, returnTypes); From be36e9c2411747561e125f11269900bafd55a381 Mon Sep 17 00:00:00 2001 From: Arun Sharma Date: Wed, 21 Oct 2026 13:28:12 -0700 Subject: [PATCH 13/14] fix(tests): update numeric and S3 expectations for DuckDB 2.0.0 PG numeric-to-hugeint conversion rounds differently in 2.0.0 (closer to the true value); update the movies hugedata expectation. The S3 missing-file error now carries trailing hint text, so allow trailing content in the match. --- duckdb/test/test_files/remote_duckdb.test | 2 +- postgres/test/test_files/postgres.test | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/duckdb/test/test_files/remote_duckdb.test b/duckdb/test/test_files/remote_duckdb.test index 343ee463..452932e2 100644 --- a/duckdb/test/test_files/remote_duckdb.test +++ b/duckdb/test/test_files/remote_duckdb.test @@ -53,4 +53,4 @@ Attached database successfully. -STATEMENT ATTACH 's3://duckdb-blobs/databases/stations1.duckdb' as remoteTiny (dbtype duckdb, skip_unsupported_table=true); # Error type is determined by duckdb version. ---- error(regex) -^.+ Error: HTTP GET error reading 's3://duckdb-blobs/databases/stations1.duckdb' .HTTP 403 Forbidden.$ +^.+ Error: HTTP GET error reading 's3://duckdb-blobs/databases/stations1.duckdb' .HTTP 403 Forbidden.[\s\S]*$ diff --git a/postgres/test/test_files/postgres.test b/postgres/test/test_files/postgres.test index 9f3c7767..00dc460b 100644 --- a/postgres/test/test_files/postgres.test +++ b/postgres/test/test_files/postgres.test @@ -42,7 +42,7 @@ ---- 3 Roma|298|the movie is very interesting and funny|{rating: 1223.000000, stars: 100, "views": 10003, "release": 2011-02-11 16:44:22, release_ns: 2011-02-11 16:44:22, release_ms: 2011-02-11 16:44:22, release_sec: 2011-02-11 16:44:22, release_tz: 2011-02-11 08:44:22+00, film: 2013-02-22, u8: 1, u16: 15, u32: 200, u64: 4, hugedata: -15.000000}|pure ascii characters|[] Sóló cón tu párejâ|126|this is a very very good movie|{rating: 5.300000, stars: 2, "views": 152, "release": 2011-08-20 11:25:30, release_ns: 2011-08-20 11:25:30, release_ms: 2011-08-20 11:25:30, release_sec: 2011-08-20 11:25:30, release_tz: 2011-08-20 03:25:30+00, film: 2012-05-11, u8: 220, u16: 20, u32: 1, u64: 180, hugedata: 1844674407370955161600000000.000000}|\x5CxAA\x5CxABinteresting\x5Cx0B|[{"key": audience1, "value": 52},{"key": audience53, "value": 42}] -The 😂😃🧘🏻‍♂️🌍🌦️🍞🚗 movie|2544|the movie is very very good|{rating: 7.000000, stars: 10, "views": 982, "release": 2018-11-13 13:33:11, release_ns: 2018-11-13 13:33:11, release_ms: 2018-11-13 13:33:11, release_sec: 2018-11-13 13:33:11, release_tz: 2018-11-13 05:33:11+00, film: 2014-09-12, u8: 12, u16: 120, u32: 55, u64: 1, hugedata: -1844674407370954899456.000000}|\x5CxAB\x5CxCD|[{"key": audience1, "value": 33}] +The 😂😃🧘🏻‍♂️🌍🌦️🍞🚗 movie|2544|the movie is very very good|{rating: 7.000000, stars: 10, "views": 982, "release": 2018-11-13 13:33:11, release_ns: 2018-11-13 13:33:11, release_ms: 2018-11-13 13:33:11, release_sec: 2018-11-13 13:33:11, release_tz: 2018-11-13 05:33:11+00, film: 2014-09-12, u8: 12, u16: 120, u32: 55, u64: 1, hugedata: -1844674407370955161600.000000}|\x5CxAB\x5CxCD|[{"key": audience1, "value": 33}] -STATEMENT LOAD FROM tinysnb.movies where length > 2500 RETURN name; ---- 1 The 😂😃🧘🏻‍♂️🌍🌦️🍞🚗 movie From 773f620a94690609ba32237aca8049f6c748d486 Mon Sep 17 00:00:00 2001 From: Arun Sharma Date: Wed, 21 Oct 2026 14:19:33 -0700 Subject: [PATCH 14/14] ci: revert temporary ladybug PR-branch pin now that it merged --- .github/workflows/ci.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 35f7fd24..9bc65ff3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -121,14 +121,10 @@ jobs: steps: # ── Source checkouts ────────────────────────────────────────── - # TEMPORARY: track the ladybug DuckDB-2.0.0 PR branch until it merges - # to main (it carries the AttachedDatabase::executeQuery base-class - # fix this CI needs). Revert to default branch afterwards. - name: Checkout ladybug uses: actions/checkout@v4 with: repository: LadybugDB/ladybug - ref: fix-duckdb-osx-download fetch-depth: 1 path: ladybug