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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ compile_commands.json
.env
.cache/
log/
/test/schedule
16 changes: 14 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ All notable changes to this project will be documented in this file. It uses the
(almost) always fetches these values in UTC, so can store them as
`TIMESTAMPTZ` values.
* Implemented `INSERT` support for the UUID and INET (IPv4 and IPv6) types.
Thanks to Rahul Mehta for the report (#127)!
Thanks to Rahul Mehta for the report ([#127])!

### 🪲 Bug Fixes

Expand All @@ -30,15 +30,27 @@ All notable changes to this project will be documented in this file. It uses the
* Fixed a server crash when attempting to insert types not yet supported by
the binary engine.

### 🚀 Distribution

* Added the [security policy](SECURITY.md).

### 📔 Notes

* Cleaned up some comments and old references to postgres_fdw left from the
original fork in 2019.
* Added tests demonstrating subqueries that pg_clickhouse does not yet push
down, to be improved in future releases.

[v0.1.3]: https://github.com/clickhouse/pg_clickhouse/compare/v0.1.2...v0.1.3
### 🏗️ Build Setup

* Configured `make installcheck` to run the tests in parallel, resulting in
far faster test execution on multi-core systems. Adjusted the schemas in
which some of the tests work to ensure they don't stomp on each other.

[v0.1.3]: https://github.com/ClickHouse/pg_clickhouse/compare/v0.1.2...v0.1.3
[ClickHouse Issue 88088]: https://github.com/ClickHouse/ClickHouse/pull/88088
[#127]: https://github.com/ClickHouse/pg_clickhouse/issues/127
"ClickHouse/pg_clickhouse#127 INSERT into ClickHouse table with UUID column fails: unexpected column type for 2950: UUID"

## [v0.1.2] — 2026-01-07

Expand Down
9 changes: 7 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ DISTVERSION = $(shell grep -m 1 '[[:space:]]\{3\}"version":' META.json | \
DATA = $(sort $(wildcard sql/$(EXTENSION)--*.sql) sql/$(EXTENSION)--$(EXTVERSION).sql)
DOCS = $(wildcard doc/*.md)
TESTS = $(wildcard test/sql/*.sql)
REGRESS = $(patsubst test/sql/%.sql,%,$(TESTS))
REGRESS = --schedule test/schedule
REGRESS_OPTS = --inputdir=test --load-extension=$(EXTENSION)
PG_CONFIG ?= pg_config
MODULE_big = $(EXTENSION)
Expand Down Expand Up @@ -71,7 +71,7 @@ ifneq ($(OS),darwin)
endif

# Clean up the clickhouse-cpp build directory and generated files.
EXTRA_CLEAN = sql/$(EXTENSION)--$(EXTVERSION).sql src/fdw.c compile_commands.json $(EXTENSION)-$(DISTVERSION).zip
EXTRA_CLEAN = sql/$(EXTENSION)--$(EXTVERSION).sql src/fdw.c compile_commands.json test/schedule $(EXTENSION)-$(DISTVERSION).zip
ifndef NO_VENDOR_CLEAN
EXTRA_CLEAN += $(CH_CPP_BUILD_DIR)
endif
Expand Down Expand Up @@ -137,6 +137,11 @@ dist: $(EXTENSION)-$(DISTVERSION).zip
$(EXTENSION)-$(DISTVERSION).zip:
git archive-all -v --prefix "$(EXTENSION)-$(DISTVERSION)/" --force-submodules $(EXTENSION)-$(DISTVERSION).zip

test/schedule:
@echo "test: $(patsubst test/sql/%.sql,%,$(TESTS))" > $@

installcheck: test/schedule

# Test the PGXN distribution.
dist-test: $(EXTENSION)-$(DISTVERSION).zip
unzip $(EXTENSION)-$(DISTVERSION).zip
Expand Down
80 changes: 80 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# pg_clickhouse Security Vulnerability Response Policy

## Security Change Log and Support

Details regarding security fixes are publicly reported in the [change log].

## Reporting a Vulnerability

We're extremely grateful for security researchers and users who report
vulnerabilities to the pg_clickhouse Open Source Community. Developers
thoroughly investigate all reports.

To report a potential vulnerability in pg_clickhouse please send the details
about it through our public bug bounty program hosted by [Bugcrowd] and be
rewarded for it as per the program scope and rules of engagement.

### When Should I Report a Vulnerability?

* You think you discovered a potential security vulnerability in pg_clickhouse
* You are unsure how a vulnerability affects pg_clickhouse

### When Should I **Not** Report a Vulnerability?

* You need help tuning pg_clickhouse components for security
* You need help applying security related updates
* Your issue is not related to security

## Security Vulnerability Response

pg_clickhouse maintainers acknowledged and analyze each report within 5
working days. As a security issue moves from triage to identified fix to
release planning, we will keep the reporter updated.

## Public Disclosure Timing

The pg_clickhouse maintainers and the bug submitter will negotiate a public
disclosure date. We prefer to fully disclose the bug as soon as possible once
a user mitigation is available. It is reasonable to delay disclosure when the
bug or the fix is not yet fully understood, the solution is not well-tested,
or for vendor coordination. The disclosure timeframe ranges from immediate
(especially if it's already publicly known) to 90 days. For a vulnerability
with a straightforward mitigation, we expect the report date to disclosure
date to be on the order of 7 days.

## Embargo Policy

Open source users and support customers may subscribe to receive alerts during
the embargo period by visiting the [Trust Center], requesting access, and
subscribing for alerts. Subscribers agree not to make these notifications
public, issue communications, share this information with others, or issue
public patches before the disclosure date. Accidental disclosures must be
reported immediately to trust@clickhouse.com. Failure to follow this policy or
repeated leaks may result in removal from the subscriber list.

### Participation criteria:

1. Be a current open source user or support customer with a valid corporate
email domain (no @gmail.com, @azure.com, etc.).
2. Sign up to the pg_clickhouse [Trust Center]
3. Accept the pg_clickhouse Security Vulnerability Response Policy as
outlined here.
4. Subscribe to pg_clickhouse OSS Trust Center alerts.

### Removal criteria:

1. Members may be removed for failure to follow this policy or for repeated
leaks.
2. Members may be removed for bounced messages (mail delivery failure).
3. Members may unsubscribe at any time.

### Notification process:

pg_clickhouse will post notifications within the [Trust Center] and notify
subscribers. Subscribers must log in to the Trust Center to download the
notification. The notification will include the timeframe for public
disclosure.

[change log]: CHANGELOG.md
[Bugcrowd]: https://bugcrowd.com/clickhouse
[Trust Center]: https://trust.clickhouse.com/?product=pg_clickhouse
8 changes: 5 additions & 3 deletions test/expected/binary_inserts.out
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ SELECT clickhouse_raw_query('CREATE TABLE binary_inserts_test.addr (

(1 row)

IMPORT FOREIGN SCHEMA binary_inserts_test FROM SERVER binary_inserts_loopback INTO public;
CREATE SCHEMA binary_inserts_test;
IMPORT FOREIGN SCHEMA binary_inserts_test FROM SERVER binary_inserts_loopback INTO binary_inserts_test;
SET search_path = binary_inserts_test, public;
/* ints */
INSERT INTO ints
SELECT i, i + 1, i + 2, i+ 3 FROM generate_series(1, 3) i;
Expand Down Expand Up @@ -182,7 +184,7 @@ SELECT * FROM null_ints ORDER BY c1;
/* check dates and strings */
ALTER TABLE complex ALTER COLUMN c8 SET DATA TYPE timestamp(3);
\d+ complex
Foreign table "public.complex"
Foreign table "binary_inserts_test.complex"
Column | Type | Collation | Nullable | Default | FDW options | Storage | Stats target | Description
--------+--------------------------------+-----------+----------+---------+-------------+----------+--------------+-------------
c1 | integer | | not null | | | plain | |
Expand Down Expand Up @@ -250,7 +252,7 @@ SELECT * FROM arrays ORDER BY c1;

/* Check UUIDs and IPs */
\d addr
Foreign table "public.addr"
Foreign table "binary_inserts_test.addr"
Column | Type | Collation | Nullable | Default | FDW options
--------+------+-----------+----------+---------+-------------
c1 | uuid | | not null | |
Expand Down
8 changes: 5 additions & 3 deletions test/expected/binary_inserts_1.out
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ SELECT clickhouse_raw_query('CREATE TABLE binary_inserts_test.addr (

(1 row)

IMPORT FOREIGN SCHEMA binary_inserts_test FROM SERVER binary_inserts_loopback INTO public;
CREATE SCHEMA binary_inserts_test;
IMPORT FOREIGN SCHEMA binary_inserts_test FROM SERVER binary_inserts_loopback INTO binary_inserts_test;
SET search_path = binary_inserts_test, public;
/* ints */
INSERT INTO ints
SELECT i, i + 1, i + 2, i+ 3 FROM generate_series(1, 3) i;
Expand Down Expand Up @@ -182,7 +184,7 @@ SELECT * FROM null_ints ORDER BY c1;
/* check dates and strings */
ALTER TABLE complex ALTER COLUMN c8 SET DATA TYPE timestamp(3);
\d+ complex
Foreign table "public.complex"
Foreign table "binary_inserts_test.complex"
Column | Type | Collation | Nullable | Default | FDW options | Storage | Stats target | Description
--------+--------------------------------+-----------+----------+---------+-------------+----------+--------------+-------------
c1 | integer | | not null | | | plain | |
Expand Down Expand Up @@ -241,7 +243,7 @@ SELECT * FROM arrays ORDER BY c1;

/* Check UUIDs and IPs */
\d addr
Foreign table "public.addr"
Foreign table "binary_inserts_test.addr"
Column | Type | Collation | Nullable | Default | FDW options
--------+------+-----------+----------+---------+-------------
c1 | uuid | | not null | |
Expand Down
32 changes: 17 additions & 15 deletions test/expected/binary_queries.out
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ SELECT clickhouse_raw_query('CREATE TABLE binary_queries_test.t4 (c1 Int, c2 Int

(1 row)

CREATE SCHEMA binary_queries_test;
SET search_path = binary_queries_test, public;
CREATE FOREIGN TABLE ft1 (
c0 int,
c1 int NOT NULL,
Expand Down Expand Up @@ -165,7 +167,7 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 ORDER BY t1.c1, t1.tableoid OF
-> Sort
Output: c1, c2, c3, c4, c5, c6, c7, c8, tableoid
Sort Key: t1.c1, t1.tableoid
-> Foreign Scan on public.ft1 t1
-> Foreign Scan on binary_queries_test.ft1 t1
Output: c1, c2, c3, c4, c5, c6, c7, c8, tableoid
Remote SQL: SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM binary_queries_test.t1
(8 rows)
Expand All @@ -188,7 +190,7 @@ SELECT * FROM ft1 t1 ORDER BY t1.c1, t1.tableoid OFFSET 100 LIMIT 10;
EXPLAIN (VERBOSE, COSTS OFF) SELECT t1 FROM ft1 t1 ORDER BY t1.c1 OFFSET 100 LIMIT 10;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1
Foreign Scan on binary_queries_test.ft1 t1
Output: t1.*, c1
Remote SQL: SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM binary_queries_test.t1 ORDER BY c1 ASC NULLS LAST LIMIT 10 OFFSET 100
(3 rows)
Expand Down Expand Up @@ -216,7 +218,7 @@ SELECT * FROM ft1 WHERE false;
EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE t1.c1 = 101 AND t1.c6 = '1' AND t1.c7 >= '1';
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1
Foreign Scan on binary_queries_test.ft1 t1
Output: c1, c2, c3, c4, c5, c6, c7, c8
Remote SQL: SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM binary_queries_test.t1 WHERE ((c7 >= '1')) AND ((c1 = 101)) AND ((c6 = '1'))
(3 rows)
Expand Down Expand Up @@ -322,15 +324,15 @@ RESET enable_nestloop;
EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE t1.c1 = 1; -- Var, OpExpr(b), Const
QUERY PLAN
--------------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1
Foreign Scan on binary_queries_test.ft1 t1
Output: c1, c2, c3, c4, c5, c6, c7, c8
Remote SQL: SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM binary_queries_test.t1 WHERE ((c1 = 1))
(3 rows)

EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE t1.c1 = 100 AND t1.c2 = 0; -- BoolExpr
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1
Foreign Scan on binary_queries_test.ft1 t1
Output: c1, c2, c3, c4, c5, c6, c7, c8
Remote SQL: SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM binary_queries_test.t1 WHERE ((c1 = 100)) AND ((c2 = 0))
(3 rows)
Expand All @@ -346,47 +348,47 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 IS NULL; -- Nu
EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 IS NOT NULL; -- NullTest
QUERY PLAN
---------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1
Foreign Scan on binary_queries_test.ft1 t1
Output: c1, c2, c3, c4, c5, c6, c7, c8
Remote SQL: SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM binary_queries_test.t1
(3 rows)

EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE round(abs(c1), 0) = 1; -- FuncExpr
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1
Foreign Scan on binary_queries_test.ft1 t1
Output: c1, c2, c3, c4, c5, c6, c7, c8
Remote SQL: SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM binary_queries_test.t1 WHERE ((round(abs(c1), 0) = 1))
(3 rows)

EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 = -c1; -- OpExpr(l)
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1
Foreign Scan on binary_queries_test.ft1 t1
Output: c1, c2, c3, c4, c5, c6, c7, c8
Remote SQL: SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM binary_queries_test.t1 WHERE ((c1 = (- c1)))
(3 rows)

EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE 1 = factorial(c1); -- OpExpr(r)
QUERY PLAN
-------------------------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1
Foreign Scan on binary_queries_test.ft1 t1
Output: c1, c2, c3, c4, c5, c6, c7, c8
Remote SQL: SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM binary_queries_test.t1 WHERE ((1 = factorial(c1)))
(3 rows)

EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE (c1 IS NOT NULL) IS DISTINCT FROM (c1 IS NOT NULL); -- DistinctExpr
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1
Foreign Scan on binary_queries_test.ft1 t1
Output: c1, c2, c3, c4, c5, c6, c7, c8
Remote SQL: SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM binary_queries_test.t1 WHERE (((c1 IS NOT NULL) IS DISTINCT FROM (c1 IS NOT NULL)))
(3 rows)

EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 = ANY(ARRAY[c2, 1, c1 + 0]); -- ScalarArrayOpExpr
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1
Foreign Scan on binary_queries_test.ft1 t1
Output: c1, c2, c3, c4, c5, c6, c7, c8
Remote SQL: SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM binary_queries_test.t1 WHERE ((has([c2, 1, (c1 + 0)],c1)))
(3 rows)
Expand Down Expand Up @@ -509,7 +511,7 @@ SELECT * FROM ft1 t1 WHERE c1 = ANY(ARRAY[c2, 1, c1 + 0]) ORDER BY c1; -- Scalar
EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c1 = (ARRAY[c1,c2,3])[1]; -- ArrayRef
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1
Foreign Scan on binary_queries_test.ft1 t1
Output: c1, c2, c3, c4, c5, c6, c7, c8
Remote SQL: SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM binary_queries_test.t1 WHERE ((c1 = (([c1, c2, 3])[1])))
(3 rows)
Expand Down Expand Up @@ -632,15 +634,15 @@ SELECT * FROM ft1 t1 WHERE c1 = (ARRAY[c1,c2,3])[1] ORDER BY c1; -- ArrayRef
EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c6 = E'foo''s\\bar'; -- check special chars
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1
Foreign Scan on binary_queries_test.ft1 t1
Output: c1, c2, c3, c4, c5, c6, c7, c8
Remote SQL: SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM binary_queries_test.t1 WHERE ((c6 = 'foo''s\\bar'))
(3 rows)

EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE c8 = 'foo'; -- can't be sent to remote
QUERY PLAN
------------------------------------------------------------------------------------------------------
Foreign Scan on public.ft1 t1
Foreign Scan on binary_queries_test.ft1 t1
Output: c1, c2, c3, c4, c5, c6, c7, c8
Remote SQL: SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM binary_queries_test.t1 WHERE ((c8 = 'foo'))
(3 rows)
Expand Down Expand Up @@ -700,7 +702,7 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT COUNT(DISTINCT c1) FILTER (WHERE c1 < 20) FR
--------------------------------------------------------------------------------------
Aggregate
Output: count(DISTINCT c1) FILTER (WHERE (c1 < 20))
-> Foreign Scan on public.ft2
-> Foreign Scan on binary_queries_test.ft2
Output: c1, c2
Remote SQL: SELECT c1 FROM binary_queries_test.t2 ORDER BY c1 ASC NULLS LAST
(5 rows)
Expand Down
Loading
Loading