diff --git a/src/include/postgres_scanner.hpp b/src/include/postgres_scanner.hpp index 3e0162cdc..3bf4bd5ad 100644 --- a/src/include/postgres_scanner.hpp +++ b/src/include/postgres_scanner.hpp @@ -84,7 +84,7 @@ class PostgresScanFunction : public TableFunction { PostgresScanFunction(); static void PrepareBind(PostgresVersion version, ClientContext &context, PostgresBindData &bind, - idx_t approx_num_pages); + int64_t approx_num_pages); }; class PostgresScanFunctionFilterPushdown : public TableFunction { diff --git a/src/include/storage/postgres_table_entry.hpp b/src/include/storage/postgres_table_entry.hpp index 529c23408..02da22ff9 100644 --- a/src/include/storage/postgres_table_entry.hpp +++ b/src/include/storage/postgres_table_entry.hpp @@ -35,7 +35,7 @@ struct PostgresTableInfo { unique_ptr create_info; vector postgres_types; vector postgres_names; - idx_t approx_num_pages = 0; + int64_t approx_num_pages = 0; }; class PostgresTableEntry : public TableCatalogEntry { @@ -64,7 +64,7 @@ class PostgresTableEntry : public TableCatalogEntry { //! We would in this case remap them to "ID" and "id:1", while postgres_names store the original names vector postgres_names; //! The approximate number of pages a table consumes in Postgres - idx_t approx_num_pages; + int64_t approx_num_pages; }; } // namespace duckdb diff --git a/src/postgres_scanner.cpp b/src/postgres_scanner.cpp index ba0cd6e79..73023cfeb 100644 --- a/src/postgres_scanner.cpp +++ b/src/postgres_scanner.cpp @@ -107,7 +107,7 @@ static void PostgresGetSnapshot(ClientContext &context, PostgresVersion version, } void PostgresScanFunction::PrepareBind(PostgresVersion version, ClientContext &context, PostgresBindData &bind_data, - idx_t approx_num_pages) { + int64_t approx_num_pages) { Value pages_per_task; if (context.TryGetCurrentSetting("pg_pages_per_task", pages_per_task)) { bind_data.pages_per_task = UBigIntValue::Get(pages_per_task); @@ -130,10 +130,14 @@ void PostgresScanFunction::PrepareBind(PostgresVersion version, ClientContext &c // see https://github.com/duckdb/postgres_scanner/issues/186 use_ctid_scan = false; } + if (approx_num_pages < 0) { + // negative relpages (e.g. partitioned tables) cannot use ctid scan + use_ctid_scan = false; + } if (!use_ctid_scan) { approx_num_pages = 0; } - bind_data.SetTablePages(approx_num_pages); + bind_data.SetTablePages(static_cast(approx_num_pages)); bind_data.version = version; if (version.type_v == PostgresInstanceType::REDSHIFT) { bind_data.use_text_protocol = true; diff --git a/src/storage/postgres_table_set.cpp b/src/storage/postgres_table_set.cpp index 321d62e22..8df2c7b82 100644 --- a/src/storage/postgres_table_set.cpp +++ b/src/storage/postgres_table_set.cpp @@ -128,9 +128,8 @@ void PostgresTableSet::CreateEntries(PostgresTransaction &transaction, PostgresR if (info) { tables.push_back(std::move(info)); } - auto approx_num_pages = result.IsNull(row, 2) ? 0 : result.GetInt64(row, 2); info = make_uniq(schema, table_name); - info->approx_num_pages = approx_num_pages; + info->approx_num_pages = result.IsNull(row, 2) ? 0 : result.GetInt64(row, 2); } AddColumnOrConstraint(&transaction, &schema, result, row, *info); } @@ -169,7 +168,7 @@ unique_ptr PostgresTableSet::GetTableInfo(PostgresTransaction for (idx_t row = 0; row < rows; row++) { AddColumnOrConstraint(&transaction, &schema, *result, row, *table_info); } - table_info->approx_num_pages = result->GetInt64(0, 2); + table_info->approx_num_pages = result->IsNull(0, 2) ? 0 : result->GetInt64(0, 2); return table_info; } @@ -185,7 +184,7 @@ unique_ptr PostgresTableSet::GetTableInfo(ClientContext &cont for (idx_t row = 0; row < rows; row++) { AddColumnOrConstraint(nullptr, nullptr, *result, row, *table_info); } - table_info->approx_num_pages = result->GetInt64(0, 2); + table_info->approx_num_pages = result->IsNull(0, 2) ? 0 : result->GetInt64(0, 2); return table_info; } diff --git a/test/other.sql b/test/other.sql index 5406c854a..d25f5e1aa 100644 --- a/test/other.sql +++ b/test/other.sql @@ -168,3 +168,10 @@ create table tbl_with_unique_constraints(pk int unique, c1 int not null, c2 int, create schema main; create table main.main_tbl(i int); insert into main.main_tbl values (42), (NULL); + +-- partitioned table (parent has relpages=-1 after analyze) +create table part_tbl (id int, val text) partition by range (id); +create table part_tbl_1 partition of part_tbl for values from (1) to (100); +create table part_tbl_2 partition of part_tbl for values from (100) to (200); +insert into part_tbl values (1,'a'),(50,'b'),(100,'c'),(150,'d'); +analyze part_tbl; diff --git a/test/sql/scanner/partitioned_table.test b/test/sql/scanner/partitioned_table.test new file mode 100644 index 000000000..f25b56860 --- /dev/null +++ b/test/sql/scanner/partitioned_table.test @@ -0,0 +1,23 @@ +# name: test/sql/scanner/partitioned_table.test +# description: Scanning a partitioned table fallbacks to 0 estimate +# group: [scanner] + +require postgres_scanner + +require-env POSTGRES_TEST_DATABASE_AVAILABLE + +statement ok +ATTACH 'dbname=postgresscanner' AS s (TYPE POSTGRES) + +query IT +SELECT * FROM s.public.part_tbl ORDER BY id +---- +1 a +50 b +100 c +150 d + +query II +EXPLAIN SELECT * FROM s.public.part_tbl +---- +physical_plan :.*~0 rows.*