From 9a7d8f108bb5c5aea7be2a530577dbd5b2b79124 Mon Sep 17 00:00:00 2001 From: Alexander Kuzmenkov Date: Tue, 11 Oct 2022 19:24:25 +0300 Subject: [PATCH] Fix SELECT count(*) This query only requires counting rows, so it is optimized into selecting a rowid column from the underlying table. In case of the Postgres table, this gets deparsed into selecting the first column. If it happens to be a text column, we then try to insert it into the rowid output vector which is int, and fail with an assertion because of type mismatch. Instead of this, deparse this case into SELECT NULL FROM postgres_table. This is enough for counting the rows. --- postgres_scanner.cpp | 24 ++++++++++++++++++++---- test/postgres_scanner/count_star.test | 13 +++++++++++++ 2 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 test/postgres_scanner/count_star.test diff --git a/postgres_scanner.cpp b/postgres_scanner.cpp index 27b5b335a..c4d773a36 100644 --- a/postgres_scanner.cpp +++ b/postgres_scanner.cpp @@ -335,15 +335,31 @@ static void PostgresInitInternal(ClientContext &context, const PostgresBindData auto bind_data = (const PostgresBindData *)bind_data_p; - // we just return the first column for ROW_ID + // Queries like SELECT count(*) do not require actually returning the columns from the + // Postgres table, but only counting the row. In this case, we will be asked to return + // the 'rowid' special column here. It must be the only selected column. The corresponding + // deparsed query will be 'SELECT NULL'..Note that the user is not allowed to explicitly + // request the 'rowid' special column from a Postgres table in a SQL query. + bool have_rowid = false; for (idx_t i = 0; i < lstate.column_ids.size(); i++) { if (lstate.column_ids[i] == (column_t)-1) { - lstate.column_ids[i] = 0; + have_rowid = true; + break; } } - auto col_names = StringUtil::Join(lstate.column_ids.data(), lstate.column_ids.size(), ", ", - [&](const idx_t column_id) { return '"' + bind_data->names[column_id] + '"'; }); + if (have_rowid && lstate.column_ids.size() > 1) { + throw InternalException("Cannot return ROW_ID from Postgres table"); + } + + std::string col_names; + if (have_rowid) { + // We are only counting rows, not interested in the actual values of the columns. + col_names = "NULL"; + } else { + col_names = StringUtil::Join(lstate.column_ids.data(), lstate.column_ids.size(), ", ", + [&](const idx_t column_id) { return '"' + bind_data->names[column_id] + '"'; }); + } string filter_string; if (lstate.filters && !lstate.filters->filters.empty()) { diff --git a/test/postgres_scanner/count_star.test b/test/postgres_scanner/count_star.test new file mode 100644 index 000000000..998ef2f14 --- /dev/null +++ b/test/postgres_scanner/count_star.test @@ -0,0 +1,13 @@ +statement ok +LOAD 'build/release/extension/postgres_scanner/postgres_scanner.duckdb_extension'; + +statement ok +pragma enable_verification + +statement ok +CALL postgres_attach('dbname=postgresscanner'); + +query I +select count(*) from cars; +---- +4 \ No newline at end of file