Skip to content

Commit de2bcae

Browse files
datalake_fdw: Apache Iceberg lake tables as an extension (skeleton)
Add contrib/datalake_fdw, a skeleton for Iceberg lake-table support that needs no kernel changes: a lake table is an ordinary CREATE TABLE ... USING iceberg, and its binding travels in reloptions naming a catalog server and a volume server, both created through foreign-data wrappers this extension registers. Making the binding a pair of foreign servers is what keeps the kernel out of it. Server options, ownership, privileges and dump/restore already exist for foreign servers, reloptions already reach every segment with pg_class, and recording the two servers in pg_depend makes DROP SERVER refuse to strand a table -- none of which needs new catalogs or grammar. The DDL path is complete against a stub metadata engine, so CREATE TABLE and DROP TABLE work end to end with no catalog service, object store, Arrow or JVM in the picture. Everything that would touch data reports a clean "iceberg: <operation> is not supported yet". The interfaces the later work plugs into ship whole so they can be reviewed before there is an implementation behind them: the IcebergMetaEngine vtable with a capability bitmap the registry validates and dispatches through, the FormatReader/FormatWriter instance interfaces, and the storage facade over open/read/write/list. Details worth a reviewer's attention: * Table metadata always goes through one engine, the Java agent, and nothing selects between implementations -- no option, no setting. The vtable stays because the implementation is expected to change; that is a property of the build, never of a table or a session, so an existing table can never be reinterpreted by a configuration change. * The table access method fills every callback GetTableAmRoutine() asserts. ANALYZE succeeds as a zero-sample no-op through relation_acquire_sample_rows, which keeps it off the scan path that reports not-supported, and VACUUM is a no-op, so database-wide maintenance never dies on a lake table. * The object-access hook records the server dependencies on the coordinator and on every segment, while only GP_ROLE_DISPATCH calls the metadata engine, so each node can protect its own catalog and the remote side sees one call. Utility-mode DDL is refused rather than creating local state without dispatch. * VACUUM FULL is refused in the utility hook, not in the access method: relation rewriting creates a transient relation first, which reaches OAT_POST_CREATE and has the engine create a table remotely before the rewrite reports its error, leaving an orphan behind. * Credentials are refused in server options and belong in user mappings, which stay optional so ambient object-store credentials remain usable. Binding resolution never reads them, so DDL and DROP work with none configured. * Volume URIs are parsed once, in the options layer, into a versioned DatalakeLocation; backends receive only that canonical form. * C++ translation units reach the server headers through common/dl_pg_api.h, which applies extern "C" -- without it the module builds and then fails to dlopen on a mangled errmsg. The C/C++ boundary macros follow the PAX pattern, including deferring ereport() until after the catch handler is left, since longjmp() out of a handler is undefined. Exported symbols are limited to the PG entry points listed in exports.txt, ELF and Mach-O each getting the right linker mechanism, so a future static Arrow cannot leak into other extensions. The regression suite covers the DDL path including per-segment catalog state, the rejection matrices, and the privilege model; installcheck is green on a three-segment cluster. Testing against real catalogs and object storage needs a service harness rather than expected-output files, and arrives with the change that first talks to them -- see test/automation/README.md.
1 parent d482b42 commit de2bcae

44 files changed

Lines changed: 5816 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

contrib/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ SUBDIRS = \
2525
btree_gin \
2626
btree_gist \
2727
citext \
28+
datalake_fdw \
2829
dblink \
2930
dict_int \
3031
dict_xsyn \

contrib/datalake_fdw/.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Generated subdirectories
2+
/log/
3+
/results/
4+
/tmp_check/
5+
6+
# Generated from exports.txt at build time
7+
/exports.map
8+
/exports_darwin.list

