diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e83b7402..9bc65ff3 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" @@ -182,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" ;; @@ -282,10 +290,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 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/duckdb/src/catalog/duckdb_catalog.cpp b/duckdb/src/catalog/duckdb_catalog.cpp index 1eb7e910..ecaf28f5 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->GetNames(), *firstChunk, result->RowCount())) { + std::unique_ptr schemaResult; try { schemaResult = connector.executeQuery( std::format("SELECT * FROM {}.{}.{} LIMIT 0", quoteDuckDBIdentifier(catalogName), @@ -334,9 +338,12 @@ static bool getTableInfo(const DuckDBConnector& connector, const std::string& ta } throw; } - columnTypes.reserve(schemaResult->types.size()); - columnNames = schemaResult->names; - for (auto& type : schemaResult->types) { + columnTypes.reserve(schemaResult->GetTypes().size()); + columnNames.reserve(schemaResult->GetNames().size()); + for (auto& name : schemaResult->GetNames()) { + columnNames.push_back(name.GetIdentifierName()); + } + for (auto& type : schemaResult->GetTypes()) { try { columnTypes.push_back(DuckDBTypeConverter::convertDuckDBType(type.ToString())); } catch (common::BinderException&) { @@ -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..07a1debe 100644 --- a/duckdb/src/connector/duckdb_connector.cpp +++ b/duckdb/src/connector/duckdb_connector.cpp @@ -1,9 +1,14 @@ #include "connector/duckdb_connector.h" +#include +#include + +#include "common/exception/binder.h" + 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); @@ -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/connector/duckdb_result_converter.cpp b/duckdb/src/connector/duckdb_result_converter.cpp index 1415f6d0..4d2e5c03 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 = (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++) { @@ -31,7 +34,8 @@ 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 = + 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)); @@ -63,9 +67,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::list_entry_t*)duckdb::FlatVector::GetDataMutable(duckDBVector); for (auto i = 0u; i < numValuesToCopy; i++) { result.setNull(i, !validityMasks.RowIsValid(i)); if (!result.isNull(i)) { @@ -82,7 +86,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 +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 = (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]); @@ -106,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); } } 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..03dc7cdd 100644 --- a/duckdb/src/include/connector/duckdb_connector.h +++ b/duckdb/src/include/connector/duckdb_connector.h @@ -37,7 +37,13 @@ class DuckDBConnector { std::move(columnNames), *this); } - std::unique_ptr executeQuery(std::string query) const; + 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()) { 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..7344a025 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->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("{}.{}.{}", - 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->GetNames().size()); + for (auto& name : schemaResult->GetNames()) { + 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/duckdb/test/test_files/remote_duckdb.test b/duckdb/test/test_files/remote_duckdb.test index 6b2688ee..452932e2 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.[\s\S]*$ 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()); diff --git a/postgres/src/function/sql_query.cpp b/postgres/src/function/sql_query.cpp index 4dec6ff6..a4e8ce8f 100644 --- a/postgres/src/function/sql_query.cpp +++ b/postgres/src/function/sql_query.cpp @@ -50,13 +50,16 @@ 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; - 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); 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); } 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 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]*