Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/include/postgres_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ struct PostgresCopyState {

enum class PostgresIsolationLevel { READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE };

enum class PostgresUseCtidScan : uint8_t { DEFAULT, ENABLED, DISABLED };

class PostgresUtils {
public:
static PGconn *PGConnect(const string &dsn, const string &attach_path);
Expand Down
1 change: 1 addition & 0 deletions src/include/storage/postgres_catalog.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class PostgresCatalog : public Catalog {
string attach_path;
AccessMode access_mode;
PostgresIsolationLevel isolation_level;
PostgresUseCtidScan use_ctid_scan = PostgresUseCtidScan::DEFAULT;

public:
void Initialize(bool load_builtin) override;
Expand Down
4 changes: 4 additions & 0 deletions src/postgres_scanner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ void PostgresScanFunction::PrepareBind(PostgresVersion version, ClientContext &c
if (context.TryGetCurrentSetting("pg_use_ctid_scan", pg_use_ctid_scan)) {
use_ctid_scan = BooleanValue::Get(pg_use_ctid_scan);
}
auto pg_catalog = bind_data.GetCatalog();
if (pg_catalog && pg_catalog->use_ctid_scan != PostgresUseCtidScan::DEFAULT) {
use_ctid_scan = pg_catalog->use_ctid_scan == PostgresUseCtidScan::ENABLED;
}
if (bind_data.use_text_protocol) {
// ctid scan is only supported for binary copy
use_ctid_scan = false;
Expand Down
13 changes: 10 additions & 3 deletions src/postgres_storage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ static unique_ptr<Catalog> PostgresAttach(optional_ptr<StorageExtensionInfo> sto
PostgresIsolationLevel isolation_level = PostgresIsolationLevel::REPEATABLE_READ;
string secret_storage_table_name;
bool secret_storage_table_specified_explicitly = false;
PostgresUseCtidScan use_ctid_scan = PostgresUseCtidScan::DEFAULT;
for (auto &entry : attach_options.options) {
auto lower_name = StringUtil::Lower(entry.first);
if (lower_name == "secret") {
Expand All @@ -45,15 +46,21 @@ static unique_ptr<Catalog> PostgresAttach(optional_ptr<StorageExtensionInfo> sto
} else if (lower_name == "secret_storage_table") {
secret_storage_table_name = entry.second.ToString();
secret_storage_table_specified_explicitly = true;
} else if (lower_name == "use_ctid_scan") {
use_ctid_scan = BooleanValue::Get(entry.second.DefaultCastAs(LogicalType::BOOLEAN))
? PostgresUseCtidScan::ENABLED
: PostgresUseCtidScan::DISABLED;
} else {
throw BinderException("Unrecognized option for Postgres attach: %s", entry.first);
}
}
SecretStorageTable secret_storage_table(std::move(secret_storage_table_name),
secret_storage_table_specified_explicitly);
return make_uniq<PostgresCatalog>(context, db, std::move(attach_path), attach_options.access_mode,
std::move(schema_to_load), isolation_level, secret_name,
std::move(secret_storage_table));
auto catalog = make_uniq<PostgresCatalog>(context, db, std::move(attach_path), attach_options.access_mode,
std::move(schema_to_load), isolation_level, secret_name,
std::move(secret_storage_table));
catalog->use_ctid_scan = use_ctid_scan;
return catalog;
}

static unique_ptr<TransactionManager> PostgresCreateTransactionManager(optional_ptr<StorageExtensionInfo> storage_info,
Expand Down
73 changes: 73 additions & 0 deletions test/sql/storage/attach_use_ctid_scan.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# name: test/sql/storage/attach_use_ctid_scan.test
# description: Test the per-attach use_ctid_scan option overriding the global pg_use_ctid_scan setting
# group: [storage]

require postgres_scanner

require-env POSTGRES_TEST_DATABASE_AVAILABLE

statement ok
PRAGMA enable_verification

# per-attach option disables ctid scan even though the global setting defaults to true
statement ok
SET pg_use_ctid_scan=true;

statement ok
ATTACH 'dbname=postgresscanner' AS s (TYPE POSTGRES, USE_CTID_SCAN false)

query I
SELECT * FROM s.pg_giant_numeric
----
0.0
1.0000000000000002e+32
-1.234567891234568e+26

statement ok
DETACH s

# per-attach option enables ctid scan even though the global setting is false (override works both ways)
statement ok
SET pg_use_ctid_scan=false;

statement ok
ATTACH 'dbname=postgresscanner' AS s (TYPE POSTGRES, USE_CTID_SCAN true)

query I
SELECT * FROM s.pg_giant_numeric
----
0.0
1.0000000000000002e+32
-1.234567891234568e+26

statement ok
DETACH s

# when omitted, behavior follows the global setting (here: false) and still produces correct results
statement ok
ATTACH 'dbname=postgresscanner' AS s (TYPE POSTGRES)

query I
SELECT * FROM s.pg_giant_numeric
----
0.0
1.0000000000000002e+32
-1.234567891234568e+26

statement ok
DETACH s

statement ok
SET pg_use_ctid_scan=true;

# boolean-typed values are accepted
statement ok
ATTACH 'dbname=postgresscanner' AS s (TYPE POSTGRES, USE_CTID_SCAN 1)

statement ok
DETACH s

# a non-boolean value raises a clear error
statement error
ATTACH 'dbname=postgresscanner' AS s (TYPE POSTGRES, USE_CTID_SCAN 'not_a_bool')
----
Loading