contrib/datalake_fdw/Makefile

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
#
18+
# contrib/datalake_fdw/Makefile
19+
20+
MODULE_big = datalake_fdw
21+
EXTENSION = datalake_fdw
22+
DATA = datalake_fdw--1.0.sql
23+
24+
OBJS = \
25+
src/am_iceberg/pg_iceberg_am_handler.o \
26+
src/am_iceberg/pg_iceberg_extensible.o \
27+
src/am_iceberg/pg_iceberg_ddl.o \
28+
src/am_iceberg/pg_iceberg_options.o \
29+
src/am_iceberg/pg_iceberg_guc.o \
30+
src/am_iceberg/pg_iceberg_reject.o \
31+
src/iceberg_catalog_fdw/iceberg_catalog_fdw.o \
32+
src/iceberg_volume_fdw/iceberg_volume_fdw.o \
33+
src/meta/meta_engine_registry.o \
34+
src/meta/meta_engine_init.o \
35+
src/meta/engine_stub/stub_engine.o \
36+
src/format/format_registry.o \
37+
src/common/file_system_wrapper.o \
38+
src/common/s3_file_system.o \
39+
src/common/backend_registry.o
40+
41+
# Use the documented PGXS knobs: pgxs.mk appends these AFTER the flags configure
42+
# chose, so optimization/warning settings survive. A pre-include
43+
# "override CFLAGS +=" would give CFLAGS override origin and silently discard
44+
# Makefile.global's own "CFLAGS = @CFLAGS@" assignment.
45+
PG_CFLAGS = -fvisibility=hidden
46+
PG_CXXFLAGS = -fvisibility=hidden -fvisibility-inlines-hidden -std=c++17
47+
PG_CPPFLAGS = -I$(srcdir)/src
48+
49+
REGRESS = iceberg_am_ddl iceberg_am_reject iceberg_am_acl
50+
51+
EXTRA_CLEAN = exports_darwin.list exports.map
52+
53+
# Keep the aggregate target as make's default goal.
54+
all:
55+
56+
ifdef USE_PGXS
57+
PG_CONFIG = pg_config
58+
PGXS := $(shell $(PG_CONFIG) --pgxs)
59+
include $(PGXS)
60+
else
61+
subdir = contrib/datalake_fdw
62+
top_builddir = ../..
63+
include $(top_builddir)/src/Makefile.global
64+
include $(top_srcdir)/contrib/contrib-global.mk
65+
endif
66+
67+
# Everything below needs variables that Makefile.global defines (PORTNAME), and
68+
# SHLIB_LINK additions still apply because the link recipe expands it when it
69+
# runs.
70+
71+
# Shared libraries are linked with $(CC) (see src/Makefile.shlib COMPILER), so a
72+
# module containing C++ translation units must pull in the C++ runtime itself.
73+
SHLIB_LINK += -lstdc++
74+
75+
# Arrow and other C++ dependencies land in this module later; the export list is
76+
# the single place that decides what stays visible, so the mechanism ships now.
77+
ifeq ($(PORTNAME), darwin)
78+
EXPORT_LIST = exports_darwin.list
79+
SHLIB_LINK += -Wl,-exported_symbols_list,exports_darwin.list
80+
81+
exports_darwin.list: exports.txt
82+
sed -e '/^[[:space:]]*#/d' -e '/^[[:space:]]*$$/d' -e 's/^/_/' $< > $@
83+
else
84+
EXPORT_LIST = exports.map
85+
SHLIB_LINK += -Wl,--version-script=exports.map -Wl,--exclude-libs,ALL
86+
87+
exports.map: exports.txt
88+
{ echo '{ global:'; sed -e '/^[[:space:]]*#/d' -e '/^[[:space:]]*$$/d' -e 's/$$/;/' $<; echo 'local: *; };'; } > $@
89+
endif
90+
91+
all: $(EXPORT_LIST)
92+
$(shlib): $(EXPORT_LIST)
93+
94+
%.o: %.cc
95+
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@

