diff --git a/flake.nix b/flake.nix index d3b5f76..d7471d2 100644 --- a/flake.nix +++ b/flake.nix @@ -13,6 +13,14 @@ pkgs = nixpkgs.legacyPackages.${system}; + # PostgreSQL 18 isn't released yet, and therefore isn't available in nixpkgs. As a temporary pre-release measure, + # override the PG17 package with the beta release tag and version to build the beta. + postgresql_18 = (pkgs.postgresql_17.override { + version = "18.0"; + rev = "refs/tags/REL_18_BETA1"; + hash = "sha256-P86bVYfPzkwK/rcvUInYZew/pKejk58/IcnDGx/BWno="; + }); + requiredPythonPackages = ps: ( # Such that we pass the UNSUPPORTS_SQLALCHEMY check in Makefile and can run the SQLAlchemy tests [ ps.sqlalchemy ] ++ ps.sqlalchemy.optional-dependencies.postgresql @@ -34,6 +42,7 @@ postgresql_15 postgresql_16 postgresql_17 + postgresql_18 ]; testVersionCombos = pkgs.lib.cartesianProduct { python = testPythonVersions; diff --git a/src/multicorn.c b/src/multicorn.c index 22bf791..2d9c8d0 100644 --- a/src/multicorn.c +++ b/src/multicorn.c @@ -27,6 +27,9 @@ #include "parser/parsetree.h" #include "fmgr.h" #include "common/hashfn.h" /* oid_hash */ +#if PG_VERSION_NUM >= 180000 +#include "commands/explain_format.h" +#endif PG_MODULE_MAGIC; @@ -361,6 +364,9 @@ multicornGetForeignPaths(PlannerInfo *root, pathes = lappend(pathes, create_foreignscan_path(root, baserel, NULL, /* default pathtarget */ baserel->rows, +#if PG_VERSION_NUM >= 180000 // # of disabled_nodes added in PG 18, commit e22253467942fdb100087787c3e1e3a8620c54b2 + 0, +#endif planstate->startupCost, baserel->rows * baserel->reltarget->width, NIL, /* no pathkeys */ @@ -400,6 +406,9 @@ multicornGetForeignPaths(PlannerInfo *root, newpath = create_foreignscan_path(root, baserel, NULL, /* default pathtarget */ path->path.rows, +#if PG_VERSION_NUM >= 180000 // # of disabled_nodes added in PG 18, commit e22253467942fdb100087787c3e1e3a8620c54b2 + 0, +#endif path->path.startup_cost, path->path.total_cost, apply_pathkeys, NULL, NULL, diff --git a/src/multicorn.h b/src/multicorn.h index 79551ad..c59f7b8 100644 --- a/src/multicorn.h +++ b/src/multicorn.h @@ -6,6 +6,9 @@ #include "catalog/pg_type.h" #include "commands/defrem.h" #include "commands/explain.h" +#if PG_VERSION_NUM >= 180000 +#include "commands/explain_state.h" +#endif #include "foreign/fdwapi.h" #include "foreign/foreign.h" #include "funcapi.h" diff --git a/src/query.c b/src/query.c index 566ff9f..d2dd779 100644 --- a/src/query.c +++ b/src/query.c @@ -714,6 +714,9 @@ findPaths(PlannerInfo *root, RelOptInfo *baserel, List *possiblePaths, root, baserel, NULL, /* default pathtarget */ nbrows, +#if PG_VERSION_NUM >= 180000 // # of disabled_nodes added in PG 18, commit e22253467942fdb100087787c3e1e3a8620c54b2 + 0, +#endif startupCost, nbrows * baserel->reltarget->width, NIL, /* no pathkeys */ @@ -757,7 +760,11 @@ deparse_sortgroup(PlannerInfo *root, Oid foreigntableid, RelOptInfo *rel) if ((expr = multicorn_get_em_expr(ec, rel))) { +#if PG_VERSION_NUM >= 180000 // pk_strategy replaced w/ pkg_cmptype in PG 18, commit 8123e91f5aeb26c6e4cf583bb61c99281485af83 + md->reversed = (key->pk_cmptype == COMPARE_GT); +#else md->reversed = (key->pk_strategy == BTGreaterStrategyNumber); +#endif md->nulls_first = key->pk_nulls_first; md->key = key; diff --git a/test-3.9/expected/write_test_4.out b/test-3.9/expected/write_test_4.out new file mode 100644 index 0000000..b39b044 --- /dev/null +++ b/test-3.9/expected/write_test_4.out @@ -0,0 +1,190 @@ +CREATE EXTENSION multicorn; +CREATE server multicorn_srv foreign data wrapper multicorn options ( + wrapper 'multicorn.testfdw.TestForeignDataWrapper' +); +CREATE user mapping FOR current_user server multicorn_srv options (usermapping 'test'); +CREATE foreign table testmulticorn ( + test1 character varying, + test2 character varying +) server multicorn_srv options ( + option1 'option1', + test_type 'nowrite', + tx_hook 'true' +); +insert into testmulticorn(test1, test2) VALUES ('test', 'test2'); +NOTICE: [('option1', 'option1'), ('test_type', 'nowrite'), ('tx_hook', 'true'), ('usermapping', 'test')] +NOTICE: [('test1', 'character varying'), ('test2', 'character varying')] +NOTICE: BEGIN +NOTICE: ROLLBACK +ERROR: Error in python: NotImplementedError +DETAIL: This FDW does not support the writable API +update testmulticorn set test1 = 'test'; +NOTICE: BEGIN +NOTICE: [] +NOTICE: ['test1', 'test2'] +ERROR: Error in python: NotImplementedError +DETAIL: This FDW does not support the writable API +NOTICE: ROLLBACK +delete from testmulticorn where test2 = 'test2 2 0'; +NOTICE: BEGIN +NOTICE: [test2 = test2 2 0] +NOTICE: ['test1', 'test2'] +ERROR: Error in python: NotImplementedError +DETAIL: This FDW does not support the writable API +NOTICE: ROLLBACK +CREATE foreign table testmulticorn_write ( + test1 character varying, + test2 character varying +) server multicorn_srv options ( + option1 'option1', + row_id_column 'test1', + test_type 'returning', + tx_hook 'true' +); +insert into testmulticorn_write(test1, test2) VALUES ('test', 'test2'); +NOTICE: [('option1', 'option1'), ('row_id_column', 'test1'), ('test_type', 'returning'), ('tx_hook', 'true'), ('usermapping', 'test')] +NOTICE: [('test1', 'character varying'), ('test2', 'character varying')] +NOTICE: BEGIN +NOTICE: INSERTING: [('test1', 'test'), ('test2', 'test2')] +NOTICE: PRECOMMIT +NOTICE: COMMIT +update testmulticorn_write set test1 = 'test' where test1 ilike 'test1 3%'; +NOTICE: BEGIN +NOTICE: [test1 ~~* test1 3%] +NOTICE: ['test1', 'test2'] +NOTICE: UPDATING: test1 3 1 with [('test1', 'test'), ('test2', 'test2 1 1')] +NOTICE: UPDATING: test1 3 4 with [('test1', 'test'), ('test2', 'test2 1 4')] +NOTICE: UPDATING: test1 3 7 with [('test1', 'test'), ('test2', 'test2 1 7')] +NOTICE: UPDATING: test1 3 10 with [('test1', 'test'), ('test2', 'test2 1 10')] +NOTICE: UPDATING: test1 3 13 with [('test1', 'test'), ('test2', 'test2 1 13')] +NOTICE: UPDATING: test1 3 16 with [('test1', 'test'), ('test2', 'test2 1 16')] +NOTICE: UPDATING: test1 3 19 with [('test1', 'test'), ('test2', 'test2 1 19')] +NOTICE: PRECOMMIT +NOTICE: COMMIT +delete from testmulticorn_write where test2 = 'test2 2 0'; +NOTICE: BEGIN +NOTICE: [test2 = test2 2 0] +NOTICE: ['test1', 'test2'] +NOTICE: DELETING: test1 1 0 +NOTICE: PRECOMMIT +NOTICE: COMMIT +-- Test returning +insert into testmulticorn_write(test1, test2) VALUES ('test', 'test2') RETURNING test1; +NOTICE: BEGIN +NOTICE: INSERTING: [('test1', 'test'), ('test2', 'test2')] +NOTICE: PRECOMMIT +NOTICE: COMMIT + test1 +---------------- + INSERTED: test +(1 row) + +update testmulticorn_write set test1 = 'test' where test1 ilike 'test1 3%' RETURNING test1; +NOTICE: BEGIN +NOTICE: [test1 ~~* test1 3%] +NOTICE: ['test1', 'test2'] +NOTICE: UPDATING: test1 3 1 with [('test1', 'test'), ('test2', 'test2 1 1')] +NOTICE: UPDATING: test1 3 4 with [('test1', 'test'), ('test2', 'test2 1 4')] +NOTICE: UPDATING: test1 3 7 with [('test1', 'test'), ('test2', 'test2 1 7')] +NOTICE: UPDATING: test1 3 10 with [('test1', 'test'), ('test2', 'test2 1 10')] +NOTICE: UPDATING: test1 3 13 with [('test1', 'test'), ('test2', 'test2 1 13')] +NOTICE: UPDATING: test1 3 16 with [('test1', 'test'), ('test2', 'test2 1 16')] +NOTICE: UPDATING: test1 3 19 with [('test1', 'test'), ('test2', 'test2 1 19')] +NOTICE: PRECOMMIT +NOTICE: COMMIT + test1 +--------------- + UPDATED: test + UPDATED: test + UPDATED: test + UPDATED: test + UPDATED: test + UPDATED: test + UPDATED: test +(7 rows) + +delete from testmulticorn_write where test1 = 'test1 1 0' returning test2, test1; +NOTICE: BEGIN +NOTICE: [test1 = test1 1 0] +NOTICE: ['test1', 'test2'] +NOTICE: DELETING: test1 1 0 +NOTICE: PRECOMMIT +NOTICE: COMMIT + test2 | test1 +-----------+----------- + test2 2 0 | test1 1 0 +(1 row) + +DROP foreign table testmulticorn_write; +-- Now test with another column +CREATE foreign table testmulticorn_write( + test1 character varying, + test2 character varying +) server multicorn_srv options ( + option1 'option1', + row_id_column 'test2' +); +insert into testmulticorn_write(test1, test2) VALUES ('test', 'test2'); +NOTICE: [('option1', 'option1'), ('row_id_column', 'test2'), ('usermapping', 'test')] +NOTICE: [('test1', 'character varying'), ('test2', 'character varying')] +NOTICE: INSERTING: [('test1', 'test'), ('test2', 'test2')] +update testmulticorn_write set test1 = 'test' where test1 ilike 'test1 3%'; +NOTICE: [test1 ~~* test1 3%] +NOTICE: ['test1', 'test2'] +NOTICE: UPDATING: test2 1 1 with [('test1', 'test'), ('test2', 'test2 1 1')] +NOTICE: UPDATING: test2 1 4 with [('test1', 'test'), ('test2', 'test2 1 4')] +NOTICE: UPDATING: test2 1 7 with [('test1', 'test'), ('test2', 'test2 1 7')] +NOTICE: UPDATING: test2 1 10 with [('test1', 'test'), ('test2', 'test2 1 10')] +NOTICE: UPDATING: test2 1 13 with [('test1', 'test'), ('test2', 'test2 1 13')] +NOTICE: UPDATING: test2 1 16 with [('test1', 'test'), ('test2', 'test2 1 16')] +NOTICE: UPDATING: test2 1 19 with [('test1', 'test'), ('test2', 'test2 1 19')] +delete from testmulticorn_write where test2 = 'test2 2 0'; +NOTICE: [test2 = test2 2 0] +NOTICE: ['test2'] +NOTICE: DELETING: test2 2 0 +update testmulticorn_write set test2 = 'test' where test2 = 'test2 1 1'; +NOTICE: [test2 = test2 1 1] +NOTICE: ['test1', 'test2'] +NOTICE: UPDATING: test2 1 1 with [('test1', 'test1 3 1'), ('test2', 'test')] +DROP foreign table testmulticorn_write; +-- Now test with other types +CREATE foreign table testmulticorn_write( + test1 date, + test2 timestamp +) server multicorn_srv options ( + option1 'option1', + row_id_column 'test2', + test_type 'date' +); +insert into testmulticorn_write(test1, test2) VALUES ('2012-01-01', '2012-01-01 00:00:00'); +NOTICE: [('option1', 'option1'), ('row_id_column', 'test2'), ('test_type', 'date'), ('usermapping', 'test')] +NOTICE: [('test1', 'date'), ('test2', 'timestamp without time zone')] +NOTICE: INSERTING: [('test1', datetime.date(2012, 1, 1)), ('test2', datetime.datetime(2012, 1, 1, 0, 0))] +delete from testmulticorn_write where test2 > '2011-12-03'; +NOTICE: [test2 > 2011-12-03 00:00:00] +NOTICE: ['test2'] +NOTICE: DELETING: 2011-12-03 14:30:25 +update testmulticorn_write set test1 = date_trunc('day', test1) where test2 = '2011-09-03 14:30:25'; +NOTICE: [test2 = 2011-09-03 14:30:25] +NOTICE: ['test1', 'test2'] +NOTICE: UPDATING: 2011-09-03 14:30:25 with [('test1', datetime.date(2011, 9, 2)), ('test2', datetime.datetime(2011, 9, 3, 14, 30, 25))] +DROP foreign table testmulticorn_write; +-- Test with unknown column +CREATE foreign table testmulticorn_write( + test1 date, + test2 timestamp +) server multicorn_srv options ( + option1 'option1', + row_id_column 'teststuff', + test_type 'date' +); +delete from testmulticorn_write; +NOTICE: [('option1', 'option1'), ('row_id_column', 'teststuff'), ('test_type', 'date'), ('usermapping', 'test')] +NOTICE: [('test1', 'date'), ('test2', 'timestamp without time zone')] +ERROR: The rowid attribute does not exist +DROP USER MAPPING FOR current_user SERVER multicorn_srv; +DROP EXTENSION multicorn cascade; +NOTICE: drop cascades to 3 other objects +DETAIL: drop cascades to server multicorn_srv +drop cascades to foreign table testmulticorn +drop cascades to foreign table testmulticorn_write diff --git a/test-3.9/expected/write_test_5.out b/test-3.9/expected/write_test_5.out new file mode 100644 index 0000000..0ddfc48 --- /dev/null +++ b/test-3.9/expected/write_test_5.out @@ -0,0 +1,190 @@ +CREATE EXTENSION multicorn; +CREATE server multicorn_srv foreign data wrapper multicorn options ( + wrapper 'multicorn.testfdw.TestForeignDataWrapper' +); +CREATE user mapping FOR current_user server multicorn_srv options (usermapping 'test'); +CREATE foreign table testmulticorn ( + test1 character varying, + test2 character varying +) server multicorn_srv options ( + option1 'option1', + test_type 'nowrite', + tx_hook 'true' +); +insert into testmulticorn(test1, test2) VALUES ('test', 'test2'); +NOTICE: [('option1', 'option1'), ('test_type', 'nowrite'), ('tx_hook', 'true'), ('usermapping', 'test')] +NOTICE: [('test1', 'character varying'), ('test2', 'character varying')] +NOTICE: BEGIN +ERROR: Error in python: NotImplementedError +DETAIL: This FDW does not support the writable API +NOTICE: ROLLBACK +update testmulticorn set test1 = 'test'; +NOTICE: BEGIN +NOTICE: [] +NOTICE: ['test1', 'test2'] +NOTICE: ROLLBACK +ERROR: Error in python: NotImplementedError +DETAIL: This FDW does not support the writable API +delete from testmulticorn where test2 = 'test2 2 0'; +NOTICE: BEGIN +NOTICE: [test2 = test2 2 0] +NOTICE: ['test1', 'test2'] +ERROR: Error in python: NotImplementedError +DETAIL: This FDW does not support the writable API +NOTICE: ROLLBACK +CREATE foreign table testmulticorn_write ( + test1 character varying, + test2 character varying +) server multicorn_srv options ( + option1 'option1', + row_id_column 'test1', + test_type 'returning', + tx_hook 'true' +); +insert into testmulticorn_write(test1, test2) VALUES ('test', 'test2'); +NOTICE: [('option1', 'option1'), ('row_id_column', 'test1'), ('test_type', 'returning'), ('tx_hook', 'true'), ('usermapping', 'test')] +NOTICE: [('test1', 'character varying'), ('test2', 'character varying')] +NOTICE: BEGIN +NOTICE: INSERTING: [('test1', 'test'), ('test2', 'test2')] +NOTICE: PRECOMMIT +NOTICE: COMMIT +update testmulticorn_write set test1 = 'test' where test1 ilike 'test1 3%'; +NOTICE: BEGIN +NOTICE: [test1 ~~* test1 3%] +NOTICE: ['test1', 'test2'] +NOTICE: UPDATING: test1 3 1 with [('test1', 'test'), ('test2', 'test2 1 1')] +NOTICE: UPDATING: test1 3 4 with [('test1', 'test'), ('test2', 'test2 1 4')] +NOTICE: UPDATING: test1 3 7 with [('test1', 'test'), ('test2', 'test2 1 7')] +NOTICE: UPDATING: test1 3 10 with [('test1', 'test'), ('test2', 'test2 1 10')] +NOTICE: UPDATING: test1 3 13 with [('test1', 'test'), ('test2', 'test2 1 13')] +NOTICE: UPDATING: test1 3 16 with [('test1', 'test'), ('test2', 'test2 1 16')] +NOTICE: UPDATING: test1 3 19 with [('test1', 'test'), ('test2', 'test2 1 19')] +NOTICE: PRECOMMIT +NOTICE: COMMIT +delete from testmulticorn_write where test2 = 'test2 2 0'; +NOTICE: BEGIN +NOTICE: [test2 = test2 2 0] +NOTICE: ['test1', 'test2'] +NOTICE: DELETING: test1 1 0 +NOTICE: PRECOMMIT +NOTICE: COMMIT +-- Test returning +insert into testmulticorn_write(test1, test2) VALUES ('test', 'test2') RETURNING test1; +NOTICE: BEGIN +NOTICE: INSERTING: [('test1', 'test'), ('test2', 'test2')] +NOTICE: PRECOMMIT +NOTICE: COMMIT + test1 +---------------- + INSERTED: test +(1 row) + +update testmulticorn_write set test1 = 'test' where test1 ilike 'test1 3%' RETURNING test1; +NOTICE: BEGIN +NOTICE: [test1 ~~* test1 3%] +NOTICE: ['test1', 'test2'] +NOTICE: UPDATING: test1 3 1 with [('test1', 'test'), ('test2', 'test2 1 1')] +NOTICE: UPDATING: test1 3 4 with [('test1', 'test'), ('test2', 'test2 1 4')] +NOTICE: UPDATING: test1 3 7 with [('test1', 'test'), ('test2', 'test2 1 7')] +NOTICE: UPDATING: test1 3 10 with [('test1', 'test'), ('test2', 'test2 1 10')] +NOTICE: UPDATING: test1 3 13 with [('test1', 'test'), ('test2', 'test2 1 13')] +NOTICE: UPDATING: test1 3 16 with [('test1', 'test'), ('test2', 'test2 1 16')] +NOTICE: UPDATING: test1 3 19 with [('test1', 'test'), ('test2', 'test2 1 19')] +NOTICE: PRECOMMIT +NOTICE: COMMIT + test1 +--------------- + UPDATED: test + UPDATED: test + UPDATED: test + UPDATED: test + UPDATED: test + UPDATED: test + UPDATED: test +(7 rows) + +delete from testmulticorn_write where test1 = 'test1 1 0' returning test2, test1; +NOTICE: BEGIN +NOTICE: [test1 = test1 1 0] +NOTICE: ['test1', 'test2'] +NOTICE: DELETING: test1 1 0 +NOTICE: PRECOMMIT +NOTICE: COMMIT + test2 | test1 +-----------+----------- + test2 2 0 | test1 1 0 +(1 row) + +DROP foreign table testmulticorn_write; +-- Now test with another column +CREATE foreign table testmulticorn_write( + test1 character varying, + test2 character varying +) server multicorn_srv options ( + option1 'option1', + row_id_column 'test2' +); +insert into testmulticorn_write(test1, test2) VALUES ('test', 'test2'); +NOTICE: [('option1', 'option1'), ('row_id_column', 'test2'), ('usermapping', 'test')] +NOTICE: [('test1', 'character varying'), ('test2', 'character varying')] +NOTICE: INSERTING: [('test1', 'test'), ('test2', 'test2')] +update testmulticorn_write set test1 = 'test' where test1 ilike 'test1 3%'; +NOTICE: [test1 ~~* test1 3%] +NOTICE: ['test1', 'test2'] +NOTICE: UPDATING: test2 1 1 with [('test1', 'test'), ('test2', 'test2 1 1')] +NOTICE: UPDATING: test2 1 4 with [('test1', 'test'), ('test2', 'test2 1 4')] +NOTICE: UPDATING: test2 1 7 with [('test1', 'test'), ('test2', 'test2 1 7')] +NOTICE: UPDATING: test2 1 10 with [('test1', 'test'), ('test2', 'test2 1 10')] +NOTICE: UPDATING: test2 1 13 with [('test1', 'test'), ('test2', 'test2 1 13')] +NOTICE: UPDATING: test2 1 16 with [('test1', 'test'), ('test2', 'test2 1 16')] +NOTICE: UPDATING: test2 1 19 with [('test1', 'test'), ('test2', 'test2 1 19')] +delete from testmulticorn_write where test2 = 'test2 2 0'; +NOTICE: [test2 = test2 2 0] +NOTICE: ['test2'] +NOTICE: DELETING: test2 2 0 +update testmulticorn_write set test2 = 'test' where test2 = 'test2 1 1'; +NOTICE: [test2 = test2 1 1] +NOTICE: ['test1', 'test2'] +NOTICE: UPDATING: test2 1 1 with [('test1', 'test1 3 1'), ('test2', 'test')] +DROP foreign table testmulticorn_write; +-- Now test with other types +CREATE foreign table testmulticorn_write( + test1 date, + test2 timestamp +) server multicorn_srv options ( + option1 'option1', + row_id_column 'test2', + test_type 'date' +); +insert into testmulticorn_write(test1, test2) VALUES ('2012-01-01', '2012-01-01 00:00:00'); +NOTICE: [('option1', 'option1'), ('row_id_column', 'test2'), ('test_type', 'date'), ('usermapping', 'test')] +NOTICE: [('test1', 'date'), ('test2', 'timestamp without time zone')] +NOTICE: INSERTING: [('test1', datetime.date(2012, 1, 1)), ('test2', datetime.datetime(2012, 1, 1, 0, 0))] +delete from testmulticorn_write where test2 > '2011-12-03'; +NOTICE: [test2 > 2011-12-03 00:00:00] +NOTICE: ['test2'] +NOTICE: DELETING: 2011-12-03 14:30:25 +update testmulticorn_write set test1 = date_trunc('day', test1) where test2 = '2011-09-03 14:30:25'; +NOTICE: [test2 = 2011-09-03 14:30:25] +NOTICE: ['test1', 'test2'] +NOTICE: UPDATING: 2011-09-03 14:30:25 with [('test1', datetime.date(2011, 9, 2)), ('test2', datetime.datetime(2011, 9, 3, 14, 30, 25))] +DROP foreign table testmulticorn_write; +-- Test with unknown column +CREATE foreign table testmulticorn_write( + test1 date, + test2 timestamp +) server multicorn_srv options ( + option1 'option1', + row_id_column 'teststuff', + test_type 'date' +); +delete from testmulticorn_write; +NOTICE: [('option1', 'option1'), ('row_id_column', 'teststuff'), ('test_type', 'date'), ('usermapping', 'test')] +NOTICE: [('test1', 'date'), ('test2', 'timestamp without time zone')] +ERROR: The rowid attribute does not exist +DROP USER MAPPING FOR current_user SERVER multicorn_srv; +DROP EXTENSION multicorn cascade; +NOTICE: drop cascades to 3 other objects +DETAIL: drop cascades to server multicorn_srv +drop cascades to foreign table testmulticorn +drop cascades to foreign table testmulticorn_write