mirror of https://github.com/citusdata/citus.git
PG18 - Handle publish_generated_columns in distributed publications (#8360)
https://github.com/postgres/postgres/commit/7054186c4 fixes #8358 This PR wires up PostgreSQL 18’s `publish_generated_columns` publication option in Citus and adds regression coverage to ensure it behaves correctly for distributed tables, without changing existing DDL output for publications that rely on the default. --- ### 1. Preserve `publish_generated_columns` when rebuilding publications In `BuildCreatePublicationStmt`: * On PG18+ we now read the new `pubgencols` field from `pg_publication` and map it as follows: * `'n'` → default (`none`) * `'s'` → `stored` * For `pubgencols == 's'` we append a `publish_generated_columns` defelem to the reconstructed statement: ```c #if PG_VERSION_NUM >= PG_VERSION_18 if (publicationForm->pubgencols == 's') /* stored */ { DefElem *pubGenColsOption = makeDefElem("publish_generated_columns", (Node *) makeString("stored"), -1); createPubStmt->options = lappend(createPubStmt->options, pubGenColsOption); } else if (publicationForm->pubgencols != 'n') /* 'n' = none (default) */ { ereport(ERROR, (errmsg("unexpected pubgencols value '%c' for publication %u", publicationForm->pubgencols, publicationId))); } #endif ``` * For `pubgencols == 'n'` we do **not** emit an option and rely on PostgreSQL’s default. * Any value other than `'n'` or `'s'` raises an error rather than silently producing incorrect DDL. This ensures: * Publications that explicitly use `publish_generated_columns = stored` are reconstructed with that option on workers, so workers get `pubgencols = 's'`. * Publications that use the default (`none`) continue to produce the same `CREATE PUBLICATION ... WITH (...)` text as before (no extra `publish_generated_columns = 'none'` noise), fixing the unintended diffs in existing publication tests. --- ### 2. New PG18 regression coverage for distributed publications In `src/test/regress/sql/pg18.sql`: * Create a table with a stored generated column and make it distributed so the publication goes through Citus DDL propagation: ```sql CREATE TABLE gen_pub_tab ( id int primary key, a int, b int GENERATED ALWAYS AS (a * 10) STORED ); SELECT create_distributed_table('gen_pub_tab', 'id', colocate_with := 'none'); ``` * Create two publications that exercise both `pubgencols` values: ```sql CREATE PUBLICATION pub_gen_cols_stored FOR TABLE gen_pub_tab WITH (publish = 'insert, update', publish_generated_columns = stored); CREATE PUBLICATION pub_gen_cols_none FOR TABLE gen_pub_tab WITH (publish = 'insert, update', publish_generated_columns = none); ``` * On coordinator and both workers, assert the catalog contents: ```sql SELECT pubname, pubgencols FROM pg_publication WHERE pubname IN ('pub_gen_cols_stored', 'pub_gen_cols_none') ORDER BY pubname; ``` Expected on all three nodes: * `pub_gen_cols_stored | s` * `pub_gen_cols_none | n` This test verifies that: * `pubgencols` is correctly set on the coordinator for both `stored` and `none`. * Citus propagates the setting unchanged to all workers for a distributed table.pull/8336/head
parent
662b7248db
commit
c600eabd82
|
|
@ -196,6 +196,27 @@ BuildCreatePublicationStmt(Oid publicationId)
|
||||||
-1);
|
-1);
|
||||||
createPubStmt->options = lappend(createPubStmt->options, pubViaRootOption);
|
createPubStmt->options = lappend(createPubStmt->options, pubViaRootOption);
|
||||||
|
|
||||||
|
/* WITH (publish_generated_columns = ...) option (PG18+) */
|
||||||
|
#if PG_VERSION_NUM >= PG_VERSION_18
|
||||||
|
if (publicationForm->pubgencols == 's') /* stored */
|
||||||
|
{
|
||||||
|
DefElem *pubGenColsOption =
|
||||||
|
makeDefElem("publish_generated_columns",
|
||||||
|
(Node *) makeString("stored"),
|
||||||
|
-1);
|
||||||
|
|
||||||
|
createPubStmt->options =
|
||||||
|
lappend(createPubStmt->options, pubGenColsOption);
|
||||||
|
}
|
||||||
|
else if (publicationForm->pubgencols != 'n') /* 'n' = none (default) */
|
||||||
|
{
|
||||||
|
ereport(ERROR,
|
||||||
|
(errmsg("unexpected pubgencols value '%c' for publication %u",
|
||||||
|
publicationForm->pubgencols, publicationId)));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/* WITH (publish = 'insert, update, delete, truncate') option */
|
/* WITH (publish = 'insert, update, delete, truncate') option */
|
||||||
List *publishList = NIL;
|
List *publishList = NIL;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1070,6 +1070,187 @@ CREATE MATERIALIZED VIEW copytest_mv AS
|
||||||
SELECT create_distributed_table('copytest_mv', 'id');
|
SELECT create_distributed_table('copytest_mv', 'id');
|
||||||
ERROR: copytest_mv is not a regular, foreign or partitioned table
|
ERROR: copytest_mv is not a regular, foreign or partitioned table
|
||||||
-- After that, any command on the materialized view is outside Citus support.
|
-- After that, any command on the materialized view is outside Citus support.
|
||||||
|
-- PG18: verify publish_generated_columns is preserved for distributed tables
|
||||||
|
-- https://github.com/postgres/postgres/commit/7054186c4
|
||||||
|
\c - - - :master_port
|
||||||
|
CREATE SCHEMA pg18_publication;
|
||||||
|
SET search_path TO pg18_publication;
|
||||||
|
-- table with a stored generated column
|
||||||
|
CREATE TABLE gen_pub_tab (
|
||||||
|
id int primary key,
|
||||||
|
a int,
|
||||||
|
b int GENERATED ALWAYS AS (a * 10) STORED
|
||||||
|
);
|
||||||
|
-- make it distributed so CREATE PUBLICATION goes through Citus metadata/DDL path
|
||||||
|
SELECT create_distributed_table('gen_pub_tab', 'id', colocate_with := 'none');
|
||||||
|
create_distributed_table
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- publication using the new PG18 option: stored
|
||||||
|
CREATE PUBLICATION pub_gen_cols_stored
|
||||||
|
FOR TABLE gen_pub_tab
|
||||||
|
WITH (publish = 'insert, update', publish_generated_columns = stored);
|
||||||
|
-- second publication explicitly using "none" for completeness
|
||||||
|
CREATE PUBLICATION pub_gen_cols_none
|
||||||
|
FOR TABLE gen_pub_tab
|
||||||
|
WITH (publish = 'insert, update', publish_generated_columns = none);
|
||||||
|
-- On coordinator: pubgencols must be 's' and 'n' respectively
|
||||||
|
SELECT pubname, pubgencols
|
||||||
|
FROM pg_publication
|
||||||
|
WHERE pubname IN ('pub_gen_cols_stored', 'pub_gen_cols_none')
|
||||||
|
ORDER BY pubname;
|
||||||
|
pubname | pubgencols
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
pub_gen_cols_none | n
|
||||||
|
pub_gen_cols_stored | s
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- On worker 1: both publications must exist and keep pubgencols in sync
|
||||||
|
\c - - - :worker_1_port
|
||||||
|
SET search_path TO pg18_publication;
|
||||||
|
SELECT pubname, pubgencols
|
||||||
|
FROM pg_publication
|
||||||
|
WHERE pubname IN ('pub_gen_cols_stored', 'pub_gen_cols_none')
|
||||||
|
ORDER BY pubname;
|
||||||
|
pubname | pubgencols
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
pub_gen_cols_none | n
|
||||||
|
pub_gen_cols_stored | s
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- On worker 2: same check
|
||||||
|
\c - - - :worker_2_port
|
||||||
|
SET search_path TO pg18_publication;
|
||||||
|
SELECT pubname, pubgencols
|
||||||
|
FROM pg_publication
|
||||||
|
WHERE pubname IN ('pub_gen_cols_stored', 'pub_gen_cols_none')
|
||||||
|
ORDER BY pubname;
|
||||||
|
pubname | pubgencols
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
pub_gen_cols_none | n
|
||||||
|
pub_gen_cols_stored | s
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- Now verify ALTER PUBLICATION .. SET (publish_generated_columns = none)
|
||||||
|
-- propagates to workers as well.
|
||||||
|
\c - - - :master_port
|
||||||
|
SET search_path TO pg18_publication;
|
||||||
|
ALTER PUBLICATION pub_gen_cols_stored
|
||||||
|
SET (publish_generated_columns = none);
|
||||||
|
-- coordinator: both publications should now have pubgencols = 'n'
|
||||||
|
SELECT pubname, pubgencols
|
||||||
|
FROM pg_publication
|
||||||
|
WHERE pubname IN ('pub_gen_cols_stored', 'pub_gen_cols_none')
|
||||||
|
ORDER BY pubname;
|
||||||
|
pubname | pubgencols
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
pub_gen_cols_none | n
|
||||||
|
pub_gen_cols_stored | n
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- worker 1: pubgencols must match coordinator
|
||||||
|
\c - - - :worker_1_port
|
||||||
|
SET search_path TO pg18_publication;
|
||||||
|
SELECT pubname, pubgencols
|
||||||
|
FROM pg_publication
|
||||||
|
WHERE pubname IN ('pub_gen_cols_stored', 'pub_gen_cols_none')
|
||||||
|
ORDER BY pubname;
|
||||||
|
pubname | pubgencols
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
pub_gen_cols_none | n
|
||||||
|
pub_gen_cols_stored | n
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- worker 2: same check
|
||||||
|
\c - - - :worker_2_port
|
||||||
|
SET search_path TO pg18_publication;
|
||||||
|
SELECT pubname, pubgencols
|
||||||
|
FROM pg_publication
|
||||||
|
WHERE pubname IN ('pub_gen_cols_stored', 'pub_gen_cols_none')
|
||||||
|
ORDER BY pubname;
|
||||||
|
pubname | pubgencols
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
pub_gen_cols_none | n
|
||||||
|
pub_gen_cols_stored | n
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- Column list precedence test: Citus must preserve both prattrs and pubgencols
|
||||||
|
\c - - - :master_port
|
||||||
|
SET search_path TO pg18_publication;
|
||||||
|
-- Case 1: column list explicitly includes the generated column, flag = none
|
||||||
|
CREATE PUBLICATION pub_gen_cols_list_includes_b
|
||||||
|
FOR TABLE gen_pub_tab (id, a, b)
|
||||||
|
WITH (publish_generated_columns = none);
|
||||||
|
-- Case 2: column list excludes the generated column, flag = stored
|
||||||
|
CREATE PUBLICATION pub_gen_cols_list_excludes_b
|
||||||
|
FOR TABLE gen_pub_tab (id, a)
|
||||||
|
WITH (publish_generated_columns = stored);
|
||||||
|
-- Helper: show pubname, pubgencols, and column list (prattrs) for gen_pub_tab
|
||||||
|
SELECT p.pubname,
|
||||||
|
p.pubgencols,
|
||||||
|
r.prattrs
|
||||||
|
FROM pg_publication p
|
||||||
|
JOIN pg_publication_rel r ON p.oid = r.prpubid
|
||||||
|
JOIN pg_class c ON c.oid = r.prrelid
|
||||||
|
WHERE p.pubname IN ('pub_gen_cols_list_includes_b',
|
||||||
|
'pub_gen_cols_list_excludes_b')
|
||||||
|
AND c.relname = 'gen_pub_tab'
|
||||||
|
ORDER BY p.pubname;
|
||||||
|
pubname | pubgencols | prattrs
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
pub_gen_cols_list_excludes_b | s | 1 2
|
||||||
|
pub_gen_cols_list_includes_b | n | 1 2 3
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- worker 1: must see the same pubgencols + prattrs
|
||||||
|
\c - - - :worker_1_port
|
||||||
|
SET search_path TO pg18_publication;
|
||||||
|
SELECT p.pubname,
|
||||||
|
p.pubgencols,
|
||||||
|
r.prattrs
|
||||||
|
FROM pg_publication p
|
||||||
|
JOIN pg_publication_rel r ON p.oid = r.prpubid
|
||||||
|
JOIN pg_class c ON c.oid = r.prrelid
|
||||||
|
WHERE p.pubname IN ('pub_gen_cols_list_includes_b',
|
||||||
|
'pub_gen_cols_list_excludes_b')
|
||||||
|
AND c.relname = 'gen_pub_tab'
|
||||||
|
ORDER BY p.pubname;
|
||||||
|
pubname | pubgencols | prattrs
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
pub_gen_cols_list_excludes_b | s | 1 2
|
||||||
|
pub_gen_cols_list_includes_b | n | 1 2 3
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- worker 2: same check
|
||||||
|
\c - - - :worker_2_port
|
||||||
|
SET search_path TO pg18_publication;
|
||||||
|
SELECT p.pubname,
|
||||||
|
p.pubgencols,
|
||||||
|
r.prattrs
|
||||||
|
FROM pg_publication p
|
||||||
|
JOIN pg_publication_rel r ON p.oid = r.prpubid
|
||||||
|
JOIN pg_class c ON c.oid = r.prrelid
|
||||||
|
WHERE p.pubname IN ('pub_gen_cols_list_includes_b',
|
||||||
|
'pub_gen_cols_list_excludes_b')
|
||||||
|
AND c.relname = 'gen_pub_tab'
|
||||||
|
ORDER BY p.pubname;
|
||||||
|
pubname | pubgencols | prattrs
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
pub_gen_cols_list_excludes_b | s | 1 2
|
||||||
|
pub_gen_cols_list_includes_b | n | 1 2 3
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- back to coordinator for subsequent tests / cleanup
|
||||||
|
\c - - - :master_port
|
||||||
|
SET search_path TO pg18_publication;
|
||||||
|
DROP PUBLICATION pub_gen_cols_stored;
|
||||||
|
DROP PUBLICATION pub_gen_cols_none;
|
||||||
|
DROP SCHEMA pg18_publication CASCADE;
|
||||||
|
NOTICE: drop cascades to table gen_pub_tab
|
||||||
|
SET search_path TO pg18_nn;
|
||||||
|
-- PG18: verify publish_generated_columns is preserved for distributed tables
|
||||||
-- cleanup with minimum verbosity
|
-- cleanup with minimum verbosity
|
||||||
SET client_min_messages TO ERROR;
|
SET client_min_messages TO ERROR;
|
||||||
RESET search_path;
|
RESET search_path;
|
||||||
|
|
|
||||||
|
|
@ -632,6 +632,157 @@ CREATE MATERIALIZED VIEW copytest_mv AS
|
||||||
SELECT create_distributed_table('copytest_mv', 'id');
|
SELECT create_distributed_table('copytest_mv', 'id');
|
||||||
-- After that, any command on the materialized view is outside Citus support.
|
-- After that, any command on the materialized view is outside Citus support.
|
||||||
|
|
||||||
|
-- PG18: verify publish_generated_columns is preserved for distributed tables
|
||||||
|
-- https://github.com/postgres/postgres/commit/7054186c4
|
||||||
|
\c - - - :master_port
|
||||||
|
CREATE SCHEMA pg18_publication;
|
||||||
|
SET search_path TO pg18_publication;
|
||||||
|
|
||||||
|
-- table with a stored generated column
|
||||||
|
CREATE TABLE gen_pub_tab (
|
||||||
|
id int primary key,
|
||||||
|
a int,
|
||||||
|
b int GENERATED ALWAYS AS (a * 10) STORED
|
||||||
|
);
|
||||||
|
|
||||||
|
-- make it distributed so CREATE PUBLICATION goes through Citus metadata/DDL path
|
||||||
|
SELECT create_distributed_table('gen_pub_tab', 'id', colocate_with := 'none');
|
||||||
|
|
||||||
|
-- publication using the new PG18 option: stored
|
||||||
|
CREATE PUBLICATION pub_gen_cols_stored
|
||||||
|
FOR TABLE gen_pub_tab
|
||||||
|
WITH (publish = 'insert, update', publish_generated_columns = stored);
|
||||||
|
|
||||||
|
-- second publication explicitly using "none" for completeness
|
||||||
|
CREATE PUBLICATION pub_gen_cols_none
|
||||||
|
FOR TABLE gen_pub_tab
|
||||||
|
WITH (publish = 'insert, update', publish_generated_columns = none);
|
||||||
|
|
||||||
|
-- On coordinator: pubgencols must be 's' and 'n' respectively
|
||||||
|
SELECT pubname, pubgencols
|
||||||
|
FROM pg_publication
|
||||||
|
WHERE pubname IN ('pub_gen_cols_stored', 'pub_gen_cols_none')
|
||||||
|
ORDER BY pubname;
|
||||||
|
|
||||||
|
-- On worker 1: both publications must exist and keep pubgencols in sync
|
||||||
|
\c - - - :worker_1_port
|
||||||
|
SET search_path TO pg18_publication;
|
||||||
|
|
||||||
|
SELECT pubname, pubgencols
|
||||||
|
FROM pg_publication
|
||||||
|
WHERE pubname IN ('pub_gen_cols_stored', 'pub_gen_cols_none')
|
||||||
|
ORDER BY pubname;
|
||||||
|
|
||||||
|
-- On worker 2: same check
|
||||||
|
\c - - - :worker_2_port
|
||||||
|
SET search_path TO pg18_publication;
|
||||||
|
|
||||||
|
SELECT pubname, pubgencols
|
||||||
|
FROM pg_publication
|
||||||
|
WHERE pubname IN ('pub_gen_cols_stored', 'pub_gen_cols_none')
|
||||||
|
ORDER BY pubname;
|
||||||
|
|
||||||
|
-- Now verify ALTER PUBLICATION .. SET (publish_generated_columns = none)
|
||||||
|
-- propagates to workers as well.
|
||||||
|
|
||||||
|
\c - - - :master_port
|
||||||
|
SET search_path TO pg18_publication;
|
||||||
|
|
||||||
|
ALTER PUBLICATION pub_gen_cols_stored
|
||||||
|
SET (publish_generated_columns = none);
|
||||||
|
|
||||||
|
-- coordinator: both publications should now have pubgencols = 'n'
|
||||||
|
SELECT pubname, pubgencols
|
||||||
|
FROM pg_publication
|
||||||
|
WHERE pubname IN ('pub_gen_cols_stored', 'pub_gen_cols_none')
|
||||||
|
ORDER BY pubname;
|
||||||
|
|
||||||
|
-- worker 1: pubgencols must match coordinator
|
||||||
|
\c - - - :worker_1_port
|
||||||
|
SET search_path TO pg18_publication;
|
||||||
|
|
||||||
|
SELECT pubname, pubgencols
|
||||||
|
FROM pg_publication
|
||||||
|
WHERE pubname IN ('pub_gen_cols_stored', 'pub_gen_cols_none')
|
||||||
|
ORDER BY pubname;
|
||||||
|
|
||||||
|
-- worker 2: same check
|
||||||
|
\c - - - :worker_2_port
|
||||||
|
SET search_path TO pg18_publication;
|
||||||
|
|
||||||
|
SELECT pubname, pubgencols
|
||||||
|
FROM pg_publication
|
||||||
|
WHERE pubname IN ('pub_gen_cols_stored', 'pub_gen_cols_none')
|
||||||
|
ORDER BY pubname;
|
||||||
|
|
||||||
|
-- Column list precedence test: Citus must preserve both prattrs and pubgencols
|
||||||
|
|
||||||
|
\c - - - :master_port
|
||||||
|
SET search_path TO pg18_publication;
|
||||||
|
|
||||||
|
-- Case 1: column list explicitly includes the generated column, flag = none
|
||||||
|
CREATE PUBLICATION pub_gen_cols_list_includes_b
|
||||||
|
FOR TABLE gen_pub_tab (id, a, b)
|
||||||
|
WITH (publish_generated_columns = none);
|
||||||
|
|
||||||
|
-- Case 2: column list excludes the generated column, flag = stored
|
||||||
|
CREATE PUBLICATION pub_gen_cols_list_excludes_b
|
||||||
|
FOR TABLE gen_pub_tab (id, a)
|
||||||
|
WITH (publish_generated_columns = stored);
|
||||||
|
|
||||||
|
-- Helper: show pubname, pubgencols, and column list (prattrs) for gen_pub_tab
|
||||||
|
SELECT p.pubname,
|
||||||
|
p.pubgencols,
|
||||||
|
r.prattrs
|
||||||
|
FROM pg_publication p
|
||||||
|
JOIN pg_publication_rel r ON p.oid = r.prpubid
|
||||||
|
JOIN pg_class c ON c.oid = r.prrelid
|
||||||
|
WHERE p.pubname IN ('pub_gen_cols_list_includes_b',
|
||||||
|
'pub_gen_cols_list_excludes_b')
|
||||||
|
AND c.relname = 'gen_pub_tab'
|
||||||
|
ORDER BY p.pubname;
|
||||||
|
|
||||||
|
-- worker 1: must see the same pubgencols + prattrs
|
||||||
|
\c - - - :worker_1_port
|
||||||
|
SET search_path TO pg18_publication;
|
||||||
|
|
||||||
|
SELECT p.pubname,
|
||||||
|
p.pubgencols,
|
||||||
|
r.prattrs
|
||||||
|
FROM pg_publication p
|
||||||
|
JOIN pg_publication_rel r ON p.oid = r.prpubid
|
||||||
|
JOIN pg_class c ON c.oid = r.prrelid
|
||||||
|
WHERE p.pubname IN ('pub_gen_cols_list_includes_b',
|
||||||
|
'pub_gen_cols_list_excludes_b')
|
||||||
|
AND c.relname = 'gen_pub_tab'
|
||||||
|
ORDER BY p.pubname;
|
||||||
|
|
||||||
|
-- worker 2: same check
|
||||||
|
\c - - - :worker_2_port
|
||||||
|
SET search_path TO pg18_publication;
|
||||||
|
|
||||||
|
SELECT p.pubname,
|
||||||
|
p.pubgencols,
|
||||||
|
r.prattrs
|
||||||
|
FROM pg_publication p
|
||||||
|
JOIN pg_publication_rel r ON p.oid = r.prpubid
|
||||||
|
JOIN pg_class c ON c.oid = r.prrelid
|
||||||
|
WHERE p.pubname IN ('pub_gen_cols_list_includes_b',
|
||||||
|
'pub_gen_cols_list_excludes_b')
|
||||||
|
AND c.relname = 'gen_pub_tab'
|
||||||
|
ORDER BY p.pubname;
|
||||||
|
|
||||||
|
-- back to coordinator for subsequent tests / cleanup
|
||||||
|
\c - - - :master_port
|
||||||
|
SET search_path TO pg18_publication;
|
||||||
|
DROP PUBLICATION pub_gen_cols_stored;
|
||||||
|
DROP PUBLICATION pub_gen_cols_none;
|
||||||
|
DROP PUBLICATION pub_gen_cols_list_includes_b;
|
||||||
|
DROP PUBLICATION pub_gen_cols_list_excludes_b;
|
||||||
|
DROP SCHEMA pg18_publication CASCADE;
|
||||||
|
SET search_path TO pg18_nn;
|
||||||
|
-- END: PG18: verify publish_generated_columns is preserved for distributed tables
|
||||||
|
|
||||||
-- cleanup with minimum verbosity
|
-- cleanup with minimum verbosity
|
||||||
SET client_min_messages TO ERROR;
|
SET client_min_messages TO ERROR;
|
||||||
RESET search_path;
|
RESET search_path;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue