Skip to content

Commit 57c8aa0

Browse files
committed
Merge remote-tracking branch 'origin' into update-to-duckdb-v1.3.0
2 parents e8e94a3 + aaedec1 commit 57c8aa0

30 files changed

+556
-207
lines changed

docs/extensions.md

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,24 +31,32 @@ Installing an extension causes it to be loaded and installed globally for any co
3131
SELECT duckdb.install_extension('iceberg');
3232
-- view currently installed extensions
3333
SELECT * FROM duckdb.extensions;
34-
-- disable or enable an extension
35-
UPDATE duckdb.extensions SET enabled = (false|true) WHERE name = 'iceberg';
36-
-- remove an extension
37-
DELETE FROM duckdb.extensions WHERE name = 'iceberg';
34+
-- Change an extension to stop being automatically loaded in new connections
35+
SELECT duckdb.auotoload_extension('iceberg', false);
36+
-- For such extensions, you can still load them manually in a session
37+
SELECT duckdb.load_extension('iceberg');
3838
-- You can also install community extensions
3939
SELECT duckdb.install_extension('prql', 'community');
4040
```
4141

42-
There is currently no practical difference between a disabled and uninstalled extension.
43-
4442
## Supported Extensions
4543

4644
You can install any extension DuckDB extension, but you might run into various issues when trying to use them from Postgres. Often you should be able to work around such issues by using `duckdb.query` or `duckdb.raw_query`. For some extensions pg_duckdb has added dedicated support to Postgres. These extensions are listed below.
4745

46+
### `azure`
47+
48+
Allows reading files from Azure Blob Storage by using `az://...` filepaths.
49+
4850
### `iceberg`
4951

5052
Iceberg support adds functions to read Iceberg tables and metadata. For a list of iceberg functions, see [pg_duckdb Functions](functions.md).
5153

5254
### `delta`
5355

5456
Delta support adds the ability to read Delta Lake files via [delta_scan](functions.md#delta_scan).
57+
58+
## Security considerations
59+
60+
By default execution `duckdb.install_extension` and `duckdb.autoload_extension` is only allowed for superusers. This is to prevent users from installing extensions that may have security implications or that may interfere with the database's operation.
61+
62+
That means that users can only use extensions that DuckDB has marked as "auto-installable". If you want to restrict the use of those extensions as well to a specific list of allowed extensions, you can do so by setting the [`duckdb.autoinstall_known_extensions`](settings.md#duckdbautoload_known_extensions) to `false`. This will prevent users from automatically install any known extensions. Note that this requires that any of the extensions you **do** want to allow are already installed by a superuser.

docs/functions.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,8 +268,6 @@ permissions:
268268

269269
```sql
270270
GRANT ALL ON FUNCTION duckdb.install_extension(TEXT, TEXT) TO my_admin;
271-
GRANT ALL ON TABLE duckdb.extensions TO my_admin;
272-
GRANT ALL ON SEQUENCE duckdb.extensions_table_seq TO my_admin;
273271
```
274272

275273
##### Required Arguments

include/pgduckdb/pg/declarations.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,6 @@ struct ObjectAddress;
8686
struct PlanState;
8787

8888
struct Plan;
89+
90+
typedef struct FunctionCallInfoBaseData *FunctionCallInfo;
8991
}

include/pgduckdb/pg/functions.hpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#pragma once
2+
3+
#include "string"
4+
#include "pgduckdb/pg/declarations.hpp"
5+
6+
namespace pgduckdb::pg {
7+
8+
std::string GetArgString(FunctionCallInfo info, int argno);
9+
Datum GetArgDatum(FunctionCallInfo info, int argno);
10+
std::string DatumToString(Datum datum);
11+
12+
} // namespace pgduckdb::pg

include/pgduckdb/pg/relations.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,8 @@ const char *GetRelationName(Relation rel);
3737

3838
Oid GetOid(Form_pg_class rel);
3939

40+
namespace pg {
41+
Form_pg_attribute GetAttributeByName(TupleDesc tupdesc, const char *colname);
42+
}
43+
4044
} // namespace pgduckdb

include/pgduckdb/pgduckdb_duckdb.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ class DuckDBManager {
6565
void LoadSecrets(duckdb::ClientContext &);
6666
void DropSecrets(duckdb::ClientContext &);
6767
void LoadExtensions(duckdb::ClientContext &);
68+
void InstallExtensions(duckdb::ClientContext &);
6869
void LoadFunctions(duckdb::ClientContext &);
6970
void RefreshConnectionState(duckdb::ClientContext &);
7071

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#pragma once
2+
3+
#include <string>
4+
#include <vector>
5+
6+
namespace pgduckdb {
7+
8+
/* constants for duckdb.extensions */
9+
#define Natts_duckdb_extension 3
10+
#define Anum_duckdb_extension_name 1
11+
#define Anum_duckdb_extension_autoload 2
12+
#define Anum_duckdb_extension_repository 3
13+
14+
typedef struct DuckdbExension {
15+
std::string name;
16+
bool autoload;
17+
std::string repository;
18+
} DuckdbExtension;
19+
20+
extern std::vector<DuckdbExtension> ReadDuckdbExtensions();
21+
22+
namespace ddb {
23+
24+
extern std::string LoadExtensionQuery(const std::string &extension_name);
25+
extern std::string InstallExtensionQuery(const std::string &extension_name, const std::string &repository);
26+
27+
} // namespace ddb
28+
} // namespace pgduckdb

include/pgduckdb/pgduckdb_options.hpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,18 @@
66
namespace pgduckdb {
77

88
/* constants for duckdb.extensions */
9-
#define Natts_duckdb_extension 2
10-
#define Anum_duckdb_extension_name 1
11-
#define Anum_duckdb_extension_enable 2
9+
#define Natts_duckdb_extension 3
10+
#define Anum_duckdb_extension_name 1
11+
#define Anum_duckdb_extension_autoload 2
12+
#define Anum_duckdb_extension_repository 3
1213

1314
typedef struct DuckdbExension {
1415
std::string name;
15-
bool enabled;
16+
bool autoload;
17+
std::string repository;
1618
} DuckdbExtension;
1719

1820
extern std::vector<DuckdbExtension> ReadDuckdbExtensions();
21+
extern std::string DuckdbInstallExtensionQuery(const std::string &extension_name, const std::string &repository);
1922

2023
} // namespace pgduckdb

sql/pg_duckdb--0.3.0--1.0.0.sql

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,27 @@ LANGUAGE C;
6464

6565
DROP FUNCTION duckdb.install_extension(TEXT);
6666
CREATE FUNCTION duckdb.install_extension(extension_name TEXT, source TEXT DEFAULT 'core') RETURNS void
67+
SECURITY DEFINER
6768
SET search_path = pg_catalog, pg_temp
69+
SET duckdb.force_execution = false
6870
LANGUAGE C AS 'MODULE_PATHNAME', 'install_extension';
6971
REVOKE ALL ON FUNCTION duckdb.install_extension(TEXT, TEXT) FROM PUBLIC;
7072

73+
CREATE FUNCTION duckdb.autoload_extension(extension_name TEXT, autoload BOOLEAN DEFAULT TRUE) RETURNS void
74+
SECURITY DEFINER
75+
SET search_path = pg_catalog, pg_temp
76+
SET duckdb.force_execution = false
77+
LANGUAGE C AS 'MODULE_PATHNAME', 'duckdb_autoload_extension';
78+
REVOKE ALL ON FUNCTION duckdb.autoload_extension(TEXT, BOOLEAN) FROM PUBLIC;
79+
80+
CREATE FUNCTION duckdb.load_extension(extension_name TEXT) RETURNS void
81+
SET search_path = pg_catalog, pg_temp
82+
SET duckdb.force_execution = false
83+
LANGUAGE C AS 'MODULE_PATHNAME', 'duckdb_load_extension';
84+
-- load_extension is allowed for all users, because it is trivial for users run
85+
-- a raw query that does the same. This function only exists for completeness
86+
-- and convenience.
87+
7188
-- The min aggregate was somehow missing from the list of aggregates in 0.3.0
7289
CREATE AGGREGATE @extschema@.min(duckdb.unresolved_type) (
7390
SFUNC = duckdb_unresolved_type_state_trans,
@@ -725,4 +742,6 @@ RETURNS TEXT
725742
SET search_path = pg_catalog, pg_temp
726743
LANGUAGE C AS 'MODULE_PATHNAME', 'pgduckdb_create_azure_secret';
727744

728-
745+
ALTER TABLE duckdb.extensions ADD COLUMN repository TEXT NOT NULL DEFAULT 'core';
746+
ALTER TABLE duckdb.extensions RENAME COLUMN enabled TO autoload;
747+
ALTER TABLE duckdb.extensions ALTER COLUMN autoload SET NOT NULL;

src/pg/functions.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#include "pgduckdb/pg/functions.hpp"
2+
#include "duckdb/common/exception.hpp"
3+
4+
#include "pgduckdb/pgduckdb_utils.hpp"
5+
6+
extern "C" {
7+
#include "postgres.h"
8+
#include "fmgr.h"
9+
#include "utils/builtins.h"
10+
}
11+
12+
namespace pgduckdb::pg {
13+
14+
std::string
15+
GetArgString(PG_FUNCTION_ARGS, int argno) {
16+
if (PG_NARGS() <= argno) {
17+
throw duckdb::InvalidInputException("argument %d is required", argno);
18+
}
19+
20+
if (PG_ARGISNULL(argno)) {
21+
throw duckdb::InvalidInputException("argument %d cannot be NULL", argno);
22+
}
23+
24+
return DatumToString(PG_GETARG_DATUM(argno));
25+
}
26+
27+
Datum
28+
GetArgDatum(PG_FUNCTION_ARGS, int argno) {
29+
if (PG_NARGS() <= argno) {
30+
throw duckdb::InvalidInputException("argument %d is required", argno);
31+
}
32+
33+
if (PG_ARGISNULL(argno)) {
34+
throw duckdb::InvalidInputException("argument %d cannot be NULL", argno);
35+
}
36+
37+
return PG_GETARG_DATUM(argno);
38+
}
39+
40+
namespace {
41+
char *
42+
DatumToCstring(Datum datum) {
43+
return text_to_cstring(DatumGetTextPP(datum));
44+
}
45+
} // namespace
46+
47+
std::string
48+
DatumToString(Datum datum) {
49+
return std::string(PostgresFunctionGuard(DatumToCstring, datum));
50+
}
51+
52+
} // namespace pgduckdb::pg

src/pg/relations.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,4 +177,19 @@ GetOid(Form_pg_class rel) {
177177
return rel->oid;
178178
}
179179

180+
namespace pg {
181+
182+
Form_pg_attribute
183+
GetAttributeByName(TupleDesc tupdesc, const char *colname) {
184+
for (int i = 0; i < tupdesc->natts; i++) {
185+
Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
186+
if (strcmp(NameStr(attr->attname), colname) == 0) {
187+
return attr;
188+
}
189+
}
190+
return NULL; // Return NULL if the column name is not found
191+
}
192+
193+
} // namespace pg
194+
180195
} // namespace pgduckdb

src/pgduckdb_duckdb.cpp

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
#include "pgduckdb/pgduckdb_fdw.hpp"
1616
#include "pgduckdb/pgduckdb_guc.hpp"
1717
#include "pgduckdb/pgduckdb_metadata_cache.hpp"
18-
#include "pgduckdb/pgduckdb_options.hpp"
18+
#include "pgduckdb/pgduckdb_extensions.hpp"
1919
#include "pgduckdb/pgduckdb_secrets_helper.hpp"
2020
#include "pgduckdb/pgduckdb_userdata_cache.hpp"
2121
#include "pgduckdb/pgduckdb_utils.hpp"
@@ -31,7 +31,7 @@ extern "C" {
3131
#include "catalog/namespace.h"
3232
#include "common/file_perm.h"
3333
#include "lib/stringinfo.h"
34-
#include "miscadmin.h" // superuser
34+
#include "miscadmin.h" // superuser
3535
#include "nodes/value.h" // strVal
3636
#include "utils/fmgrprotos.h" // pg_sequence_last_value
3737
#include "utils/lsyscache.h" // get_relname_relid
@@ -189,6 +189,9 @@ DuckDBManager::Initialize() {
189189
}
190190

191191
LoadFunctions(context);
192+
if (duckdb_autoinstall_known_extensions) {
193+
InstallExtensions(context);
194+
}
192195
LoadExtensions(context);
193196
}
194197

@@ -239,12 +242,21 @@ DuckDBManager::LoadExtensions(duckdb::ClientContext &context) {
239242
auto duckdb_extensions = ReadDuckdbExtensions();
240243

241244
for (auto &extension : duckdb_extensions) {
242-
if (extension.enabled) {
243-
DuckDBQueryOrThrow(context, "LOAD " + extension.name);
245+
if (extension.autoload) {
246+
DuckDBQueryOrThrow(context, ddb::LoadExtensionQuery(extension.name));
244247
}
245248
}
246249
}
247250

251+
void
252+
DuckDBManager::InstallExtensions(duckdb::ClientContext &context) {
253+
auto duckdb_extensions = ReadDuckdbExtensions();
254+
255+
for (auto &extension : duckdb_extensions) {
256+
DuckDBQueryOrThrow(context, ddb::InstallExtensionQuery(extension.name, extension.repository));
257+
}
258+
}
259+
248260
void
249261
DuckDBManager::RefreshConnectionState(duckdb::ClientContext &context) {
250262
const auto extensions_table_last_seq = GetSeqLastValue("extensions_table_seq");
@@ -328,8 +340,8 @@ DuckDBManager::GetConnection(bool force_transaction) {
328340
* Returns the cached connection to the global DuckDB instance, but does not do
329341
* any checks required to correctly initialize the DuckDB transaction nor
330342
* refreshes the secrets/extensions/etc. Only use this in rare cases where you
331-
* know for sure that the connection is already initialized for the correctly
332-
* for the current query, and you just want a pointer to it.
343+
* know for sure that the connection is already initialized correctly for the
344+
* current query, and you just want a pointer to it.
333345
*/
334346
duckdb::Connection *
335347
DuckDBManager::GetConnectionUnsafe() {

0 commit comments

Comments
 (0)