contrib/datalake_fdw/README.md

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# datalake_fdw
2+
3+
`datalake_fdw` is a skeleton of Apache Iceberg lake-table support for Apache
4+
Cloudberry, built as an ordinary extension: no kernel changes, no new grammar.
5+
A lake table is a regular `CREATE TABLE ... USING iceberg`, and its binding to a
6+
catalog and a storage volume travels in reloptions that point at foreign servers.
7+
8+
This first drop wires up the whole DDL path against a stub metadata engine, so
9+
`CREATE TABLE` and `DROP TABLE` work end to end with no external dependency --
10+
no catalog service, no object store, no Arrow, no JVM. Everything that would
11+
touch data reports a clean not-supported error. The interfaces the later work
12+
plugs into (metadata engine vtable, format reader/writer, storage facade) ship
13+
here in full so they can be reviewed before there is an implementation behind
14+
them.
15+
16+
## Build and configuration
17+
18+
```sh
19+
make USE_PGXS=1 install
20+
```
21+
22+
The extension installs hooks from `_PG_init`, so it must be preloaded:
23+
24+
```conf
25+
shared_preload_libraries = 'datalake_fdw'
26+
```
27+
28+
```sh
29+
gpconfig -c shared_preload_libraries -v datalake_fdw && gpstop -ar
30+
```
31+
32+
## Example
33+
34+
```sql
35+
CREATE EXTENSION datalake_fdw;
36+
37+
-- Where the table metadata lives.
38+
CREATE SERVER hive_cat FOREIGN DATA WRAPPER iceberg_catalog_fdw
39+
OPTIONS (type 'hive', uri 'thrift://metastore:9083');
40+
41+
-- Where the data files live.
42+
CREATE SERVER s3_vol FOREIGN DATA WRAPPER iceberg_volume_fdw
43+
OPTIONS (base_path 's3://bucket/prefix', endpoint 'http://minio:9000',
44+
region 'us-east-1');
45+
46+
-- Credentials are optional: without a user mapping the storage backend falls
47+
-- back to ambient credentials (instance role, environment, and so on).
48+
CREATE USER MAPPING FOR CURRENT_USER SERVER s3_vol
49+
OPTIONS (access_key '...', secret_key '...');
50+
51+
CREATE TABLE t (a int, b text)
52+
USING iceberg
53+
WITH (catalog = 'hive_cat', volume = 's3_vol');
54+
55+
DROP TABLE t;
56+
```
57+
58+
Both servers are recorded as dependencies of the table, so `DROP SERVER` is
59+
refused while lake tables reference it, and `DROP SERVER ... CASCADE` removes
60+
them together. Creating a table requires `USAGE` on both servers.
61+
62+
`iceberg.default_catalog` and `iceberg.default_volume` supply the binding when
63+
`WITH` omits it.
64+
65+
Table metadata always goes through one metadata engine, the Java agent, and
66+
nothing selects between implementations -- no option, no setting. The engine
67+
sits behind a vtable so that the implementation can change (an in-process C++
68+
one is the expected next step), but that is a property of the build, never of a
69+
table or a session, so an existing table can never be reinterpreted by a
70+
configuration change.
71+
72+
## What this skeleton does
73+
74+
| Works | Behaviour |
75+
| --- | --- |
76+
| `CREATE TABLE ... USING iceberg` | Binding validated, distribution forced to random, stub engine reports the create |
77+
| `DROP TABLE` | Stub engine reports the drop; dependencies removed on every segment |
78+
| `ANALYZE`, `VACUUM` | Succeed as no-ops, so database-wide maintenance is never blocked by a lake table |
79+
| Catalog and volume validators | Per-context option allowlists; credentials are refused on servers and belong in user mappings |
80+
81+
| Rejected for now | |
82+
| --- | --- |
83+
| `SELECT`, `INSERT`, `UPDATE`, `DELETE`, `COPY` | Data paths land in a later change |
84+
| `CREATE INDEX`, `TABLESAMPLE`, `TRUNCATE`, `CLUSTER`, `VACUUM FULL` | Not meaningful, or unsafe, without the data path |
85+
| Every `ALTER` form on a lake table, and on servers a lake table references | Schema evolution and identity tracking land in a later change |
86+
| `CREATE TABLE AS`, materialized views, partitioning, inheritance, typed tables, `TEMP`, `UNLOGGED`, `ON COMMIT`, explicit `TABLESPACE` | Out of scope for the skeleton |
87+
88+
## Running the tests
89+
90+
Against a running cluster with the library preloaded:
91+
92+
```sh
93+
make USE_PGXS=1 installcheck
94+
```
95+
96+
The suite covers the DDL path (including per-segment catalog state), the
97+
rejection matrices, and the privilege model.
98+
99+
## Layout
100+
101+
| Directory | Contents |
102+
| --- | --- |
103+
| `src/am_iceberg/` | Table access method, utility and object-access hooks, binding resolution, GUCs |
104+
| `src/iceberg_catalog_fdw/`, `src/iceberg_volume_fdw/` | Foreign-data-wrapper validators for the two server kinds |
105+
| `src/meta/` | `IcebergMetaEngine` vtable, registry with capability checking, stub engine |
106+
| `src/format/` | `FormatReader` / `FormatWriter` interfaces and their registry |
107+
| `src/common/` | Storage facade, canonical location type, C/C++ exception boundary macros |
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*
19+
* contrib/datalake_fdw/datalake_fdw--1.0.sql
20+
*/
21+
22+
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
23+
\echo Use "CREATE EXTENSION datalake_fdw" to load this file. \quit
24+
25+
CREATE FUNCTION iceberg_am_handler(internal)
26+
RETURNS table_am_handler AS 'MODULE_PATHNAME' LANGUAGE C;
27+
28+
CREATE ACCESS METHOD iceberg TYPE TABLE HANDLER iceberg_am_handler;
29+
30+
CREATE FUNCTION iceberg_catalog_fdw_validator(text[], oid)
31+
RETURNS void AS 'MODULE_PATHNAME' LANGUAGE C STRICT;
32+
33+
CREATE FOREIGN DATA WRAPPER iceberg_catalog_fdw
34+
VALIDATOR iceberg_catalog_fdw_validator;
35+
36+
CREATE FUNCTION iceberg_volume_fdw_validator(text[], oid)
37+
RETURNS void AS 'MODULE_PATHNAME' LANGUAGE C STRICT;
38+
39+
CREATE FOREIGN DATA WRAPPER iceberg_volume_fdw
40+
VALIDATOR iceberg_volume_fdw_validator;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
#
18+
# contrib/datalake_fdw/datalake_fdw.control
19+
20+
comment = 'Apache Iceberg lake tables for Cloudberry (demo skeleton)'
21+
default_version = '1.0'
22+
module_pathname = '$libdir/datalake_fdw'
23+
relocatable = false
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
-- Foreign-server USAGE is required; user mappings remain optional.
2+
SET client_min_messages = warning;
3+
RESET ROLE;
4+
DROP SCHEMA IF EXISTS dlskel_s CASCADE;
5+
DROP SERVER IF EXISTS dlskel_acl_cat CASCADE;
6+
DROP SERVER IF EXISTS dlskel_acl_vol CASCADE;
7+
DROP ROLE IF EXISTS dlskel_user;
8+
RESET client_min_messages;
9+
CREATE EXTENSION IF NOT EXISTS datalake_fdw;
10+
NOTICE: extension "datalake_fdw" already exists, skipping
11+
CREATE SERVER dlskel_acl_cat
12+
FOREIGN DATA WRAPPER iceberg_catalog_fdw
13+
OPTIONS (type 'hive', uri 'thrift://fake:9083');
14+
CREATE SERVER dlskel_acl_vol
15+
FOREIGN DATA WRAPPER iceberg_volume_fdw
16+
OPTIONS (base_path 's3://dlskel-bucket/acl',
17+
endpoint 'http://fake:9000');
18+
CREATE ROLE dlskel_user LOGIN;
19+
NOTICE: resource queue required -- using default resource queue "pg_default"
20+
CREATE SCHEMA dlskel_s;
21+
GRANT CREATE, USAGE ON SCHEMA dlskel_s TO dlskel_user;
22+
SET ROLE dlskel_user;
23+
CREATE TABLE dlskel_s.t (a int)
24+
USING iceberg
25+
WITH (catalog = 'dlskel_acl_cat', volume = 'dlskel_acl_vol');
26+
ERROR: permission denied for foreign server dlskel_acl_cat
27+
RESET ROLE;
28+
GRANT USAGE ON FOREIGN SERVER dlskel_acl_cat TO dlskel_user;
29+
SET ROLE dlskel_user;
30+
CREATE TABLE dlskel_s.t (a int)
31+
USING iceberg
32+
WITH (catalog = 'dlskel_acl_cat', volume = 'dlskel_acl_vol');
33+
ERROR: permission denied for foreign server dlskel_acl_vol
34+
RESET ROLE;
35+
GRANT USAGE ON FOREIGN SERVER dlskel_acl_vol TO dlskel_user;
36+
SET ROLE dlskel_user;
37+
CREATE TABLE dlskel_s.t (a int)
38+
USING iceberg
39+
WITH (catalog = 'dlskel_acl_cat', volume = 'dlskel_acl_vol');
40+
NOTICE: stub engine: created iceberg table "dlskel_s.t" in catalog "dlskel_acl_cat"
41+
CREATE USER MAPPING FOR dlskel_user
42+
SERVER dlskel_acl_cat
43+
OPTIONS (user 'u', password 'p');
44+
ALTER USER MAPPING FOR dlskel_user
45+
SERVER dlskel_acl_cat
46+
OPTIONS (ADD warehouse 'x');
47+
ERROR: invalid iceberg catalog user mapping option "warehouse"
48+
HINT: Allowed options are "user", "password", and "token".
49+
RESET ROLE;
50+
DROP SCHEMA dlskel_s CASCADE;
51+
NOTICE: drop cascades to table dlskel_s.t
52+
NOTICE: stub engine: dropped iceberg table "dlskel_s.t" from catalog "dlskel_acl_cat"
53+
DROP USER MAPPING FOR dlskel_user SERVER dlskel_acl_cat;
54+
DROP SERVER dlskel_acl_cat;
55+
DROP SERVER dlskel_acl_vol;
56+
DROP ROLE dlskel_user;
57+
SET client_min_messages = warning;
58+
RESET ROLE;
59+
DROP SCHEMA IF EXISTS dlskel_s CASCADE;
60+
DROP SERVER IF EXISTS dlskel_acl_cat CASCADE;
61+
DROP SERVER IF EXISTS dlskel_acl_vol CASCADE;
62+
DROP ROLE IF EXISTS dlskel_user;
63+
RESET client_min_messages;

0 commit comments

Comments
 (0)