Skip to content

Commit 929bf00

Browse files
committed
Avoid modifying the path that is shown in duckdb_databases with the secret contents
1 parent ef6c95e commit 929bf00

File tree

5 files changed

+92
-78
lines changed

5 files changed

+92
-78
lines changed

src/include/storage/postgres_catalog.hpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,11 @@ class PostgresSchemaEntry;
2020

2121
class PostgresCatalog : public Catalog {
2222
public:
23-
explicit PostgresCatalog(AttachedDatabase &db_p, const string &path, AccessMode access_mode, string schema_to_load);
23+
explicit PostgresCatalog(AttachedDatabase &db_p, string connection_string, string attach_path, AccessMode access_mode, string schema_to_load);
2424
~PostgresCatalog();
2525

26-
string path;
26+
string connection_string;
27+
string attach_path;
2728
AccessMode access_mode;
2829

2930
public:
@@ -32,6 +33,8 @@ class PostgresCatalog : public Catalog {
3233
return "postgres";
3334
}
3435

36+
static string GetConnectionString(ClientContext &context, const string &attach_path, string secret_name);
37+
3538
optional_ptr<CatalogEntry> CreateSchema(CatalogTransaction transaction, CreateSchemaInfo &info) override;
3639

3740
void ScanSchemas(ClientContext &context, std::function<void(SchemaCatalogEntry &)> callback) override;

src/postgres_storage.cpp

Lines changed: 3 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -4,58 +4,13 @@
44
#include "storage/postgres_catalog.hpp"
55
#include "duckdb/parser/parsed_data/attach_info.hpp"
66
#include "storage/postgres_transaction_manager.hpp"
7-
#include "duckdb/main/secret/secret_manager.hpp"
87

98
namespace duckdb {
109

11-
string EscapeConnectionString(const string &input) {
12-
string result = "'";
13-
for (auto c : input) {
14-
if (c == '\\') {
15-
result += "\\\\";
16-
} else if (c == '\'') {
17-
result += "\\'";
18-
} else {
19-
result += c;
20-
}
21-
}
22-
result += "'";
23-
return result;
24-
}
25-
26-
string AddConnectionOption(const KeyValueSecret &kv_secret, const string &name) {
27-
Value input_val = kv_secret.TryGetValue(name);
28-
if (input_val.IsNull()) {
29-
// not provided
30-
return string();
31-
}
32-
string result;
33-
result += name;
34-
result += "=";
35-
result += EscapeConnectionString(input_val.ToString());
36-
result += " ";
37-
return result;
38-
}
39-
40-
unique_ptr<SecretEntry> GetSecret(ClientContext &context, const string &secret_name) {
41-
auto &secret_manager = SecretManager::Get(context);
42-
auto transaction = CatalogTransaction::GetSystemCatalogTransaction(context);
43-
// FIXME: this should be adjusted once the `GetSecretByName` API supports this use case
44-
auto secret_entry = secret_manager.GetSecretByName(transaction, secret_name, "memory");
45-
if (secret_entry) {
46-
return secret_entry;
47-
}
48-
secret_entry = secret_manager.GetSecretByName(transaction, secret_name, "local_file");
49-
if (secret_entry) {
50-
return secret_entry;
51-
}
52-
return nullptr;
53-
}
54-
5510
static unique_ptr<Catalog> PostgresAttach(StorageExtensionInfo *storage_info, ClientContext &context,
5611
AttachedDatabase &db, const string &name, AttachInfo &info,
5712
AccessMode access_mode) {
58-
string connection_string = info.path;
13+
string attach_path = info.path;
5914

6015
string secret_name;
6116
string schema_to_load;
@@ -71,32 +26,8 @@ static unique_ptr<Catalog> PostgresAttach(StorageExtensionInfo *storage_info, Cl
7126
throw BinderException("Unrecognized option for Postgres attach: %s", entry.first);
7227
}
7328
}
74-
75-
// if no secret is specified we default to the unnamed postgres secret, if it exists
76-
bool explicit_secret = !secret_name.empty();
77-
if (!explicit_secret) {
78-
// look up settings from the default unnamed postgres secret if none is provided
79-
secret_name = "__default_postgres";
80-
}
81-
82-
auto secret_entry = GetSecret(context, secret_name);
83-
if (secret_entry) {
84-
// secret found - read data
85-
const auto &kv_secret = dynamic_cast<const KeyValueSecret &>(*secret_entry->secret);
86-
string new_connection_info;
87-
88-
new_connection_info += AddConnectionOption(kv_secret, "user");
89-
new_connection_info += AddConnectionOption(kv_secret, "password");
90-
new_connection_info += AddConnectionOption(kv_secret, "host");
91-
new_connection_info += AddConnectionOption(kv_secret, "port");
92-
new_connection_info += AddConnectionOption(kv_secret, "dbname");
93-
94-
connection_string = new_connection_info + connection_string;
95-
} else if (explicit_secret) {
96-
// secret not found and one was explicitly provided - throw an error
97-
throw BinderException("Secret with name \"%s\" not found", secret_name);
98-
}
99-
return make_uniq<PostgresCatalog>(db, connection_string, access_mode, std::move(schema_to_load));
29+
auto connection_string = PostgresCatalog::GetConnectionString(context, attach_path, secret_name);
30+
return make_uniq<PostgresCatalog>(db, std::move(connection_string), std::move(attach_path), access_mode, std::move(schema_to_load));
10031
}
10132

10233
static unique_ptr<TransactionManager> PostgresCreateTransactionManager(StorageExtensionInfo *storage_info,

src/storage/postgres_catalog.cpp

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@
66
#include "duckdb/parser/parsed_data/drop_info.hpp"
77
#include "duckdb/parser/parsed_data/create_schema_info.hpp"
88
#include "duckdb/main/attached_database.hpp"
9+
#include "duckdb/main/secret/secret_manager.hpp"
910

1011
namespace duckdb {
1112

12-
PostgresCatalog::PostgresCatalog(AttachedDatabase &db_p, const string &path, AccessMode access_mode,
13+
PostgresCatalog::PostgresCatalog(AttachedDatabase &db_p, string connection_string_p, string attach_path_p, AccessMode access_mode,
1314
string schema_to_load)
14-
: Catalog(db_p), path(path), access_mode(access_mode), schemas(*this, schema_to_load), connection_pool(*this),
15+
: Catalog(db_p), connection_string(std::move(connection_string_p)), attach_path(std::move(attach_path_p)), access_mode(access_mode), schemas(*this, schema_to_load), connection_pool(*this),
1516
default_schema(schema_to_load) {
1617
if (default_schema.empty()) {
1718
default_schema = "public";
@@ -26,6 +27,80 @@ PostgresCatalog::PostgresCatalog(AttachedDatabase &db_p, const string &path, Acc
2627
this->version = connection.GetConnection().GetPostgresVersion();
2728
}
2829

30+
string EscapeConnectionString(const string &input) {
31+
string result = "'";
32+
for (auto c : input) {
33+
if (c == '\\') {
34+
result += "\\\\";
35+
} else if (c == '\'') {
36+
result += "\\'";
37+
} else {
38+
result += c;
39+
}
40+
}
41+
result += "'";
42+
return result;
43+
}
44+
45+
string AddConnectionOption(const KeyValueSecret &kv_secret, const string &name) {
46+
Value input_val = kv_secret.TryGetValue(name);
47+
if (input_val.IsNull()) {
48+
// not provided
49+
return string();
50+
}
51+
string result;
52+
result += name;
53+
result += "=";
54+
result += EscapeConnectionString(input_val.ToString());
55+
result += " ";
56+
return result;
57+
}
58+
59+
unique_ptr<SecretEntry> GetSecret(ClientContext &context, const string &secret_name) {
60+
auto &secret_manager = SecretManager::Get(context);
61+
auto transaction = CatalogTransaction::GetSystemCatalogTransaction(context);
62+
// FIXME: this should be adjusted once the `GetSecretByName` API supports this use case
63+
auto secret_entry = secret_manager.GetSecretByName(transaction, secret_name, "memory");
64+
if (secret_entry) {
65+
return secret_entry;
66+
}
67+
secret_entry = secret_manager.GetSecretByName(transaction, secret_name, "local_file");
68+
if (secret_entry) {
69+
return secret_entry;
70+
}
71+
return nullptr;
72+
}
73+
74+
string PostgresCatalog::GetConnectionString(ClientContext &context, const string &attach_path, string secret_name) {
75+
// if no secret is specified we default to the unnamed postgres secret, if it exists
76+
string connection_string = attach_path;
77+
bool explicit_secret = !secret_name.empty();
78+
if (!explicit_secret) {
79+
// look up settings from the default unnamed postgres secret if none is provided
80+
secret_name = "__default_postgres";
81+
}
82+
83+
auto secret_entry = GetSecret(context, secret_name);
84+
if (secret_entry) {
85+
// secret found - read data
86+
const auto &kv_secret = dynamic_cast<const KeyValueSecret &>(*secret_entry->secret);
87+
string new_connection_info;
88+
89+
new_connection_info += AddConnectionOption(kv_secret, "user");
90+
new_connection_info += AddConnectionOption(kv_secret, "password");
91+
new_connection_info += AddConnectionOption(kv_secret, "host");
92+
new_connection_info += AddConnectionOption(kv_secret, "port");
93+
new_connection_info += AddConnectionOption(kv_secret, "dbname");
94+
95+
connection_string = new_connection_info + connection_string;
96+
} else if (explicit_secret) {
97+
// secret not found and one was explicitly provided - throw an error
98+
throw BinderException("Secret with name \"%s\" not found", secret_name);
99+
}
100+
return connection_string;
101+
}
102+
103+
29104
PostgresCatalog::~PostgresCatalog() = default;
30105

31106
void PostgresCatalog::Initialize(bool load_builtin) {
@@ -85,7 +160,7 @@ bool PostgresCatalog::InMemory() {
85160
}
86161

87162
string PostgresCatalog::GetDBPath() {
88-
return path;
163+
return attach_path;
89164
}
90165

91166
DatabaseSize PostgresCatalog::GetDatabaseSize(ClientContext &context) {

src/storage/postgres_connection_pool.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ PostgresPoolConnection PostgresConnectionPool::GetConnectionInternal() {
5454
}
5555

5656
// no cached connections left but there is space to open a new one - open it
57-
return PostgresPoolConnection(this, PostgresConnection::Open(postgres_catalog.path));
57+
return PostgresPoolConnection(this, PostgresConnection::Open(postgres_catalog.connection_string));
5858
}
5959

6060
PostgresPoolConnection PostgresConnectionPool::ForceGetConnection() {

test/sql/storage/attach_secret.test

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ CREATE SECRET postgres_db (
3535
statement ok
3636
ATTACH '' AS secret_attach (TYPE POSTGRES, SECRET postgres_db)
3737

38+
query I
39+
SELECT path FROM duckdb_databases() WHERE database_name='secret_attach'
40+
----
41+
(empty)
42+
3843
statement ok
3944
DETACH secret_attach
4045

0 commit comments

Comments
 (0)