diff --git a/README.md b/README.md index 56220ff..08f0139 100644 --- a/README.md +++ b/README.md @@ -59,4 +59,5 @@ The Postgres_13 branch contains a parser based on the PostgreSQL 13 parser.
The Postgres_14 branch contains a parser based on the PostgreSQL 14 parser.
The Postgres_15 branch contains a parser based on the PostgreSQL 15 parser.
The Postgres_16 branch contains a parser based on the PostgreSQL 16 parser.
-The main branch is based on the PostgreSQL 17 parser. +The Postgres_17 branch contains a parser based on the PostgreSQL 17 parser.
+The main branch is based on the PostgreSQL 18 parser. diff --git a/grammar-to-java/pom.xml b/grammar-to-java/pom.xml index 2f53840..ddf3f21 100644 --- a/grammar-to-java/pom.xml +++ b/grammar-to-java/pom.xml @@ -5,7 +5,7 @@ com.splendiddata pg_sqlparser - 17.0 + 17.2 com.splendiddata.pg_sqlparser grammar-to-java @@ -70,7 +70,7 @@ https://www.gnu.org/licenses/gpl-3.0.en.html https://www.splendiddata.com - Copyright (c) Splendid Data Product Development B.V. 2013 - 2024 + Copyright (c) Splendid Data Product Development B.V. 2013 - 2025 diff --git a/grammar-to-java/src/main/java/com/splendiddata/sqlparser/grammartojava/GrammarRuleSpecial.java b/grammar-to-java/src/main/java/com/splendiddata/sqlparser/grammartojava/GrammarRuleSpecial.java index e76fca5..d1dc95e 100644 --- a/grammar-to-java/src/main/java/com/splendiddata/sqlparser/grammartojava/GrammarRuleSpecial.java +++ b/grammar-to-java/src/main/java/com/splendiddata/sqlparser/grammartojava/GrammarRuleSpecial.java @@ -1,5 +1,5 @@ /* - * Copyright (c) Splendid Data Product Development B.V. 2020 - 2023 + * Copyright (c) Splendid Data Product Development B.V. 2020 - 2025 * * This program is free software: You may redistribute and/or modify under the terms of the GNU General Public License * as published by the Free Software Foundation, either version 3 of the License, or (at Client's option) any later @@ -462,7 +462,7 @@ public String processLine(String line) { * The SplitColQualList method needs replacement */ .replace("SplitColQualList($6, &n->constraints, &n->collClause,", - "n.collClause = splitColQualList($5, n.constraints,"); + "splitColQualList($6, n,"); } }), diff --git a/parser-enums/pom.xml b/parser-enums/pom.xml index 6197bf6..7626d5f 100644 --- a/parser-enums/pom.xml +++ b/parser-enums/pom.xml @@ -5,7 +5,7 @@ com.splendiddata pg_sqlparser - 17.0 + 17.2 com.splendiddata.pg_sqlparser parser-enums @@ -21,7 +21,7 @@ https://www.gnu.org/licenses/gpl-3.0.en.html https://www.splendiddata.com - Copyright (c) Splendid Data Product Development B.V. 2013 - 2024 + Copyright (c) Splendid Data Product Development B.V. 2013 - 2025 diff --git a/parser-test-gui/pom.xml b/parser-test-gui/pom.xml index 21d6d79..c63d156 100644 --- a/parser-test-gui/pom.xml +++ b/parser-test-gui/pom.xml @@ -5,7 +5,7 @@ com.splendiddata pg_sqlparser - 17.0 + 17.2 parser-test-gui com.splendiddata.pg_sqlparser @@ -136,7 +136,7 @@ https://www.gnu.org/licenses/gpl-3.0.en.html https://www.splendiddata.com - Copyright (c) Splendid Data Product Development B.V. 2013 - 2023 + Copyright (c) Splendid Data Product Development B.V. 2013 - 2025 diff --git a/parser/pom.xml b/parser/pom.xml index 305c7af..e5b4e7b 100644 --- a/parser/pom.xml +++ b/parser/pom.xml @@ -5,7 +5,7 @@ com.splendiddata pg_sqlparser - 17.0 + 17.2 com.splendiddata.pg_sqlparser parser @@ -233,7 +233,7 @@ https://www.gnu.org/licenses/gpl-3.0.en.html https://www.splendiddata.com - Copyright (c) Splendid Data Product Development B.V. 2013 - 2024 + Copyright (c) Splendid Data Product Development B.V. 2013 - 2025 diff --git a/parser/src/main/grammar/postgres/src/backend/parser/gram.y b/parser/src/main/grammar/postgres/src/backend/parser/gram.y index 66e4243..f230c5f 100644 --- a/parser/src/main/grammar/postgres/src/backend/parser/gram.y +++ b/parser/src/main/grammar/postgres/src/backend/parser/gram.y @@ -14048,7 +14048,7 @@ xmltable_column_el: parser_errposition(defel->location))); fc->colexpr = defel->arg; } - else if (strcmp(defel->defname, "is_not_null") == 0) + else if (strcmp(defel->defname, "__pg__is_not_null") == 0) { if (nullability_seen) ereport(ERROR, @@ -14091,13 +14091,20 @@ xmltable_column_option_list: xmltable_column_option_el: IDENT b_expr - { $$ = makeDefElem($1, $2, @1); } + { + if (strcmp($1, "__pg__is_not_null") == 0) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("option name \"%s\" cannot be used in XMLTABLE", $1), + parser_errposition(@1))); + $$ = makeDefElem($1, $2, @1); + } | DEFAULT b_expr { $$ = makeDefElem("default", $2, @1); } | NOT NULL_P - { $$ = makeDefElem("is_not_null", (Node *) makeBoolean(true), @1); } + { $$ = makeDefElem("__pg__is_not_null", (Node *) makeBoolean(true), @1); } | NULL_P - { $$ = makeDefElem("is_not_null", (Node *) makeBoolean(false), @1); } + { $$ = makeDefElem("__pg__is_not_null", (Node *) makeBoolean(false), @1); } | PATH b_expr { $$ = makeDefElem("path", $2, @1); } ; @@ -19510,4 +19517,4 @@ void parser_init(base_yy_extra_type *yyext) { yyext->parsetree = NIL; /* in case grammar forgets to set it */ -} \ No newline at end of file +} diff --git a/parser/src/main/java/com/splendiddata/sqlparser/AbstractParser.java b/parser/src/main/java/com/splendiddata/sqlparser/AbstractParser.java index 9194980..6cb3629 100644 --- a/parser/src/main/java/com/splendiddata/sqlparser/AbstractParser.java +++ b/parser/src/main/java/com/splendiddata/sqlparser/AbstractParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) Splendid Data Product Development B.V. 2020 - 2023 + * Copyright (c) Splendid Data Product Development B.V. 2020 - 2025 * * This program is free software: You may redistribute and/or modify under the terms of the GNU General Public License * as published by the Free Software Foundation, either version 3 of the License, or (at Client's option) any later @@ -1474,46 +1474,6 @@ protected static int pg_strcasecmp(String s1, String s2) { return s1.compareToIgnoreCase(s2); } - /** - * Separate Constraint nodes from COLLATE clauses in a ColQualList - *

- * Inspired upon postgresql-16beta1/src/backend/parser/gram.c - * - * @param qualList - * A list that may contain Constraints and a CollateClause - * @param constraintList - * The Constraints from the qualList - * @param yyscanner2 - * @return CollateClause The CollateClause from the qualList or null if there isn't any - * @since Postgres 16 - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - protected CollateClause splitColQualList(List qualList, List constraintList, core_yyscan_t yyscanner) { - if (qualList == null) { - return null; - } - CollateClause collClause = null; - for (Node n : (List) qualList) { - if (n instanceof Constraint) { - constraintList.add((Constraint) n); - } else if (n instanceof CollateClause) { - if (collClause == null) { - collClause = (CollateClause) n; - } else { - - ereport(Severity.ERROR, ErrCode.ERRCODE_SYNTAX_ERROR, - errmsg("multiple COLLATE clauses not allowed"), parser_errposition(n.location)); - } - } else if (n instanceof DefElem) { - log.debug(() -> "splitColQualList ignores DefElem: " + ParserUtil.stmtToXml(n)); - } else { - ereport(Severity.ERROR, ErrCode.ERRCODE_SYNTAX_ERROR, Severity.ERROR, - errmsg("unexpected node type " + n.type), parser_errposition(n.location)); - } - } - return collClause; - } - /** * makeJsonValueExpr - creates a JsonValueExpr node *

diff --git a/parser/src/main/java/com/splendiddata/sqlparser/structure/CreateStatsStmt.java b/parser/src/main/java/com/splendiddata/sqlparser/structure/CreateStatsStmt.java index 6181480..0ffe0ec 100644 --- a/parser/src/main/java/com/splendiddata/sqlparser/structure/CreateStatsStmt.java +++ b/parser/src/main/java/com/splendiddata/sqlparser/structure/CreateStatsStmt.java @@ -1,5 +1,5 @@ /* - * Copyright (c) Splendid Data Product Development B.V. 2020 - 2023 + * Copyright (c) Splendid Data Product Development B.V. 2020 - 2025 * * This program is free software: You may redistribute and/or modify under the terms of the GNU General Public License * as published by the Free Software Foundation, either version 3 of the License, or (at Client's option) any later @@ -145,7 +145,7 @@ public String toString() { } if (relations != null) { String separator = " from "; - for (RangeVar relation : relations) { + for (Node relation : relations) { result.append(separator).append(relation); separator = ", "; } diff --git a/parser/src/main/java/com/splendiddata/sqlparser/structure/DefineStmt.java b/parser/src/main/java/com/splendiddata/sqlparser/structure/DefineStmt.java index 724d5c7..88c6cb8 100644 --- a/parser/src/main/java/com/splendiddata/sqlparser/structure/DefineStmt.java +++ b/parser/src/main/java/com/splendiddata/sqlparser/structure/DefineStmt.java @@ -231,6 +231,9 @@ public String toString() { result.append(" = ").append(def.arg); } break; + case "category": + result.append(separator).append(def.defname.toLowerCase()).append(" = '").append(def.arg).append("'"); + break; default: result.append(separator).append('"').append(def.defname.toLowerCase()).append('"'); if (def.arg != null) { diff --git a/parser/src/main/java/com/splendiddata/sqlparser/structure/FuncCall.java b/parser/src/main/java/com/splendiddata/sqlparser/structure/FuncCall.java index cb066c6..4a8d2cc 100644 --- a/parser/src/main/java/com/splendiddata/sqlparser/structure/FuncCall.java +++ b/parser/src/main/java/com/splendiddata/sqlparser/structure/FuncCall.java @@ -203,13 +203,12 @@ public String toString() { } if (over != null) { - result.append(" over ("); + result.append(" over "); if (over.name == null) { - result.append(over); + result.append('(').append(over).append(')'); } else { result.append(over.name); } - result.append(')'); } return result.toString(); diff --git a/parser/src/main/scanner/postgres/src/backend/parser/scan.l b/parser/src/main/scanner/postgres/src/backend/parser/scan.l index b9cf949..c97db94 100644 --- a/parser/src/main/scanner/postgres/src/backend/parser/scan.l +++ b/parser/src/main/scanner/postgres/src/backend/parser/scan.l @@ -1492,4 +1492,4 @@ core_yyfree(void *ptr, core_yyscan_t yyscanner) { if (ptr) pfree(ptr); -} \ No newline at end of file +} diff --git a/parser/src/test/resources/postgres/test/regress/sql/aggregates.sql b/parser/src/test/resources/postgres/test/regress/sql/aggregates.sql index b2341b8..2ffa882 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/aggregates.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/aggregates.sql @@ -604,6 +604,25 @@ explain (costs off) select sum(two order by two) from tenk1; reset enable_presorted_aggregate; +-- +-- Test cases with FILTER clause +-- + +-- Ensure we presort when the aggregate contains plain Vars +explain (costs off) +select sum(two order by two) filter (where two > 1) from tenk1; + +-- Ensure we presort for RelabelType'd Vars +explain (costs off) +select string_agg(distinct f1, ',') filter (where length(f1) > 1) +from varchar_tbl; + +-- Ensure we don't presort when the aggregate's argument contains an +-- explicit cast. +explain (costs off) +select string_agg(distinct f1::varchar(2), ',') filter (where length(f1) > 1) +from varchar_tbl; + -- -- Test combinations of DISTINCT and/or ORDER BY -- @@ -814,11 +833,16 @@ select * from v_pagg_test order by y; -- Ensure parallel aggregation is actually being used. explain (costs off) select * from v_pagg_test order by y; -set max_parallel_workers_per_gather = 0; - -- Ensure results are the same without parallel aggregation. +set max_parallel_workers_per_gather = 0; select * from v_pagg_test order by y; +-- Check that we don't fail on anonymous record types. +set max_parallel_workers_per_gather = 2; +explain (costs off) +select array_dims(array_agg(s)) from (select * from pagg_test) s; +select array_dims(array_agg(s)) from (select * from pagg_test) s; + -- Clean up reset max_parallel_workers_per_gather; reset bytea_output; diff --git a/parser/src/test/resources/postgres/test/regress/sql/alter_table.sql b/parser/src/test/resources/postgres/test/regress/sql/alter_table.sql index 689c740..460d988 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/alter_table.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/alter_table.sql @@ -1503,8 +1503,6 @@ select conname, obj_description(oid, 'pg_constraint') as desc alter table at_partitioned alter column name type varchar(127); --- Note: these tests currently show the wrong behavior for comments :-( - select relname, c.oid = oldoid as orig_oid, case relfilenode @@ -2189,13 +2187,15 @@ SELECT conname as constraint, obj_description(oid, 'pg_constraint') as comment F -- filenode function call can return NULL for a relation dropped concurrently -- with the call's surrounding query, so ignore a NULL mapped_oid for -- relations that no longer exist after all calls finish. +-- Temporary relations are ignored, as not supported by pg_filenode_relation(). CREATE TEMP TABLE filenode_mapping AS SELECT oid, mapped_oid, reltablespace, relfilenode, relname FROM pg_class, pg_filenode_relation(reltablespace, pg_relation_filenode(oid)) AS mapped_oid -WHERE relkind IN ('r', 'i', 'S', 't', 'm') AND mapped_oid IS DISTINCT FROM oid; - +WHERE relkind IN ('r', 'i', 'S', 't', 'm') + AND relpersistence != 't' + AND mapped_oid IS DISTINCT FROM oid; SELECT m.* FROM filenode_mapping m LEFT JOIN pg_class c ON c.oid = m.oid WHERE c.oid IS NOT NULL OR m.mapped_oid IS NOT NULL; @@ -2316,6 +2316,14 @@ ALTER TABLE test_add_column \d test_add_column ALTER TABLE test_add_column ADD COLUMN IF NOT EXISTS c5 SERIAL CHECK (c5 > 10); +ALTER TABLE test_add_column + ADD c6 integer; -- omit COLUMN +ALTER TABLE test_add_column + ADD IF NOT EXISTS c6 integer; +ALTER TABLE test_add_column + DROP c6; -- omit COLUMN +ALTER TABLE test_add_column + DROP IF EXISTS c6; \d test_add_column* DROP TABLE test_add_column; \d test_add_column* @@ -2432,6 +2440,13 @@ CREATE TABLE parent (LIKE list_parted); CREATE TABLE child () INHERITS (parent); ALTER TABLE list_parted ATTACH PARTITION child FOR VALUES IN (1); ALTER TABLE list_parted ATTACH PARTITION parent FOR VALUES IN (1); +DROP TABLE child; +-- now it should work, with a little tweak +ALTER TABLE parent ADD CONSTRAINT check_a CHECK (a > 0); +ALTER TABLE list_parted ATTACH PARTITION parent FOR VALUES IN (1); +-- test insert/update, per bug #18550 +INSERT INTO parent VALUES (1); +UPDATE parent SET a = 2 WHERE a = 1; DROP TABLE parent CASCADE; -- check any TEMP-ness @@ -2799,6 +2814,15 @@ ALTER TABLE range_parted2 DETACH PARTITION part_rp100 CONCURRENTLY; \d part_rp100 DROP TABLE range_parted2; +-- Test that hash partitions continue to work after they're concurrently +-- detached (bugs #18371, #19070) +CREATE TABLE hash_parted2 (a int) PARTITION BY HASH(a); +CREATE TABLE part_hp PARTITION OF hash_parted2 FOR VALUES WITH (MODULUS 2, REMAINDER 0); +ALTER TABLE hash_parted2 DETACH PARTITION part_hp CONCURRENTLY; +DROP TABLE hash_parted2; +INSERT INTO part_hp VALUES (1); +DROP TABLE part_hp; + -- Check ALTER TABLE commands for partitioned tables and partitions -- cannot add/drop column to/from *only* the parent @@ -3029,6 +3053,23 @@ drop table attbl, atref; /* End test case for bug #17409 */ +/* Test case for bug #18970 */ + +create table attbl(a int); +create table atref(b attbl check ((b).a is not null)); +alter table attbl alter column a type numeric; -- someday this should work +alter table atref drop constraint atref_b_check; + +create statistics atref_stat on ((b).a is not null) from atref; +alter table attbl alter column a type numeric; -- someday this should work +drop statistics atref_stat; + +create index atref_idx on atref (((b).a)); +alter table attbl alter column a type numeric; -- someday this should work +drop table attbl, atref; + +/* End test case for bug #18970 */ + -- Test that ALTER TABLE rewrite preserves a clustered index -- for normal indexes and indexes on constraints. create table alttype_cluster (a int); diff --git a/parser/src/test/resources/postgres/test/regress/sql/arrays.sql b/parser/src/test/resources/postgres/test/regress/sql/arrays.sql index 50aa539..339eee7 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/arrays.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/arrays.sql @@ -447,6 +447,8 @@ reset enable_bitmapscan; insert into arr_pk_tbl values(10, '[-2147483648:-2147483647]={1,2}'); update arr_pk_tbl set f1[2147483647] = 42 where pk = 10; update arr_pk_tbl set f1[2147483646:2147483647] = array[4,2] where pk = 10; +insert into arr_pk_tbl(pk, f1[0:2147483647]) values (2, '{}'); +insert into arr_pk_tbl(pk, f1[-2147483648:2147483647]) values (2, '{}'); -- also exercise the expanded-array case do $$ declare a int[]; @@ -526,6 +528,10 @@ select '[2147483646:2147483646]={1}'::int[]; select '[-2147483648:-2147483647]={1,2}'::int[]; -- all of the above should be accepted +-- some day we might allow these cases, but for now they're errors: +select array[]::oidvector; +select array[]::int2vector; + -- tests for array aggregates CREATE TEMP TABLE arraggtest ( f1 INT[], f2 TEXT[][], f3 FLOAT[]); @@ -711,6 +717,28 @@ select array_replace(array['AB',NULL,'CDE'],NULL,'12'); select array(select array[i,i/2] from generate_series(1,5) i); select array(select array['Hello', i::text] from generate_series(9,11) i); +-- int2vector and oidvector should be treated as scalar types for this purpose +select pg_typeof(array(select '11 22 33'::int2vector from generate_series(1,5))); +select array(select '11 22 33'::int2vector from generate_series(1,5)); +select unnest(array(select '11 22 33'::int2vector from generate_series(1,5))); +select pg_typeof(array(select '11 22 33'::oidvector from generate_series(1,5))); +select array(select '11 22 33'::oidvector from generate_series(1,5)); +select unnest(array(select '11 22 33'::oidvector from generate_series(1,5))); + +-- array[] should do the same +select pg_typeof(array['11 22 33'::int2vector]); +select array['11 22 33'::int2vector]; +select pg_typeof(unnest(array['11 22 33'::int2vector])); +select unnest(array['11 22 33'::int2vector]); +select pg_typeof(unnest('11 22 33'::int2vector)); +select unnest('11 22 33'::int2vector); +select pg_typeof(array['11 22 33'::oidvector]); +select array['11 22 33'::oidvector]; +select pg_typeof(unnest(array['11 22 33'::oidvector])); +select unnest(array['11 22 33'::oidvector]); +select pg_typeof(unnest('11 22 33'::oidvector)); +select unnest('11 22 33'::oidvector); + -- Insert/update on a column that is array of composite create temp table t1 (f1 int8_tbl[]); diff --git a/parser/src/test/resources/postgres/test/regress/sql/case.sql b/parser/src/test/resources/postgres/test/regress/sql/case.sql index 83fe43b..388d4c6 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/case.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/case.sql @@ -242,6 +242,11 @@ SELECT CASE make_ad(1,2) WHEN array[1,2]::arrdomain THEN 'right' END; +-- While we're here, also test handling of a NULLIF arg that is a read/write +-- object (bug #18722) + +SELECT NULLIF(make_ad(1,2), array[2,3]::arrdomain); + ROLLBACK; -- Test interaction of CASE with ArrayCoerceExpr (bug #15471) diff --git a/parser/src/test/resources/postgres/test/regress/sql/collate.icu.utf8.sql b/parser/src/test/resources/postgres/test/regress/sql/collate.icu.utf8.sql index 99a5727..b9155f7 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/collate.icu.utf8.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/collate.icu.utf8.sql @@ -29,7 +29,7 @@ CREATE TABLE collate_test1 ( b text COLLATE "en-x-icu" NOT NULL ); --- Deactivated for SplendidDataTest: \d collate_test1 +\d collate_test1 CREATE TABLE collate_test_fail ( a int, @@ -50,7 +50,7 @@ CREATE TABLE collate_test_like ( LIKE collate_test1 ); --- Deactivated for SplendidDataTest: \d collate_test_like +\d collate_test_like CREATE TABLE collate_test2 ( a int, @@ -446,8 +446,8 @@ CREATE INDEX collate_dep_test4i ON collate_dep_test4t (b COLLATE test0); DROP COLLATION test0 RESTRICT; -- fail DROP COLLATION test0 CASCADE; --- Deactivated for SplendidDataTest: \d collate_dep_test1 --- Deactivated for SplendidDataTest: \d collate_dep_test2 +\d collate_dep_test1 +\d collate_dep_test2 DROP TABLE collate_dep_test1, collate_dep_test4t; DROP TYPE collate_dep_test2; @@ -516,7 +516,6 @@ DROP TABLE test7; CREATE COLLATION testcoll_rulesx (provider = icu, locale = '', rules = '!!wrong!!'); - -- nondeterministic collations CREATE COLLATION ctest_det (provider = icu, locale = '', deterministic = true); @@ -581,6 +580,109 @@ CREATE UNIQUE INDEX ON test3cs (x); -- ok SELECT string_to_array('ABC,DEF,GHI' COLLATE case_sensitive, ',', 'abc'); SELECT string_to_array('ABCDEFGHI' COLLATE case_sensitive, NULL, 'b'); +-- +-- A unique index under one collation does not prove uniqueness under +-- another, so the planner must not use such a proof for any optimization. +-- + +-- Ensure that we do not use inner-unique join execution +EXPLAIN (VERBOSE, COSTS OFF) +SELECT * FROM test1cs t1, test3cs t2 +WHERE t1.x = t2.x COLLATE case_insensitive +ORDER BY 1, 2; + +SELECT * FROM test1cs t1, test3cs t2 +WHERE t1.x = t2.x COLLATE case_insensitive +ORDER BY 1, 2; + +-- Ensure that left-join is not removed +EXPLAIN (COSTS OFF) +SELECT t1.* FROM test3cs t1 + LEFT JOIN test3cs t2 ON t1.x = t2.x COLLATE case_insensitive +ORDER BY 1; + +SELECT t1.* FROM test3cs t1 + LEFT JOIN test3cs t2 ON t1.x = t2.x COLLATE case_insensitive +ORDER BY 1; + +-- Ensure that self-join is not removed +EXPLAIN (COSTS OFF) +SELECT * FROM test3cs t1, test3cs t2 +WHERE t1.x = t2.x COLLATE case_insensitive +ORDER BY 1, 2; + +SELECT * FROM test3cs t1, test3cs t2 +WHERE t1.x = t2.x COLLATE case_insensitive +ORDER BY 1, 2; + +-- Ensure that semijoin is not reduced to innerjoin +EXPLAIN (COSTS OFF) +SELECT * FROM test3cs t1 + WHERE EXISTS (SELECT 1 FROM test3cs t2 WHERE t1.x = t2.x COLLATE case_insensitive) +ORDER BY 1; + +SELECT * FROM test3cs t1 + WHERE EXISTS (SELECT 1 FROM test3cs t2 WHERE t1.x = t2.x COLLATE case_insensitive) +ORDER BY 1; + +-- +-- A DISTINCT / GROUP BY / set-op on a subquery does not prove uniqueness +-- under a different collation, so the planner must not use such a proof for +-- any optimization. +-- + +-- Ensure that we do not use inner-unique join execution +EXPLAIN (VERBOSE, COSTS OFF) +SELECT * FROM test1cs t1, (SELECT DISTINCT x FROM test3cs) t2 +WHERE t1.x = t2.x COLLATE case_insensitive +ORDER BY 1, 2; + +SELECT * FROM test1cs t1, (SELECT DISTINCT x FROM test3cs) t2 +WHERE t1.x = t2.x COLLATE case_insensitive +ORDER BY 1, 2; + +-- Same with GROUP BY +EXPLAIN (VERBOSE, COSTS OFF) +SELECT * FROM test1cs t1, (SELECT x FROM test3cs GROUP BY x) t2 +WHERE t1.x = t2.x COLLATE case_insensitive +ORDER BY 1, 2; + +SELECT * FROM test1cs t1, (SELECT x FROM test3cs GROUP BY x) t2 +WHERE t1.x = t2.x COLLATE case_insensitive +ORDER BY 1, 2; + +-- Same with set-op +EXPLAIN (VERBOSE, COSTS OFF) +SELECT * FROM test1cs t1, (SELECT x FROM test3cs UNION SELECT x FROM test3cs) t2 +WHERE t1.x = t2.x COLLATE case_insensitive +ORDER BY 1, 2; + +SELECT * FROM test1cs t1, (SELECT x FROM test3cs UNION SELECT x FROM test3cs) t2 +WHERE t1.x = t2.x COLLATE case_insensitive +ORDER BY 1, 2; + +-- Ensure that left-join is not removed +EXPLAIN (COSTS OFF) +SELECT t1.* FROM test3cs t1 + LEFT JOIN (SELECT DISTINCT x FROM test3cs) t2 ON t1.x = t2.x COLLATE case_insensitive +ORDER BY 1; + +SELECT t1.* FROM test3cs t1 + LEFT JOIN (SELECT DISTINCT x FROM test3cs) t2 ON t1.x = t2.x COLLATE case_insensitive +ORDER BY 1; + +-- Ensure that semijoin is not reduced to innerjoin +EXPLAIN (COSTS OFF) +SELECT * FROM test3cs t1 + WHERE EXISTS (SELECT 1 FROM (SELECT DISTINCT x FROM test3cs) t2 + WHERE t1.x = t2.x COLLATE case_insensitive) +ORDER BY 1; + +SELECT * FROM test3cs t1 + WHERE EXISTS (SELECT 1 FROM (SELECT DISTINCT x FROM test3cs) t2 + WHERE t1.x = t2.x COLLATE case_insensitive) +ORDER BY 1; + CREATE TABLE test1ci (x text COLLATE case_insensitive); CREATE TABLE test2ci (x text COLLATE case_insensitive); CREATE TABLE test3ci (x text COLLATE case_insensitive); @@ -804,6 +906,79 @@ INSERT INTO test33 VALUES (2, 'DEF'); -- they end up in the same partition (but it's platform-dependent which one) SELECT (SELECT count(*) FROM test33_0) <> (SELECT count(*) FROM test33_1); +-- +-- Bug #18568 +-- +-- Partitionwise aggregate (full or partial) should not be used when a +-- partition key's collation doesn't match that of the GROUP BY column it is +-- matched with. +SET max_parallel_workers_per_gather TO 0; +SET enable_incremental_sort TO off; + +CREATE TABLE pagg_tab3 (a text, c text collate case_insensitive) PARTITION BY LIST(c collate "C"); +CREATE TABLE pagg_tab3_p1 PARTITION OF pagg_tab3 FOR VALUES IN ('a', 'b'); +CREATE TABLE pagg_tab3_p2 PARTITION OF pagg_tab3 FOR VALUES IN ('B', 'A'); +INSERT INTO pagg_tab3 SELECT i % 4 + 1, substr('abAB', (i % 4) + 1 , 1) FROM generate_series(0, 19) i; +ANALYZE pagg_tab3; + +SET enable_partitionwise_aggregate TO false; +EXPLAIN (COSTS OFF) +SELECT upper(c collate case_insensitive), count(c) FROM pagg_tab3 GROUP BY c collate case_insensitive ORDER BY 1; +SELECT upper(c collate case_insensitive), count(c) FROM pagg_tab3 GROUP BY c collate case_insensitive ORDER BY 1; + +-- No "full" partitionwise aggregation allowed, though "partial" is allowed. +SET enable_partitionwise_aggregate TO true; +EXPLAIN (COSTS OFF) +SELECT upper(c collate case_insensitive), count(c) FROM pagg_tab3 GROUP BY c collate case_insensitive ORDER BY 1; +SELECT upper(c collate case_insensitive), count(c) FROM pagg_tab3 GROUP BY c collate case_insensitive ORDER BY 1; + +-- OK to use full partitionwise aggregate after changing the GROUP BY column's +-- collation to be the same as that of the partition key. +EXPLAIN (COSTS OFF) +SELECT c collate "C", count(c) FROM pagg_tab3 GROUP BY c collate "C" ORDER BY 1; +SELECT c collate "C", count(c) FROM pagg_tab3 GROUP BY c collate "C" ORDER BY 1; + +-- Partitionwise join should not be allowed too when the collation used by the +-- join keys doesn't match the partition key collation. +SET enable_partitionwise_join TO false; +EXPLAIN (COSTS OFF) +SELECT t1.c, count(t2.c) FROM pagg_tab3 t1 JOIN pagg_tab3 t2 ON t1.c = t2.c GROUP BY 1 ORDER BY t1.c COLLATE "C"; +SELECT t1.c, count(t2.c) FROM pagg_tab3 t1 JOIN pagg_tab3 t2 ON t1.c = t2.c GROUP BY 1 ORDER BY t1.c COLLATE "C"; + +SET enable_partitionwise_join TO true; +EXPLAIN (COSTS OFF) +SELECT t1.c, count(t2.c) FROM pagg_tab3 t1 JOIN pagg_tab3 t2 ON t1.c = t2.c GROUP BY 1 ORDER BY t1.c COLLATE "C"; +SELECT t1.c, count(t2.c) FROM pagg_tab3 t1 JOIN pagg_tab3 t2 ON t1.c = t2.c GROUP BY 1 ORDER BY t1.c COLLATE "C"; + +-- OK when the join clause uses the same collation as the partition key. +EXPLAIN (COSTS OFF) +SELECT t1.c COLLATE "C", count(t2.c) FROM pagg_tab3 t1 JOIN pagg_tab3 t2 ON t1.c = t2.c COLLATE "C" GROUP BY t1.c COLLATE "C" ORDER BY t1.c COLLATE "C"; +SELECT t1.c COLLATE "C", count(t2.c) FROM pagg_tab3 t1 JOIN pagg_tab3 t2 ON t1.c = t2.c COLLATE "C" GROUP BY t1.c COLLATE "C" ORDER BY t1.c COLLATE "C"; + +SET enable_partitionwise_join TO false; +EXPLAIN (COSTS OFF) +SELECT t1.c COLLATE "C", count(t2.c) FROM pagg_tab3 t1 JOIN pagg_tab3 t2 ON t1.c = t2.c COLLATE "C" GROUP BY t1.c COLLATE "C" ORDER BY t1.c COLLATE "C"; +SELECT t1.c COLLATE "C", count(t2.c) FROM pagg_tab3 t1 JOIN pagg_tab3 t2 ON t1.c = t2.c COLLATE "C" GROUP BY t1.c COLLATE "C" ORDER BY t1.c COLLATE "C"; + +DROP TABLE pagg_tab3; + +RESET enable_partitionwise_aggregate; +RESET max_parallel_workers_per_gather; +RESET enable_incremental_sort; + +-- Check that DEFAULT expressions in SQL/JSON functions use the same collation +-- as the RETURNING type. Mismatched collations should raise an error. +CREATE DOMAIN d1 AS text COLLATE case_insensitive; +CREATE DOMAIN d2 AS text COLLATE "C"; +SELECT JSON_VALUE('{"a": "A"}', '$.a' RETURNING d1 DEFAULT ('C' COLLATE "C") COLLATE case_insensitive ON EMPTY) = 'a'; -- true +SELECT JSON_VALUE('{"a": "A"}', '$.a' RETURNING d1 DEFAULT 'C' ON EMPTY) = 'a'; -- true +SELECT JSON_VALUE('{"a": "A"}', '$.a' RETURNING d1 DEFAULT 'C'::d2 ON EMPTY) = 'a'; -- error +SELECT JSON_VALUE('{"a": "A"}', '$.a' RETURNING d1 DEFAULT 'C' COLLATE "C" ON EMPTY) = 'a'; -- error +SELECT JSON_VALUE('{"a": "A"}', '$.c' RETURNING d1 DEFAULT 'A' ON EMPTY) = 'a'; -- true +SELECT JSON_VALUE('{"a": "A"}', '$.c' RETURNING d1 DEFAULT 'A' COLLATE case_insensitive ON EMPTY) = 'a'; -- true +SELECT JSON_VALUE('{"a": "A"}', '$.c' RETURNING d1 DEFAULT 'A'::d2 ON EMPTY) = 'a'; -- error +SELECT JSON_VALUE('{"a": "A"}', '$.c' RETURNING d1 DEFAULT 'A' COLLATE "C" ON EMPTY) = 'a'; -- error +DROP DOMAIN d1, d2; -- cleanup RESET search_path; diff --git a/parser/src/test/resources/postgres/test/regress/sql/compression_pglz.sql b/parser/src/test/resources/postgres/test/regress/sql/compression_pglz.sql new file mode 100644 index 0000000..dbd37f7 --- /dev/null +++ b/parser/src/test/resources/postgres/test/regress/sql/compression_pglz.sql @@ -0,0 +1,63 @@ +-- +-- Tests for PGLZ compression +-- + +-- directory paths and dlsuffix are passed to us in environment variables +\getenv libdir PG_LIBDIR +\getenv dlsuffix PG_DLSUFFIX + +\set regresslib :libdir '/regress' :dlsuffix + +CREATE FUNCTION test_pglz_compress(bytea) + RETURNS bytea + AS :'regresslib' LANGUAGE C STRICT; +CREATE FUNCTION test_pglz_decompress(bytea, int4, bool) + RETURNS bytea + AS :'regresslib' LANGUAGE C STRICT; + +-- Round-trip with pglz: compress then decompress. +SELECT test_pglz_decompress(test_pglz_compress( + decode(repeat('abcd', 100), 'escape')), 400, false) = + decode(repeat('abcd', 100), 'escape') AS roundtrip_ok; +SELECT test_pglz_decompress(test_pglz_compress( + decode(repeat('abcd', 100), 'escape')), 400, true) = + decode(repeat('abcd', 100), 'escape') AS roundtrip_ok; + +-- Decompression with rawsize too large, fails to fill the destination +-- buffer. +SELECT test_pglz_decompress(test_pglz_compress( + decode(repeat('abcd', 100), 'escape')), 500, true); + +-- Decompression with rawsize too small, fails with source not fully +-- consumed. +SELECT test_pglz_decompress(test_pglz_compress( + decode(repeat('abcd', 100), 'escape')), 100, true); + +-- Corrupted compressed data. Set control bit with read of a match tag, +-- no data follows. +SELECT length(test_pglz_decompress('\x01'::bytea, 1024, false)) AS ctrl_only_len; +SELECT test_pglz_decompress('\x01'::bytea, 1024, true); + +-- Corrupted compressed data. Set control bit with read of a match tag, +-- 1 byte follows. +SELECT test_pglz_decompress('\x01ff'::bytea, 1024, false); +SELECT test_pglz_decompress('\x01ff'::bytea, 1024, true); + +-- Corrupted compressed data. Set control bit with match tag where length +-- nibble is 3 bytes (extended length), no data follows. +SELECT test_pglz_decompress('\x010f01'::bytea, 1024, false); +SELECT test_pglz_decompress('\x010f01'::bytea, 1024, true); + +-- Corrupted compressed data. Set control bit with a valid 2-byte match +-- tag where offset exceeds output written. +SELECT test_pglz_decompress('\x011001'::bytea, 1024, false); +SELECT test_pglz_decompress('\x011001'::bytea, 1024, true); + +-- Corrupted compressed data. Set control bit with a valid 2-byte match +-- tag where offset is 0. +SELECT test_pglz_decompress('\x010300'::bytea, 1024, false); +SELECT test_pglz_decompress('\x010300'::bytea, 1024, true); + +-- Clean up +DROP FUNCTION test_pglz_compress; +DROP FUNCTION test_pglz_decompress; diff --git a/parser/src/test/resources/postgres/test/regress/sql/constraints.sql b/parser/src/test/resources/postgres/test/regress/sql/constraints.sql index 697d398..3c3d6c0 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/constraints.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/constraints.sql @@ -17,7 +17,7 @@ -- -- directory paths are passed to us in environment variables --- Deactivated for SplendidDataTest: \getenv abs_srcdir PG_ABS_SRCDIR +\getenv abs_srcdir PG_ABS_SRCDIR -- -- DEFAULT syntax @@ -457,6 +457,47 @@ ALTER TABLE parted_fk_naming ATTACH PARTITION parted_fk_naming_1 FOR VALUES IN ( SELECT conname FROM pg_constraint WHERE conrelid = 'parted_fk_naming_1'::regclass AND contype = 'f'; DROP TABLE parted_fk_naming; +-- +-- Test various ways to create primary keys on partitions, linked to unique +-- indexes (without constraints) on the partitioned table. Ideally these should +-- fail, but we don't dare change released behavior, so instead cope with it at +-- DETACH time. +CREATE TEMP TABLE t (a integer, b integer) PARTITION BY HASH (a, b); +CREATE TEMP TABLE tp (a integer, b integer, PRIMARY KEY (a, b), UNIQUE (b, a)); +ALTER TABLE t ATTACH PARTITION tp FOR VALUES WITH (MODULUS 1, REMAINDER 0); +CREATE UNIQUE INDEX t_a_idx ON t (a, b); +CREATE UNIQUE INDEX t_b_idx ON t (b, a); +ALTER INDEX t_a_idx ATTACH PARTITION tp_pkey; +ALTER INDEX t_b_idx ATTACH PARTITION tp_b_a_key; +SELECT conname, conparentid, conislocal, coninhcount + FROM pg_constraint WHERE conname IN ('tp_pkey', 'tp_b_a_key') + ORDER BY conname DESC; +ALTER TABLE t DETACH PARTITION tp; +DROP TABLE t, tp; + +CREATE TEMP TABLE t (a integer) PARTITION BY LIST (a); +CREATE TEMP TABLE tp (a integer PRIMARY KEY); +CREATE UNIQUE INDEX t_a_idx ON t (a); +ALTER TABLE t ATTACH PARTITION tp FOR VALUES IN (1); +ALTER TABLE t DETACH PARTITION tp; +DROP TABLE t, tp; + +CREATE TEMP TABLE t (a integer) PARTITION BY LIST (a); +CREATE TEMP TABLE tp (a integer PRIMARY KEY); +CREATE UNIQUE INDEX t_a_idx ON ONLY t (a); +ALTER TABLE t ATTACH PARTITION tp FOR VALUES IN (1); +ALTER TABLE t DETACH PARTITION tp; +DROP TABLE t, tp; + +CREATE TABLE regress_constr_partitioned (a integer) PARTITION BY LIST (a); +CREATE TABLE regress_constr_partition1 PARTITION OF regress_constr_partitioned FOR VALUES IN (1); +ALTER TABLE regress_constr_partition1 ADD PRIMARY KEY (a); +CREATE UNIQUE INDEX ON regress_constr_partitioned (a); +BEGIN; +ALTER TABLE regress_constr_partitioned DETACH PARTITION regress_constr_partition1; +ROLLBACK; +-- Leave this one in funny state for pg_upgrade testing + -- test a HOT update that invalidates the conflicting tuple. -- the trigger should still fire and catch the violation @@ -599,3 +640,14 @@ DROP DOMAIN constraint_comments_dom; DROP ROLE regress_constraint_comments; DROP ROLE regress_constraint_comments_noaccess; + +-- Leave some constraints for the pg_upgrade test to pick up +CREATE DOMAIN constraint_comments_dom AS int; + +ALTER DOMAIN constraint_comments_dom ADD CONSTRAINT inv_ck CHECK (value > 0) NOT VALID; +COMMENT ON CONSTRAINT inv_ck ON DOMAIN constraint_comments_dom IS 'comment on invalid constraint'; + +-- Create a table that exercises pg_upgrade +CREATE TABLE regress_notnull1 (a integer); +CREATE TABLE regress_notnull2 () INHERITS (regress_notnull1); +ALTER TABLE ONLY regress_notnull2 ALTER COLUMN a SET NOT NULL; diff --git a/parser/src/test/resources/postgres/test/regress/sql/conversion.sql b/parser/src/test/resources/postgres/test/regress/sql/conversion.sql index 9a65fca..a80d623 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/conversion.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/conversion.sql @@ -8,6 +8,11 @@ \set regresslib :libdir '/regress' :dlsuffix +CREATE FUNCTION test_enc_setup() RETURNS void + AS :'regresslib', 'test_enc_setup' + LANGUAGE C STRICT; +SELECT FROM test_enc_setup(); + CREATE FUNCTION test_enc_conversion(bytea, name, name, bool, validlen OUT int, result OUT bytea) AS :'regresslib', 'test_enc_conversion' LANGUAGE C STRICT; @@ -295,11 +300,14 @@ insert into gb18030_inputs values ('\x666f6f84309c38', 'valid, translates to UTF-8 by mapping function'), ('\x666f6f84309c', 'incomplete char '), ('\x666f6f84309c0a', 'incomplete char, followed by newline '), + ('\x666f6f84', 'incomplete char at end'), ('\x666f6f84309c3800', 'invalid, NUL byte'), ('\x666f6f84309c0038', 'invalid, NUL byte'); --- Test GB18030 verification -select description, inbytes, (test_conv(inbytes, 'gb18030', 'gb18030')).* from gb18030_inputs; +-- Test GB18030 verification. Round-trip through text so the backing of the +-- bytea values is palloc, not shared_buffers. This lets Valgrind detect +-- reads past the end. +select description, inbytes, (test_conv(inbytes::text::bytea, 'gb18030', 'gb18030')).* from gb18030_inputs; -- Test conversions from GB18030 select description, inbytes, (test_conv(inbytes, 'gb18030', 'utf8')).* from gb18030_inputs; diff --git a/parser/src/test/resources/postgres/test/regress/sql/copy2.sql b/parser/src/test/resources/postgres/test/regress/sql/copy2.sql index d732a20..7ddbac5 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/copy2.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/copy2.sql @@ -37,18 +37,18 @@ FOR EACH ROW EXECUTE PROCEDURE fn_x_before(); COPY x (a, b, c, d, e) from stdin; -- Deactivated for SplendidDataTest: 9999 \N \\N \NN \N -- Deactivated for SplendidDataTest: 10000 21 31 41 51 --- Deactivated for SplendidDataTest: \. +\. COPY x (b, d) from stdin; -- Deactivated for SplendidDataTest: 1 test_1 --- Deactivated for SplendidDataTest: \. +\. COPY x (b, d) from stdin; -- Deactivated for SplendidDataTest: 2 test_2 -- Deactivated for SplendidDataTest: 3 test_3 -- Deactivated for SplendidDataTest: 4 test_4 -- Deactivated for SplendidDataTest: 5 test_5 --- Deactivated for SplendidDataTest: \. +\. COPY x (a, b, c, d, e) from stdin; -- Deactivated for SplendidDataTest: 10001 22 32 42 52 @@ -56,7 +56,7 @@ COPY x (a, b, c, d, e) from stdin; -- Deactivated for SplendidDataTest: 10003 24 34 44 54 -- Deactivated for SplendidDataTest: 10004 25 35 45 55 -- Deactivated for SplendidDataTest: 10005 26 36 46 56 --- Deactivated for SplendidDataTest: \. +\. -- non-existent column in column list: should fail COPY x (xyz) from stdin; @@ -78,18 +78,24 @@ COPY x from stdin (on_error ignore, on_error ignore); COPY x from stdin (log_verbosity default, log_verbosity verbose); -- incorrect options -COPY x to stdin (format BINARY, delimiter ','); -COPY x to stdin (format BINARY, null 'x'); +COPY x from stdin (format BINARY, delimiter ','); +COPY x from stdin (format BINARY, null 'x'); COPY x from stdin (format BINARY, on_error ignore); COPY x from stdin (on_error unsupported); -COPY x to stdin (format TEXT, force_quote(a)); +COPY x from stdin (format TEXT, force_quote(a)); +COPY x from stdin (format TEXT, force_quote *); COPY x from stdin (format CSV, force_quote(a)); -COPY x to stdout (format TEXT, force_not_null(a)); -COPY x to stdin (format CSV, force_not_null(a)); -COPY x to stdout (format TEXT, force_null(a)); -COPY x to stdin (format CSV, force_null(a)); -COPY x to stdin (format BINARY, on_error unsupported); -COPY x to stdout (log_verbosity unsupported); +COPY x from stdin (format CSV, force_quote *); +COPY x from stdin (format TEXT, force_not_null(a)); +COPY x from stdin (format TEXT, force_not_null *); +COPY x to stdout (format CSV, force_not_null(a)); +COPY x to stdout (format CSV, force_not_null *); +COPY x from stdin (format TEXT, force_null(a)); +COPY x from stdin (format TEXT, force_null *); +COPY x to stdout (format CSV, force_null(a)); +COPY x to stdout (format CSV, force_null *); +COPY x to stdout (format BINARY, on_error unsupported); +COPY x from stdin (log_verbosity unsupported); -- too many columns in column list: should fail COPY x (a, b, c, d, e, d, c) from stdin; @@ -97,29 +103,29 @@ COPY x (a, b, c, d, e, d, c) from stdin; -- missing data: should fail COPY x from stdin; --- Deactivated for SplendidDataTest: \. +\. COPY x from stdin; -- Deactivated for SplendidDataTest: 2000 230 23 23 --- Deactivated for SplendidDataTest: \. +\. COPY x from stdin; -- Deactivated for SplendidDataTest: 2001 231 \N \N --- Deactivated for SplendidDataTest: \. +\. -- extra data: should fail COPY x from stdin; -- Deactivated for SplendidDataTest: 2002 232 40 50 60 70 80 --- Deactivated for SplendidDataTest: \. +\. -- various COPY options: delimiters, oids, NULL string, encoding COPY x (b, c, d, e) from stdin delimiter ',' null 'x'; -- Deactivated for SplendidDataTest: x,45,80,90 -- Deactivated for SplendidDataTest: x,\x,\\x,\\\x -- Deactivated for SplendidDataTest: x,\,,\\\,,\\ --- Deactivated for SplendidDataTest: \. +\. COPY x from stdin WITH DELIMITER AS ';' NULL AS ''; -- Deactivated for SplendidDataTest: 3000;;c;; --- Deactivated for SplendidDataTest: \. +\. COPY x from stdin WITH DELIMITER AS ':' NULL AS E'\\X' ENCODING 'sql_ascii'; -- Deactivated for SplendidDataTest: 4000:\X:C:\X:\X @@ -131,14 +137,14 @@ COPY x from stdin WITH DELIMITER AS ':' NULL AS E'\\X' ENCODING 'sql_ascii'; -- Deactivated for SplendidDataTest: 4006:6:BackslashN:\\N:\\N -- Deactivated for SplendidDataTest: 4007:7:XX:\XX:\XX -- Deactivated for SplendidDataTest: 4008:8:Delimiter:\::\: --- Deactivated for SplendidDataTest: \. +\. -- Deactivated for SplendidDataTest: COPY x TO stdout WHERE a = 1; COPY x from stdin WHERE a = 50004; -- Deactivated for SplendidDataTest: 50003 24 34 44 54 -- Deactivated for SplendidDataTest: 50004 25 35 45 55 -- Deactivated for SplendidDataTest: 50005 26 36 46 56 --- Deactivated for SplendidDataTest: \. +\. COPY x from stdin WHERE a > 60003; -- Deactivated for SplendidDataTest: 60001 22 32 42 52 @@ -146,7 +152,7 @@ COPY x from stdin WHERE a > 60003; -- Deactivated for SplendidDataTest: 60003 24 34 44 54 -- Deactivated for SplendidDataTest: 60004 25 35 45 55 -- Deactivated for SplendidDataTest: 60005 26 36 46 56 --- Deactivated for SplendidDataTest: \. +\. COPY x from stdin WHERE f > 60003; @@ -188,10 +194,10 @@ COPY y TO stdout (FORMAT CSV, QUOTE '''', DELIMITER '|'); COPY y TO stdout (FORMAT CSV, FORCE_QUOTE (col2), ESCAPE E'\\'); COPY y TO stdout (FORMAT CSV, FORCE_QUOTE *); --- Deactivated for SplendidDataTest: \copy y TO stdout (FORMAT CSV) --- Deactivated for SplendidDataTest: \copy y TO stdout (FORMAT CSV, QUOTE '''', DELIMITER '|') --- Deactivated for SplendidDataTest: \copy y TO stdout (FORMAT CSV, FORCE_QUOTE (col2), ESCAPE E'\\') --- Deactivated for SplendidDataTest: \copy y TO stdout (FORMAT CSV, FORCE_QUOTE *) +\copy y TO stdout (FORMAT CSV) +\copy y TO stdout (FORMAT CSV, QUOTE '''', DELIMITER '|') +\copy y TO stdout (FORMAT CSV, FORCE_QUOTE (col2), ESCAPE E'\\') +\copy y TO stdout (FORMAT CSV, FORCE_QUOTE *) --test that we read consecutive LFs properly @@ -201,17 +207,17 @@ COPY testnl FROM stdin CSV; -- Deactivated for SplendidDataTest: 1,"a field with two LFs -- Deactivated for SplendidDataTest: inside",2 --- Deactivated for SplendidDataTest: \. +\. -- test end of copy marker CREATE TEMP TABLE testeoc (a text); COPY testeoc FROM stdin CSV; -- Deactivated for SplendidDataTest: a\. --- Deactivated for SplendidDataTest: \.b +\.b -- Deactivated for SplendidDataTest: c\.d -- Deactivated for SplendidDataTest: "\." --- Deactivated for SplendidDataTest: \. +\. COPY testeoc TO stdout CSV; @@ -224,8 +230,8 @@ COPY testnull TO stdout WITH NULL AS E'\\0'; COPY testnull FROM stdin WITH NULL AS E'\\0'; -- Deactivated for SplendidDataTest: 42 \\0 --- Deactivated for SplendidDataTest: \0 \0 --- Deactivated for SplendidDataTest: \. +\0 \0 +\. SELECT * FROM testnull; @@ -234,7 +240,7 @@ CREATE TABLE vistest (LIKE testeoc); COPY vistest FROM stdin CSV; -- Deactivated for SplendidDataTest: a0 -- Deactivated for SplendidDataTest: b --- Deactivated for SplendidDataTest: \. +\. COMMIT; SELECT * FROM vistest; BEGIN; @@ -242,14 +248,14 @@ TRUNCATE vistest; COPY vistest FROM stdin CSV; -- Deactivated for SplendidDataTest: a1 -- Deactivated for SplendidDataTest: b --- Deactivated for SplendidDataTest: \. +\. SELECT * FROM vistest; SAVEPOINT s1; TRUNCATE vistest; COPY vistest FROM stdin CSV; -- Deactivated for SplendidDataTest: d1 -- Deactivated for SplendidDataTest: e --- Deactivated for SplendidDataTest: \. +\. SELECT * FROM vistest; COMMIT; SELECT * FROM vistest; @@ -259,14 +265,14 @@ TRUNCATE vistest; COPY vistest FROM stdin CSV FREEZE; -- Deactivated for SplendidDataTest: a2 -- Deactivated for SplendidDataTest: b --- Deactivated for SplendidDataTest: \. +\. SELECT * FROM vistest; SAVEPOINT s1; TRUNCATE vistest; COPY vistest FROM stdin CSV FREEZE; -- Deactivated for SplendidDataTest: d2 -- Deactivated for SplendidDataTest: e --- Deactivated for SplendidDataTest: \. +\. SELECT * FROM vistest; COMMIT; SELECT * FROM vistest; @@ -276,21 +282,21 @@ TRUNCATE vistest; COPY vistest FROM stdin CSV FREEZE; -- Deactivated for SplendidDataTest: x -- Deactivated for SplendidDataTest: y --- Deactivated for SplendidDataTest: \. +\. SELECT * FROM vistest; COMMIT; TRUNCATE vistest; COPY vistest FROM stdin CSV FREEZE; -- Deactivated for SplendidDataTest: p -- Deactivated for SplendidDataTest: g --- Deactivated for SplendidDataTest: \. +\. BEGIN; TRUNCATE vistest; SAVEPOINT s1; COPY vistest FROM stdin CSV FREEZE; -- Deactivated for SplendidDataTest: m -- Deactivated for SplendidDataTest: k --- Deactivated for SplendidDataTest: \. +\. COMMIT; BEGIN; INSERT INTO vistest VALUES ('z'); @@ -300,7 +306,7 @@ ROLLBACK TO SAVEPOINT s1; COPY vistest FROM stdin CSV FREEZE; -- Deactivated for SplendidDataTest: d3 -- Deactivated for SplendidDataTest: e --- Deactivated for SplendidDataTest: \. +\. COMMIT; CREATE FUNCTION truncate_in_subxact() RETURNS VOID AS $$ @@ -317,7 +323,7 @@ SELECT truncate_in_subxact(); COPY vistest FROM stdin CSV FREEZE; -- Deactivated for SplendidDataTest: d4 -- Deactivated for SplendidDataTest: e --- Deactivated for SplendidDataTest: \. +\. SELECT * FROM vistest; COMMIT; SELECT * FROM vistest; @@ -329,26 +335,26 @@ CREATE TEMP TABLE forcetest ( d TEXT, e TEXT ); --- Deactivated for SplendidDataTest: \pset null NULL +\pset null NULL -- should succeed with no effect ("b" remains an empty string, "c" remains NULL) BEGIN; COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(b), FORCE_NULL(c)); -- Deactivated for SplendidDataTest: 1,,"" --- Deactivated for SplendidDataTest: \. +\. COMMIT; SELECT b, c FROM forcetest WHERE a = 1; -- should succeed, FORCE_NULL and FORCE_NOT_NULL can be both specified BEGIN; COPY forcetest (a, b, c, d) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL(c,d), FORCE_NULL(c,d)); -- Deactivated for SplendidDataTest: 2,'a',,"" --- Deactivated for SplendidDataTest: \. +\. COMMIT; SELECT c, d FROM forcetest WHERE a = 2; -- should fail with not-null constraint violation BEGIN; COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NULL(b), FORCE_NOT_NULL(c)); -- Deactivated for SplendidDataTest: 3,,"" --- Deactivated for SplendidDataTest: \. +\. ROLLBACK; -- should fail with "not referenced by COPY" error BEGIN; @@ -362,21 +368,21 @@ ROLLBACK; BEGIN; COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL *, FORCE_NULL *); -- Deactivated for SplendidDataTest: 4,,"" --- Deactivated for SplendidDataTest: \. +\. COMMIT; SELECT b, c FROM forcetest WHERE a = 4; -- should succeed with effect ("b" remains an empty string) BEGIN; COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NOT_NULL *); -- Deactivated for SplendidDataTest: 5,,"" --- Deactivated for SplendidDataTest: \. +\. COMMIT; SELECT b, c FROM forcetest WHERE a = 5; -- should succeed with effect ("c" remains NULL) BEGIN; COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NULL *); -- Deactivated for SplendidDataTest: 6,"b","" --- Deactivated for SplendidDataTest: \. +\. COMMIT; SELECT b, c FROM forcetest WHERE a = 6; -- should fail with "conflicting or redundant options" error @@ -388,7 +394,7 @@ BEGIN; COPY forcetest (a, b, c) FROM STDIN WITH (FORMAT csv, FORCE_NULL *, FORCE_NULL(b)); ROLLBACK; --- Deactivated for SplendidDataTest: \pset null '' +\pset null '' -- test case with whole-row Var in a check constraint create table check_con_tbl (f1 int); @@ -398,14 +404,14 @@ begin return $1.f1 > 0; end $$ language plpgsql immutable; alter table check_con_tbl add check (check_con_function(check_con_tbl.*)); --- Deactivated for SplendidDataTest: \d+ check_con_tbl +\d+ check_con_tbl copy check_con_tbl from stdin; -- Deactivated for SplendidDataTest: 1 --- Deactivated for SplendidDataTest: \N --- Deactivated for SplendidDataTest: \. +\N +\. copy check_con_tbl from stdin; -- Deactivated for SplendidDataTest: 0 --- Deactivated for SplendidDataTest: \. +\. select * from check_con_tbl; -- test with RLS enabled. @@ -418,7 +424,7 @@ COPY rls_t1 (a, b, c) from stdin; -- Deactivated for SplendidDataTest: 2 3 2 -- Deactivated for SplendidDataTest: 3 2 3 -- Deactivated for SplendidDataTest: 4 1 4 --- Deactivated for SplendidDataTest: \. +\. CREATE POLICY p1 ON rls_t1 FOR SELECT USING (a % 2 = 0); ALTER TABLE rls_t1 ENABLE ROW LEVEL SECURITY; @@ -474,7 +480,7 @@ CREATE VIEW instead_of_insert_tbl_view AS SELECT ''::text AS str; COPY instead_of_insert_tbl_view FROM stdin; -- fail -- Deactivated for SplendidDataTest: test1 --- Deactivated for SplendidDataTest: \. +\. CREATE FUNCTION fun_instead_of_insert_tbl() RETURNS trigger AS $$ BEGIN @@ -488,7 +494,7 @@ CREATE TRIGGER trig_instead_of_insert_tbl_view COPY instead_of_insert_tbl_view FROM stdin; -- Deactivated for SplendidDataTest: test1 --- Deactivated for SplendidDataTest: \. +\. SELECT * FROM instead_of_insert_tbl; @@ -503,7 +509,7 @@ CREATE TRIGGER trig_instead_of_insert_tbl_view_2 COPY instead_of_insert_tbl_view_2 FROM stdin; -- Deactivated for SplendidDataTest: test1 --- Deactivated for SplendidDataTest: \. +\. SELECT * FROM instead_of_insert_tbl; COMMIT; @@ -517,10 +523,10 @@ COPY check_ign_err FROM STDIN WITH (on_error stop); -- Deactivated for SplendidDataTest: 4 {a, 4} 4 -- Deactivated for SplendidDataTest: 5 {5} 5 --- Deactivated for SplendidDataTest: \. +\. -- want context for notices --- Deactivated for SplendidDataTest: \set SHOW_CONTEXT always +\set SHOW_CONTEXT always COPY check_ign_err FROM STDIN WITH (on_error ignore, log_verbosity verbose); -- Deactivated for SplendidDataTest: 1 {1} 1 @@ -532,7 +538,7 @@ COPY check_ign_err FROM STDIN WITH (on_error ignore, log_verbosity verbose); -- Deactivated for SplendidDataTest: 6 a -- Deactivated for SplendidDataTest: 7 {7} a -- Deactivated for SplendidDataTest: 8 {8} 8 --- Deactivated for SplendidDataTest: \. +\. -- tests for on_error option with log_verbosity and null constraint via domain CREATE DOMAIN dcheck_ign_err2 varchar(15) NOT NULL; @@ -540,10 +546,10 @@ CREATE TABLE check_ign_err2 (n int, m int[], k int, l dcheck_ign_err2); COPY check_ign_err2 FROM STDIN WITH (on_error ignore, log_verbosity verbose); -- Deactivated for SplendidDataTest: 1 {1} 1 'foo' -- Deactivated for SplendidDataTest: 2 {2} 2 \N --- Deactivated for SplendidDataTest: \. +\. -- reset context choice --- Deactivated for SplendidDataTest: \set SHOW_CONTEXT errors +\set SHOW_CONTEXT errors SELECT * FROM check_ign_err; @@ -553,17 +559,17 @@ SELECT * FROM check_ign_err2; CREATE TABLE hard_err(foo widget); COPY hard_err FROM STDIN WITH (on_error ignore); -- Deactivated for SplendidDataTest: 1 --- Deactivated for SplendidDataTest: \. +\. -- test missing data: should fail COPY check_ign_err FROM STDIN WITH (on_error ignore); -- Deactivated for SplendidDataTest: 1 {1} --- Deactivated for SplendidDataTest: \. +\. -- test extra data: should fail COPY check_ign_err FROM STDIN WITH (on_error ignore); -- Deactivated for SplendidDataTest: 1 {1} 3 abc --- Deactivated for SplendidDataTest: \. +\. -- clean up DROP TABLE forcetest; @@ -598,7 +604,7 @@ create temp table copy_default ( copy copy_default from stdin; -- Deactivated for SplendidDataTest: 1 value '2022-07-04' -- Deactivated for SplendidDataTest: 2 \D '2022-07-05' --- Deactivated for SplendidDataTest: \. +\. select id, text_value, ts_value from copy_default; @@ -607,7 +613,7 @@ truncate copy_default; copy copy_default from stdin with (format csv); -- Deactivated for SplendidDataTest: 1,value,2022-07-04 -- Deactivated for SplendidDataTest: 2,\D,2022-07-05 --- Deactivated for SplendidDataTest: \. +\. select id, text_value, ts_value from copy_default; @@ -631,21 +637,21 @@ copy copy_default from stdin with (default '\N'); -- cannot use DEFAULT marker in column that has no DEFAULT value copy copy_default from stdin with (default '\D'); --- Deactivated for SplendidDataTest: \D value '2022-07-04' +\D value '2022-07-04' -- Deactivated for SplendidDataTest: 2 \D '2022-07-05' --- Deactivated for SplendidDataTest: \. +\. copy copy_default from stdin with (format csv, default '\D'); --- Deactivated for SplendidDataTest: \D,value,2022-07-04 +\D,value,2022-07-04 -- Deactivated for SplendidDataTest: 2,\D,2022-07-05 --- Deactivated for SplendidDataTest: \. +\. -- The DEFAULT marker must be unquoted and unescaped or it's not recognized copy copy_default from stdin with (default '\D'); -- Deactivated for SplendidDataTest: 1 \D '2022-07-04' -- Deactivated for SplendidDataTest: 2 \\D '2022-07-04' -- Deactivated for SplendidDataTest: 3 "\D" '2022-07-04' --- Deactivated for SplendidDataTest: \. +\. select id, text_value, ts_value from copy_default; @@ -655,7 +661,7 @@ copy copy_default from stdin with (format csv, default '\D'); -- Deactivated for SplendidDataTest: 1,\D,2022-07-04 -- Deactivated for SplendidDataTest: 2,\\D,2022-07-04 -- Deactivated for SplendidDataTest: 3,"\D",2022-07-04 --- Deactivated for SplendidDataTest: \. +\. select id, text_value, ts_value from copy_default; @@ -666,7 +672,7 @@ copy copy_default from stdin with (default '\D'); -- Deactivated for SplendidDataTest: 1 value '2022-07-04' -- Deactivated for SplendidDataTest: 2 \D '2022-07-03' -- Deactivated for SplendidDataTest: 3 \D \D --- Deactivated for SplendidDataTest: \. +\. select id, text_value, ts_value from copy_default; @@ -676,7 +682,7 @@ copy copy_default from stdin with (format csv, default '\D'); -- Deactivated for SplendidDataTest: 1,value,2022-07-04 -- Deactivated for SplendidDataTest: 2,\D,2022-07-03 -- Deactivated for SplendidDataTest: 3,\D,\D --- Deactivated for SplendidDataTest: \. +\. select id, text_value, ts_value from copy_default; diff --git a/parser/src/test/resources/postgres/test/regress/sql/copydml.sql b/parser/src/test/resources/postgres/test/regress/sql/copydml.sql index 4578342..b7eeb0e 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/copydml.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/copydml.sql @@ -66,6 +66,10 @@ create rule qqq as on delete to copydml_test where old.t <> 'f' do instead inser copy (delete from copydml_test) to stdout; drop rule qqq on copydml_test; +create rule qqq as on insert to copydml_test do instead notify copydml_test; +copy (insert into copydml_test default values) to stdout; +drop rule qqq on copydml_test; + -- triggers create function qqq_trig() returns trigger as $$ begin diff --git a/parser/src/test/resources/postgres/test/regress/sql/create_am.sql b/parser/src/test/resources/postgres/test/regress/sql/create_am.sql index 0f595f7..6ec2161 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/create_am.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/create_am.sql @@ -225,6 +225,16 @@ ALTER MATERIALIZED VIEW heapmv SET ACCESS METHOD heap, SET ACCESS METHOD heap2; DROP MATERIALIZED VIEW heapmv; DROP TABLE heaptable; +-- Partitioned table with USING +CREATE TABLE am_partitioned(x INT, y INT) PARTITION BY hash (x) USING heap2; +SELECT pg_describe_object(classid, objid, objsubid) AS obj, + pg_describe_object(refclassid, refobjid, refobjsubid) as refobj + FROM pg_depend, pg_am + WHERE pg_depend.refclassid = 'pg_am'::regclass + AND pg_am.oid = pg_depend.refobjid + AND pg_depend.objid = 'am_partitioned'::regclass; +DROP TABLE am_partitioned; + -- Partition hierarchies with access methods BEGIN; SET LOCAL default_table_access_method = 'heap'; diff --git a/parser/src/test/resources/postgres/test/regress/sql/create_index.sql b/parser/src/test/resources/postgres/test/regress/sql/create_index.sql index 41c353f..f26ef1b 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/create_index.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/create_index.sql @@ -12,7 +12,7 @@ -- -- directory paths are passed to us in environment variables --- Deactivated for SplendidDataTest: \getenv abs_srcdir PG_ABS_SRCDIR +\getenv abs_srcdir PG_ABS_SRCDIR -- -- BTREE @@ -53,6 +53,10 @@ CREATE INDEX six ON shighway USING btree (name text_ops); COMMENT ON INDEX six_wrong IS 'bad index'; COMMENT ON INDEX six IS 'good index'; COMMENT ON INDEX six IS NULL; +SELECT obj_description('six'::regclass, 'pg_class') IS NULL AS six_comment_is_null; +COMMENT ON INDEX six IS 'add the comment back'; +COMMENT ON INDEX six IS ''; -- empty string removes the comment, same as NULL +SELECT obj_description('six'::regclass, 'pg_class') IS NULL AS six_comment_is_null; -- -- BTREE partial indices @@ -78,7 +82,7 @@ CREATE TABLE fast_emp4000 ( home_base box ); --- Deactivated for SplendidDataTest: \set filename :abs_srcdir '/data/rect.data' +\set filename :abs_srcdir '/data/rect.data' -- Deactivated for SplendidDataTest: COPY slow_emp4000 FROM :'filename'; INSERT INTO fast_emp4000 SELECT * FROM slow_emp4000; @@ -276,7 +280,7 @@ CREATE TABLE array_index_op_test ( t text[] ); --- Deactivated for SplendidDataTest: \set filename :abs_srcdir '/data/array.data' +\set filename :abs_srcdir '/data/array.data' -- Deactivated for SplendidDataTest: COPY array_index_op_test FROM :'filename'; ANALYZE array_index_op_test; @@ -362,7 +366,7 @@ DROP TABLE array_gin_test; -- CREATE INDEX gin_relopts_test ON array_index_op_test USING gin (i) WITH (FASTUPDATE=on, GIN_PENDING_LIST_LIMIT=128); --- Deactivated for SplendidDataTest: \d+ gin_relopts_test +\d+ gin_relopts_test -- -- HASH @@ -412,9 +416,9 @@ DELETE FROM unique_tbl WHERE t = 'seven'; CREATE UNIQUE INDEX unique_idx4 ON unique_tbl (i) NULLS NOT DISTINCT; -- ok now --- Deactivated for SplendidDataTest: \d unique_tbl --- Deactivated for SplendidDataTest: \d unique_idx3 --- Deactivated for SplendidDataTest: \d unique_idx4 +\d unique_tbl +\d unique_idx3 +\d unique_idx4 SELECT pg_get_indexdef('unique_idx3'::regclass); SELECT pg_get_indexdef('unique_idx4'::regclass); @@ -436,8 +440,8 @@ INSERT INTO func_index_heap VALUES('ABCD', 'EF'); INSERT INTO func_index_heap VALUES('QWERTY'); -- while we're here, see that the metadata looks sane --- Deactivated for SplendidDataTest: \d func_index_heap --- Deactivated for SplendidDataTest: \d func_index_index +\d func_index_heap +\d func_index_index -- @@ -456,8 +460,8 @@ INSERT INTO func_index_heap VALUES('ABCD', 'EF'); INSERT INTO func_index_heap VALUES('QWERTY'); -- while we're here, see that the metadata looks sane --- Deactivated for SplendidDataTest: \d func_index_heap --- Deactivated for SplendidDataTest: \d func_index_index +\d func_index_heap +\d func_index_index -- this should fail because of unsafe column type (anonymous record) create index on func_index_heap ((f1 || f2), (row(f1, f2))); @@ -533,9 +537,9 @@ VACUUM FULL concur_heap; REINDEX TABLE concur_heap; DELETE FROM concur_heap WHERE f1 = 'b'; VACUUM FULL concur_heap; --- Deactivated for SplendidDataTest: \d concur_heap +\d concur_heap REINDEX TABLE concur_heap; --- Deactivated for SplendidDataTest: \d concur_heap +\d concur_heap -- Temporary tables with concurrent builds and on-commit actions -- CONCURRENTLY used with CREATE INDEX and DROP INDEX is ignored. @@ -581,7 +585,7 @@ DROP INDEX CONCURRENTLY "concur_index5"; DROP INDEX CONCURRENTLY "concur_index1"; DROP INDEX CONCURRENTLY "concur_heap_expr_idx"; --- Deactivated for SplendidDataTest: \d concur_heap +\d concur_heap DROP TABLE concur_heap; @@ -598,16 +602,16 @@ INSERT INTO cwi_test VALUES(1, 2), (3, 4), (5, 6); CREATE UNIQUE INDEX cwi_uniq_idx ON cwi_test(a , b); ALTER TABLE cwi_test ADD primary key USING INDEX cwi_uniq_idx; --- Deactivated for SplendidDataTest: \d cwi_test --- Deactivated for SplendidDataTest: \d cwi_uniq_idx +\d cwi_test +\d cwi_uniq_idx CREATE UNIQUE INDEX cwi_uniq2_idx ON cwi_test(b , a); ALTER TABLE cwi_test DROP CONSTRAINT cwi_uniq_idx, ADD CONSTRAINT cwi_replaced_pkey PRIMARY KEY USING INDEX cwi_uniq2_idx; --- Deactivated for SplendidDataTest: \d cwi_test --- Deactivated for SplendidDataTest: \d cwi_replaced_pkey +\d cwi_test +\d cwi_replaced_pkey DROP INDEX cwi_replaced_pkey; -- Should fail; a constraint depends on it @@ -637,7 +641,7 @@ DROP TABLE cwi_test; CREATE TABLE syscol_table (a INT); -- System columns cannot be indexed -CREATE INDEX ON syscolcol_table (ctid); +CREATE INDEX ON syscol_table (ctid); -- nor used in expressions CREATE INDEX ON syscol_table ((ctid >= '(1000,0)')); @@ -877,9 +881,9 @@ explain (costs off) -- REINDEX (VERBOSE) -- CREATE TABLE reindex_verbose(id integer primary key); --- Deactivated for SplendidDataTest: \set VERBOSITY terse \\ -- suppress machine-dependent details +\set VERBOSITY terse \\ -- suppress machine-dependent details REINDEX (VERBOSE) TABLE reindex_verbose; --- Deactivated for SplendidDataTest: \set VERBOSITY default +\set VERBOSITY default DROP TABLE reindex_verbose; -- @@ -976,7 +980,7 @@ CREATE INDEX concur_appclass_ind on concur_appclass_tab CREATE INDEX concur_appclass_ind_2 on concur_appclass_tab USING gist (k tsvector_ops (siglen='300'), j tsvector_ops); REINDEX TABLE CONCURRENTLY concur_appclass_tab; --- Deactivated for SplendidDataTest: \d concur_appclass_tab +\d concur_appclass_tab DROP TABLE concur_appclass_tab; -- Partitions @@ -1146,7 +1150,7 @@ REINDEX SCHEMA CONCURRENTLY pg_catalog; REINDEX DATABASE not_current_database; -- Check the relation status, there should not be invalid indexes --- Deactivated for SplendidDataTest: \d concur_reindex_tab +\d concur_reindex_tab DROP MATERIALIZED VIEW concur_reindex_matview; DROP TABLE concur_reindex_tab, concur_reindex_tab2, concur_reindex_tab3; @@ -1158,16 +1162,16 @@ CREATE UNIQUE INDEX CONCURRENTLY concur_reindex_ind5 ON concur_reindex_tab4 (c1) -- Reindexing concurrently this index fails with the same failure. -- The extra index created is itself invalid, and can be dropped. REINDEX INDEX CONCURRENTLY concur_reindex_ind5; --- Deactivated for SplendidDataTest: \d concur_reindex_tab4 +\d concur_reindex_tab4 DROP INDEX concur_reindex_ind5_ccnew; -- This makes the previous failure go away, so the index can become valid. DELETE FROM concur_reindex_tab4 WHERE c1 = 1; -- The invalid index is not processed when running REINDEX TABLE. REINDEX TABLE CONCURRENTLY concur_reindex_tab4; --- Deactivated for SplendidDataTest: \d concur_reindex_tab4 +\d concur_reindex_tab4 -- But it is fixed with REINDEX INDEX. REINDEX INDEX CONCURRENTLY concur_reindex_ind5; --- Deactivated for SplendidDataTest: \d concur_reindex_tab4 +\d concur_reindex_tab4 DROP TABLE concur_reindex_tab4; -- Check handling of indexes with expressions and predicates. The diff --git a/parser/src/test/resources/postgres/test/regress/sql/create_role.sql b/parser/src/test/resources/postgres/test/regress/sql/create_role.sql index 4491a28..b22f9c6 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/create_role.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/create_role.sql @@ -92,6 +92,13 @@ CREATE ROLE regress_hasprivs CREATEROLE LOGIN INHERIT CONNECTION LIMIT 5; -- ok, we should be able to modify a role we created COMMENT ON ROLE regress_hasprivs IS 'some comment'; +SELECT shobj_description('regress_hasprivs'::regrole, 'pg_authid') IS NOT NULL AS has_comment; +COMMENT ON ROLE regress_hasprivs IS NULL; +SELECT shobj_description('regress_hasprivs'::regrole, 'pg_authid') IS NULL AS no_comment; +COMMENT ON ROLE regress_hasprivs IS 'add the comment back'; +SELECT shobj_description('regress_hasprivs'::regrole, 'pg_authid') IS NOT NULL AS has_comment; +COMMENT ON ROLE regress_hasprivs IS ''; -- empty string removes the comment, same as NULL +SELECT shobj_description('regress_hasprivs'::regrole, 'pg_authid') IS NULL AS no_comment; ALTER ROLE regress_hasprivs RENAME TO regress_tenant; ALTER ROLE regress_tenant NOINHERIT NOLOGIN CONNECTION LIMIT 7; diff --git a/parser/src/test/resources/postgres/test/regress/sql/create_table.sql b/parser/src/test/resources/postgres/test/regress/sql/create_table.sql index 77e9f80..fc88e26 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/create_table.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/create_table.sql @@ -70,6 +70,14 @@ CREATE TABLE withoid() WITH (oids = true); CREATE TEMP TABLE withoutoid() WITHOUT OIDS; DROP TABLE withoutoid; CREATE TEMP TABLE withoutoid() WITH (oids = false); DROP TABLE withoutoid; +-- temporary tables are ignored by pg_filenode_relation(). +CREATE TEMP TABLE relation_filenode_check(c1 int); +SELECT relpersistence, + pg_filenode_relation (reltablespace, pg_relation_filenode(oid)) + FROM pg_class + WHERE relname = 'relation_filenode_check'; +DROP TABLE relation_filenode_check; + -- check restriction with default expressions -- invalid use of column reference in default expressions CREATE TABLE default_expr_column (id int DEFAULT (id)); diff --git a/parser/src/test/resources/postgres/test/regress/sql/create_table_like.sql b/parser/src/test/resources/postgres/test/regress/sql/create_table_like.sql index 04008a0..7c7206c 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/create_table_like.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/create_table_like.sql @@ -223,3 +223,29 @@ DROP SEQUENCE ctlseq1; DROP TYPE ctlty1; DROP VIEW ctlv1; DROP TABLE IF EXISTS ctlt4, ctlt10, ctlt11, ctlt11a, ctlt12; + +-- LIKE ... INCLUDING STATISTICS with dropped columns in the parent, +-- so stxkeys attnums are not contiguous. +CREATE TABLE ctl_stats3_parent (a int, b int, c int); +ALTER TABLE ctl_stats3_parent DROP COLUMN b; +CREATE STATISTICS ctl_stats3_stat ON a, c FROM ctl_stats3_parent; +CREATE TABLE ctl_stats3_child (LIKE ctl_stats3_parent INCLUDING STATISTICS); +CREATE TABLE ctl_stats4_parent (a int, b int, c int, d int); +ALTER TABLE ctl_stats4_parent DROP COLUMN b; +CREATE STATISTICS ctl_stats4_stat ON a, c FROM ctl_stats4_parent; +CREATE TABLE ctl_stats4_child (LIKE ctl_stats4_parent INCLUDING STATISTICS); +SELECT s.stxrelid::regclass AS relation, + array_agg(a.attname ORDER BY u.ord) AS stats_columns +FROM pg_statistic_ext s +CROSS JOIN LATERAL + unnest(s.stxkeys::int2[]) WITH ORDINALITY AS u(attnum, ord) +JOIN pg_attribute a + ON a.attrelid = s.stxrelid AND a.attnum = u.attnum +WHERE s.stxrelid IN ('ctl_stats3_child'::regclass, + 'ctl_stats4_child'::regclass) +GROUP BY s.stxrelid +ORDER BY s.stxrelid::regclass::text; +DROP TABLE ctl_stats3_parent; +DROP TABLE ctl_stats3_child; +DROP TABLE ctl_stats4_parent; +DROP TABLE ctl_stats4_child; diff --git a/parser/src/test/resources/postgres/test/regress/sql/database.sql b/parser/src/test/resources/postgres/test/regress/sql/database.sql index 0367c0e..46ad263 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/database.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/database.sql @@ -14,4 +14,11 @@ WHERE datname = 'regression_utf8'; ALTER DATABASE regression_utf8 RESET TABLESPACE; ROLLBACK; +CREATE ROLE regress_datdba_before; +CREATE ROLE regress_datdba_after; +ALTER DATABASE regression_utf8 OWNER TO regress_datdba_before; +REASSIGN OWNED BY regress_datdba_before TO regress_datdba_after; + DROP DATABASE regression_utf8; +DROP ROLE regress_datdba_before; +DROP ROLE regress_datdba_after; diff --git a/parser/src/test/resources/postgres/test/regress/sql/domain.sql b/parser/src/test/resources/postgres/test/regress/sql/domain.sql index f6dd822..5bac3b1 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/domain.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/domain.sql @@ -54,11 +54,11 @@ INSERT INTO basictest values ('88', 'haha', 'short', '123.1212'); -- Truncate -- Test copy COPY basictest (testvarchar) FROM stdin; -- fail -- Deactivated for SplendidDataTest: notsoshorttext --- Deactivated for SplendidDataTest: \. +\. COPY basictest (testvarchar) FROM stdin; -- Deactivated for SplendidDataTest: short --- Deactivated for SplendidDataTest: \. +\. select * from basictest; @@ -118,12 +118,12 @@ select array_dims(testint4arr), array_dims(testchar4arr) from domarrtest; COPY domarrtest FROM stdin; -- Deactivated for SplendidDataTest: {3,4} {q,w,e} --- Deactivated for SplendidDataTest: \N \N --- Deactivated for SplendidDataTest: \. +\N \N +\. COPY domarrtest FROM stdin; -- fail -- Deactivated for SplendidDataTest: {3,4} {qwerty,w,e} --- Deactivated for SplendidDataTest: \. +\. select * from domarrtest; @@ -349,18 +349,18 @@ INSERT INTO nulltest values ('a', 'b', 'c', NULL, 'd'); -- Good -- Test copy COPY nulltest FROM stdin; --fail -- Deactivated for SplendidDataTest: a b \N d d --- Deactivated for SplendidDataTest: \. +\. COPY nulltest FROM stdin; --fail -- Deactivated for SplendidDataTest: a b c d \N --- Deactivated for SplendidDataTest: \. +\. -- Last row is bad COPY nulltest FROM stdin; -- Deactivated for SplendidDataTest: a b c \N c -- Deactivated for SplendidDataTest: a b c \N d -- Deactivated for SplendidDataTest: a b c \N a --- Deactivated for SplendidDataTest: \. +\. select * from nulltest; @@ -408,7 +408,7 @@ insert into defaulttest default values; -- Test defaults with copy COPY defaulttest(col5) FROM stdin; -- Deactivated for SplendidDataTest: 42 --- Deactivated for SplendidDataTest: \. +\. select * from defaulttest; @@ -437,6 +437,17 @@ alter domain dnotnulltest drop not null; update domnotnull set col1 = null; +update domnotnull set col1 = 5; + +-- these constraints can also be added and removed by name +alter domain dnotnulltest add constraint dnotnulltest_notnull not null; +update domnotnull set col1 = null; -- fails +select conname, pg_get_constraintdef(oid) from pg_constraint + where contypid = 'dnotnulltest'::regtype; + +alter domain dnotnulltest drop constraint dnotnulltest_notnull; +update domnotnull set col1 = null; + drop domain dnotnulltest cascade; -- Test ALTER DOMAIN .. DEFAULT .. diff --git a/parser/src/test/resources/postgres/test/regress/sql/encoding.sql b/parser/src/test/resources/postgres/test/regress/sql/encoding.sql new file mode 100644 index 0000000..d591818 --- /dev/null +++ b/parser/src/test/resources/postgres/test/regress/sql/encoding.sql @@ -0,0 +1,247 @@ +/* skip test if not UTF8 server encoding */ +SELECT getdatabaseencoding() <> 'UTF8' AS skip_test \gset +\if :skip_test +\quit +\endif + +\getenv libdir PG_LIBDIR +\getenv dlsuffix PG_DLSUFFIX + +\set regresslib :libdir '/regress' :dlsuffix + +CREATE FUNCTION test_bytea_to_text(bytea) RETURNS text + AS :'regresslib' LANGUAGE C STRICT; +CREATE FUNCTION test_text_to_bytea(text) RETURNS bytea + AS :'regresslib' LANGUAGE C STRICT; +CREATE FUNCTION test_mblen_func(text, text, text, int) RETURNS int + AS :'regresslib' LANGUAGE C STRICT; +CREATE FUNCTION test_text_to_wchars(text, text) RETURNS int[] + AS :'regresslib' LANGUAGE C STRICT; +CREATE FUNCTION test_wchars_to_text(text, int[]) RETURNS text + AS :'regresslib' LANGUAGE C STRICT; +CREATE FUNCTION test_valid_server_encoding(text) RETURNS boolean + AS :'regresslib' LANGUAGE C STRICT; + + +CREATE TABLE regress_encoding(good text, truncated text, with_nul text, truncated_with_nul text); +INSERT INTO regress_encoding +VALUES ('café', + 'caf' || test_bytea_to_text('\xc3'), + 'café' || test_bytea_to_text('\x00') || 'dcba', + 'caf' || test_bytea_to_text('\xc300') || 'dcba'); + +SELECT good, truncated, with_nul FROM regress_encoding; + +SELECT length(good) FROM regress_encoding; +SELECT substring(good, 3, 1) FROM regress_encoding; +SELECT substring(good, 4, 1) FROM regress_encoding; +SELECT regexp_replace(good, '^caf(.)$', '\1') FROM regress_encoding; +SELECT reverse(good) FROM regress_encoding; + +-- invalid short mb character = error +SELECT length(truncated) FROM regress_encoding; +SELECT substring(truncated, 1, 3) FROM regress_encoding; +SELECT substring(truncated, 1, 4) FROM regress_encoding; +SELECT reverse(truncated) FROM regress_encoding; +-- invalid short mb character = silently dropped +SELECT regexp_replace(truncated, '^caf(.)$', '\1') FROM regress_encoding; + +-- PostgreSQL doesn't allow strings to contain NUL. If a corrupted string +-- contains NUL at a character boundary position, some functions treat it as a +-- character while others treat it as a terminator, as implementation details. + +-- NUL = terminator +SELECT length(with_nul) FROM regress_encoding; +SELECT substring(with_nul, 3, 1) FROM regress_encoding; +SELECT substring(with_nul, 4, 1) FROM regress_encoding; +SELECT substring(with_nul, 5, 1) FROM regress_encoding; +SELECT convert_to(substring(with_nul, 5, 1), 'UTF8') FROM regress_encoding; +SELECT regexp_replace(with_nul, '^caf(.)$', '\1') FROM regress_encoding; +-- NUL = character +SELECT with_nul, reverse(with_nul), reverse(reverse(with_nul)) FROM regress_encoding; + +-- If a corrupted string contains NUL in the tail bytes of a multibyte +-- character (invalid in all encodings), it is considered part of the +-- character for length purposes. An error will only be raised in code paths +-- that convert or verify encodings. + +SELECT length(truncated_with_nul) FROM regress_encoding; +SELECT substring(truncated_with_nul, 3, 1) FROM regress_encoding; +SELECT substring(truncated_with_nul, 4, 1) FROM regress_encoding; +SELECT convert_to(substring(truncated_with_nul, 4, 1), 'UTF8') FROM regress_encoding; +SELECT substring(truncated_with_nul, 5, 1) FROM regress_encoding; +SELECT regexp_replace(truncated_with_nul, '^caf(.)dcba$', '\1') = test_bytea_to_text('\xc300') FROM regress_encoding; +SELECT reverse(truncated_with_nul) FROM regress_encoding; + +-- unbounded: sequence would overrun the string! +SELECT test_mblen_func('pg_mblen_unbounded', 'UTF8', truncated, 3) +FROM regress_encoding; + +-- condition detected when using the length/range variants +SELECT test_mblen_func('pg_mblen_with_len', 'UTF8', truncated, 3) +FROM regress_encoding; +SELECT test_mblen_func('pg_mblen_range', 'UTF8', truncated, 3) +FROM regress_encoding; + +-- unbounded: sequence would overrun the string, if the terminator were really +-- the end of it +SELECT test_mblen_func('pg_mblen_unbounded', 'UTF8', truncated_with_nul, 3) +FROM regress_encoding; +SELECT test_mblen_func('pg_encoding_mblen', 'GB18030', truncated_with_nul, 3) +FROM regress_encoding; + +-- condition detected when using the cstr variants +SELECT test_mblen_func('pg_mblen_cstr', 'UTF8', truncated_with_nul, 3) +FROM regress_encoding; + +DROP TABLE regress_encoding; + +-- mb<->wchar conversions +CREATE FUNCTION test_encoding(encoding text, description text, input bytea) +RETURNS VOID LANGUAGE plpgsql AS +$$ +DECLARE + prefix text; + len int; + wchars int[]; + round_trip bytea; + result text; +BEGIN + prefix := rpad(encoding || ' ' || description || ':', 28); + + -- XXX could also test validation, length functions and include client + -- only encodings with these test cases + + IF test_valid_server_encoding(encoding) THEN + wchars := test_text_to_wchars(encoding, test_bytea_to_text(input)); + round_trip = test_text_to_bytea(test_wchars_to_text(encoding, wchars)); + if input = round_trip then + result := 'OK'; + elsif length(input) > length(round_trip) and round_trip = substr(input, 1, length(round_trip)) then + result := 'truncated'; + else + result := 'failed'; + end if; + RAISE NOTICE '% % -> % -> % = %', prefix, input, wchars, round_trip, result; + END IF; +END; +$$; +-- No validation is done on the encoding itself, just the length to avoid +-- overruns, so some of the byte sequences below are bogus. They cover +-- all code branches, server encodings only for now. +CREATE TABLE encoding_tests (encoding text, description text, input bytea); +INSERT INTO encoding_tests VALUES + -- LATIN1, other single-byte encodings + ('LATIN1', 'ASCII', 'a'), + ('LATIN1', 'extended', '\xe9'), + -- EUC_JP, EUC_JIS_2004, EUR_KR (for the purposes of wchar conversion): + -- 2 8e (CS2, not used by EUR_KR but arbitrarily considered to have EUC_JP length) + -- 3 8f (CS3, not used by EUR_KR but arbitrarily considered to have EUC_JP length) + -- 2 80..ff (CS1) + ('EUC_JP', 'ASCII', 'a'), + ('EUC_JP', 'CS1, short', '\x80'), + ('EUC_JP', 'CS1', '\x8002'), + ('EUC_JP', 'CS2, short', '\x8e'), + ('EUC_JP', 'CS2', '\x8e02'), + ('EUC_JP', 'CS3, short', '\x8f'), + ('EUC_JP', 'CS3, short', '\x8f02'), + ('EUC_JP', 'CS3', '\x8f0203'), + -- EUC_CN + -- 3 8e (CS2, not used but arbitrarily considered to have length 3) + -- 3 8f (CS3, not used but arbitrarily considered to have length 3) + -- 2 80..ff (CS1) + ('EUC_CN', 'ASCII', 'a'), + ('EUC_CN', 'CS1, short', '\x80'), + ('EUC_CN', 'CS1', '\x8002'), + ('EUC_CN', 'CS2, short', '\x8e'), + ('EUC_CN', 'CS2, short', '\x8e02'), + ('EUC_CN', 'CS2', '\x8e0203'), + ('EUC_CN', 'CS3, short', '\x8f'), + ('EUC_CN', 'CS3, short', '\x8f02'), + ('EUC_CN', 'CS3', '\x8f0203'), + -- EUC_TW: + -- 4 8e (CS2) + -- 3 8f (CS3, not used but arbitrarily considered to have length 3) + -- 2 80..ff (CS1) + ('EUC_TW', 'ASCII', 'a'), + ('EUC_TW', 'CS1, short', '\x80'), + ('EUC_TW', 'CS1', '\x8002'), + ('EUC_TW', 'CS2, short', '\x8e'), + ('EUC_TW', 'CS2, short', '\x8e02'), + ('EUC_TW', 'CS2, short', '\x8e0203'), + ('EUC_TW', 'CS2', '\x8e020304'), + ('EUC_TW', 'CS3, short', '\x8f'), + ('EUC_TW', 'CS3, short', '\x8f02'), + ('EUC_TW', 'CS3', '\x8f0203'), + -- UTF8 + -- 2 c0..df + -- 3 e0..ef + -- 4 f0..f7 (but maximum real codepoint U+10ffff has f4) + -- 5 f8..fb (not supported) + -- 6 fc..fd (not supported) + ('UTF8', 'ASCII', 'a'), + ('UTF8', '2 byte, short', '\xdf'), + ('UTF8', '2 byte', '\xdf82'), + ('UTF8', '3 byte, short', '\xef'), + ('UTF8', '3 byte, short', '\xef82'), + ('UTF8', '3 byte', '\xef8283'), + ('UTF8', '4 byte, short', '\xf7'), + ('UTF8', '4 byte, short', '\xf782'), + ('UTF8', '4 byte, short', '\xf78283'), + ('UTF8', '4 byte', '\xf7828384'), + ('UTF8', '5 byte, unsupported', '\xfb'), + ('UTF8', '5 byte, unsupported', '\xfb82'), + ('UTF8', '5 byte, unsupported', '\xfb8283'), + ('UTF8', '5 byte, unsupported', '\xfb828384'), + ('UTF8', '5 byte, unsupported', '\xfb82838485'), + ('UTF8', '6 byte, unsupported', '\xfd'), + ('UTF8', '6 byte, unsupported', '\xfd82'), + ('UTF8', '6 byte, unsupported', '\xfd8283'), + ('UTF8', '6 byte, unsupported', '\xfd828384'), + ('UTF8', '6 byte, unsupported', '\xfd82838485'), + ('UTF8', '6 byte, unsupported', '\xfd8283848586'), + -- MULE_INTERNAL + -- 2 81..8d LC1 + -- 3 90..99 LC2 + ('MULE_INTERNAL', 'ASCII', 'a'), + ('MULE_INTERNAL', 'LC1, short', '\x81'), + ('MULE_INTERNAL', 'LC1', '\x8182'), + ('MULE_INTERNAL', 'LC2, short', '\x90'), + ('MULE_INTERNAL', 'LC2, short', '\x9082'), + ('MULE_INTERNAL', 'LC2', '\x908283'); + +SELECT COUNT(test_encoding(encoding, description, input)) > 0 +FROM encoding_tests; + +-- substring fetches a slice of a toasted value; unused tail of that slice is +-- an incomplete char (bug #19406) +CREATE TABLE toast_3b_utf8 (c text); +INSERT INTO toast_3b_utf8 VALUES (repeat(U&'\2026', 4000)); +SELECT SUBSTRING(c FROM 1 FOR 1) FROM toast_3b_utf8; +SELECT SUBSTRING(c FROM 4001 FOR 1) FROM toast_3b_utf8; +-- diagnose incomplete char iff within the substring +UPDATE toast_3b_utf8 SET c = c || test_bytea_to_text('\xe280'); +SELECT SUBSTRING(c FROM 4000 FOR 1) FROM toast_3b_utf8; +SELECT SUBSTRING(c FROM 4001 FOR 1) FROM toast_3b_utf8; +-- substring needing last byte of its slice_size +ALTER TABLE toast_3b_utf8 RENAME TO toast_4b_utf8; +UPDATE toast_4b_utf8 SET c = repeat(U&'\+01F680', 3000); +SELECT SUBSTRING(c FROM 3000 FOR 1) FROM toast_4b_utf8; + +DROP TABLE encoding_tests; +DROP TABLE toast_4b_utf8; +DROP FUNCTION test_encoding; +DROP FUNCTION test_wchars_to_text; +DROP FUNCTION test_text_to_wchars; +DROP FUNCTION test_valid_server_encoding; +DROP FUNCTION test_mblen_func; +DROP FUNCTION test_bytea_to_text; +DROP FUNCTION test_text_to_bytea; + + +-- substring slow path: multi-byte escape char vs. multi-byte pattern char. +SELECT SUBSTRING('a' SIMILAR U&'\00AC' ESCAPE U&'\00A7'); +-- Levenshtein distance metric: exercise character length cache. +SELECT U&"real\00A7_name" FROM (select 1) AS x(real_name); +-- JSON errcontext: truncate long data. +SELECT repeat(U&'\00A7', 30)::json; diff --git a/parser/src/test/resources/postgres/test/regress/sql/euc_kr.sql b/parser/src/test/resources/postgres/test/regress/sql/euc_kr.sql new file mode 100644 index 0000000..1851b2a --- /dev/null +++ b/parser/src/test/resources/postgres/test/regress/sql/euc_kr.sql @@ -0,0 +1,12 @@ +-- This test is about EUC_KR encoding, chosen as perhaps the most prevalent +-- non-UTF8, multibyte encoding as of 2026-01. Since UTF8 can represent all +-- of EUC_KR, also run the test in UTF8. +SELECT getdatabaseencoding() NOT IN ('EUC_KR', 'UTF8') AS skip_test \gset +\if :skip_test +\quit +\endif + +-- Exercise is_multibyte_char_in_char (non-UTF8) slow path. +SELECT POSITION( + convert_from('\xbcf6c7d0', 'EUC_KR') IN + convert_from('\xb0fac7d02c20bcf6c7d02c20b1e2bcfa2c20bbee', 'EUC_KR')); diff --git a/parser/src/test/resources/postgres/test/regress/sql/event_trigger.sql b/parser/src/test/resources/postgres/test/regress/sql/event_trigger.sql index 73b941f..65ba460 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/event_trigger.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/event_trigger.sql @@ -210,9 +210,15 @@ INSERT INTO undroppable_objs VALUES ('table', 'audit_tbls.schema_two_table_three'); CREATE TABLE dropped_objects ( - type text, - schema text, - object text + object_type text, + schema_name text, + object_name text, + object_identity text, + address_names text[], + address_args text[], + is_temporary bool, + original bool, + normal bool ); -- This tests errors raised within event triggers; the one in audit_tbls @@ -253,8 +259,12 @@ BEGIN END IF; INSERT INTO dropped_objects - (type, schema, object) VALUES - (obj.object_type, obj.schema_name, obj.object_identity); + (object_type, schema_name, object_name, + object_identity, address_names, address_args, + is_temporary, original, normal) VALUES + (obj.object_type, obj.schema_name, obj.object_name, + obj.object_identity, obj.address_names, obj.address_args, + obj.is_temporary, obj.original, obj.normal); END LOOP; END $$; @@ -271,10 +281,12 @@ DROP SCHEMA schema_one, schema_two CASCADE; DELETE FROM undroppable_objs WHERE object_identity = 'schema_one.table_three'; DROP SCHEMA schema_one, schema_two CASCADE; -SELECT * FROM dropped_objects WHERE schema IS NULL OR schema <> 'pg_toast'; +-- exclude TOAST objects because they have unstable names +SELECT * FROM dropped_objects + WHERE schema_name IS NULL OR schema_name <> 'pg_toast'; DROP OWNED BY regress_evt_user; -SELECT * FROM dropped_objects WHERE type = 'schema'; +SELECT * FROM dropped_objects WHERE object_type = 'schema'; DROP ROLE regress_evt_user; @@ -293,9 +305,10 @@ BEGIN IF NOT r.normal AND NOT r.original THEN CONTINUE; END IF; - RAISE NOTICE 'NORMAL: orig=% normal=% istemp=% type=% identity=% name=% args=%', + RAISE NOTICE 'NORMAL: orig=% normal=% istemp=% type=% identity=% schema=% name=% addr=% args=%', r.original, r.normal, r.is_temporary, r.object_type, - r.object_identity, r.address_names, r.address_args; + r.object_identity, r.schema_name, r.object_name, + r.address_names, r.address_args; END LOOP; END; $$; CREATE EVENT TRIGGER regress_event_trigger_report_dropped ON sql_drop @@ -345,6 +358,46 @@ DROP INDEX evttrig.one_idx; DROP SCHEMA evttrig CASCADE; DROP TABLE a_temp_tbl; +-- check unfiltered results, too +CREATE OR REPLACE FUNCTION event_trigger_report_dropped() + RETURNS event_trigger + LANGUAGE plpgsql +AS $$ +DECLARE r record; +BEGIN + FOR r IN SELECT * from pg_event_trigger_dropped_objects() + LOOP + RAISE NOTICE 'DROP: orig=% normal=% istemp=% type=% identity=% schema=% name=% addr=% args=%', + r.original, r.normal, r.is_temporary, r.object_type, + r.object_identity, r.schema_name, r.object_name, + r.address_names, r.address_args; + END LOOP; +END; $$; + +CREATE FUNCTION event_trigger_dummy_trigger() + RETURNS trigger + LANGUAGE plpgsql +AS $$ +BEGIN + RETURN new; +END; $$; + +CREATE TABLE evtrg_nontemp_table (f1 int primary key, f2 int default 42); +CREATE TRIGGER evtrg_nontemp_trig + BEFORE INSERT ON evtrg_nontemp_table + EXECUTE FUNCTION event_trigger_dummy_trigger(); +CREATE POLICY evtrg_nontemp_pol ON evtrg_nontemp_table USING (f2 > 0); +DROP TABLE evtrg_nontemp_table; + +CREATE TEMP TABLE a_temp_tbl (f1 int primary key, f2 int default 42); +CREATE TRIGGER a_temp_trig + BEFORE INSERT ON a_temp_tbl + EXECUTE FUNCTION event_trigger_dummy_trigger(); +CREATE POLICY a_temp_pol ON a_temp_tbl USING (f2 > 0); +DROP TABLE a_temp_tbl; + +DROP FUNCTION event_trigger_dummy_trigger(); + -- CREATE OPERATOR CLASS without FAMILY clause should report -- both CREATE OPERATOR FAMILY and CREATE OPERATOR CLASS CREATE OPERATOR CLASS evttrigopclass FOR TYPE int USING btree AS STORAGE int; diff --git a/parser/src/test/resources/postgres/test/regress/sql/expressions.sql b/parser/src/test/resources/postgres/test/regress/sql/expressions.sql index e02c21f..3b3048f 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/expressions.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/expressions.sql @@ -196,16 +196,108 @@ default for type myint using hash as function 1 myinthash(myint); create table inttest (a myint); -insert into inttest values(1::myint),(null); - --- try an array with enough elements to cause hashing -select * from inttest where a in (1::myint,2::myint,3::myint,4::myint,5::myint,6::myint,7::myint,8::myint,9::myint, null); -select * from inttest where a not in (1::myint,2::myint,3::myint,4::myint,5::myint,6::myint,7::myint,8::myint,9::myint, null); -select * from inttest where a not in (0::myint,2::myint,3::myint,4::myint,5::myint,6::myint,7::myint,8::myint,9::myint, null); --- ensure the result matched with the non-hashed version. We simply remove --- some array elements so that we don't reach the hashing threshold. -select * from inttest where a in (1::myint,2::myint,3::myint,4::myint,5::myint, null); -select * from inttest where a not in (1::myint,2::myint,3::myint,4::myint,5::myint, null); -select * from inttest where a not in (0::myint,2::myint,3::myint,4::myint,5::myint, null); +insert into inttest values (null), (0::myint), (1::myint); + +-- Test EEOP_HASHED_SCALARARRAYOP against EEOP_SCALARARRAYOP. Ensure the +-- result of non-hashed vs hashed is the same. +select + a, + a in (1::myint,2::myint,3::myint,4::myint,5::myint,6::myint,7::myint,8::myint) as not_hashed, + a in (1::myint,2::myint,3::myint,4::myint,5::myint,6::myint,7::myint,8::myint,9::myint) as hashed +from inttest; + +select + a, + a in (null::myint,1::myint,2::myint,3::myint,4::myint,5::myint,6::myint,7::myint) as not_hashed, + a in (null::myint,1::myint,2::myint,3::myint,4::myint,5::myint,6::myint,7::myint,8::myint) as hashed + from inttest; + +select + a, + a not in (1::myint,2::myint,3::myint,4::myint,5::myint,6::myint,7::myint,8::myint) as not_hashed, + a not in (1::myint,2::myint,3::myint,4::myint,5::myint,6::myint,7::myint,8::myint,9::myint) as hashed +from inttest; + +select + a, + a not in (null::myint,1::myint,2::myint,3::myint,4::myint,5::myint,6::myint,7::myint) as not_hashed, + a not in (null::myint,1::myint,2::myint,3::myint,4::myint,5::myint,6::myint,7::myint,8::myint) as hashed +from inttest; + +-- Now make the equal function return false when given two NULLs +create or replace function myinteq(myint, myint) returns bool as $$ +begin + if $1 is null and $2 is null then + return false; + else + return $1::int = $2::int; + end if; +end; +$$ language plpgsql immutable; + +-- And try the same again to ensure EEOP_HASHED_SCALARARRAYOP does the same +-- thing as EEOP_SCALARARRAYOP. +select + a, + a in (1::myint,2::myint,3::myint,4::myint,5::myint,6::myint,7::myint,8::myint) as not_hashed, + a in (1::myint,2::myint,3::myint,4::myint,5::myint,6::myint,7::myint,8::myint,9::myint) as hashed +from inttest; + +select + a, + a in (null::myint,1::myint,2::myint,3::myint,4::myint,5::myint,6::myint,7::myint) as not_hashed, + a in (null::myint,1::myint,2::myint,3::myint,4::myint,5::myint,6::myint,7::myint,8::myint) as hashed + from inttest; + +select + a, + a not in (1::myint,2::myint,3::myint,4::myint,5::myint,6::myint,7::myint,8::myint) as not_hashed, + a not in (1::myint,2::myint,3::myint,4::myint,5::myint,6::myint,7::myint,8::myint,9::myint) as hashed +from inttest; + +select + a, + a not in (null::myint,1::myint,2::myint,3::myint,4::myint,5::myint,6::myint,7::myint) as not_hashed, + a not in (null::myint,1::myint,2::myint,3::myint,4::myint,5::myint,6::myint,7::myint,8::myint) as hashed +from inttest; + +-- Try again with an equality function that treats NULLs as equal to 0. +create or replace function myinteq(myint, myint) returns bool as $$ +begin + if $1 is null and $2 is null then + return false; + else + return coalesce($1::int,0) = coalesce($2::int, 0); + end if; +end; +$$ language plpgsql immutable; + +select + a, + a in (1::myint,2::myint,3::myint,4::myint,5::myint,6::myint,7::myint,8::myint) as not_hashed, + a in (1::myint,2::myint,3::myint,4::myint,5::myint,6::myint,7::myint,8::myint,9::myint) as hashed, + a in (0::myint,1::myint,2::myint,3::myint,4::myint,5::myint,6::myint,7::myint) as not_hashed_zero, + a in (0::myint,1::myint,2::myint,3::myint,4::myint,5::myint,6::myint,7::myint,8::myint) as hashed_zero +from inttest; + +select + a, + a in (null::myint,1::myint,2::myint,3::myint,4::myint,5::myint,6::myint,7::myint) as not_hashed, + a in (null::myint,1::myint,2::myint,3::myint,4::myint,5::myint,6::myint,7::myint,8::myint) as hashed + from inttest; + +select + a, + a not in (1::myint,2::myint,3::myint,4::myint,5::myint,6::myint,7::myint,8::myint) as not_hashed, + a not in (1::myint,2::myint,3::myint,4::myint,5::myint,6::myint,7::myint,8::myint,9::myint) as hashed, + a not in (0::myint,1::myint,2::myint,3::myint,4::myint,5::myint,6::myint,7::myint) as not_hashed_zero, + a not in (0::myint,1::myint,2::myint,3::myint,4::myint,5::myint,6::myint,7::myint,8::myint) as hashed_zero +from inttest; + +select + a, + a not in (null::myint,1::myint,2::myint,3::myint,4::myint,5::myint,6::myint,7::myint) as not_hashed, + a not in (null::myint,1::myint,2::myint,3::myint,4::myint,5::myint,6::myint,7::myint,8::myint) as hashed +from inttest; rollback; diff --git a/parser/src/test/resources/postgres/test/regress/sql/fast_default.sql b/parser/src/test/resources/postgres/test/regress/sql/fast_default.sql index dc9df78..a21b406 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/fast_default.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/fast_default.sql @@ -237,6 +237,50 @@ SELECT comp(); DROP TABLE T; +-- Test domains with default value for table rewrite. +CREATE DOMAIN domain1 AS int DEFAULT 11; -- constant +CREATE DOMAIN domain2 AS int DEFAULT random(min=>10, max=>100); -- volatile +CREATE DOMAIN domain3 AS text DEFAULT foo(4); -- stable +CREATE DOMAIN domain4 AS text[] + DEFAULT ('{"This", "is", "' || foo(4) || '","the", "real", "world"}')::TEXT[]; + +CREATE TABLE t2 (a domain1); +INSERT INTO t2 VALUES (1),(2); + +-- no table rewrite +ALTER TABLE t2 ADD COLUMN b domain1 default 3; + +SELECT attnum, attname, atthasmissing, atthasdef, attmissingval +FROM pg_attribute +WHERE attnum > 0 AND attrelid = 't2'::regclass +ORDER BY attnum; + +-- table rewrite should happen +ALTER TABLE t2 ADD COLUMN c domain3 default left(random()::text,3); + +-- no table rewrite +ALTER TABLE t2 ADD COLUMN d domain4; + +SELECT attnum, attname, atthasmissing, atthasdef, attmissingval +FROM pg_attribute +WHERE attnum > 0 AND attrelid = 't2'::regclass +ORDER BY attnum; + +-- table rewrite should happen +ALTER TABLE t2 ADD COLUMN e domain2; + +SELECT attnum, attname, atthasmissing, atthasdef, attmissingval +FROM pg_attribute +WHERE attnum > 0 AND attrelid = 't2'::regclass +ORDER BY attnum; + +SELECT a, b, length(c) = 3 as c_ok, d, e >= 10 as e_ok FROM t2; + +DROP TABLE t2; +DROP DOMAIN domain1; +DROP DOMAIN domain2; +DROP DOMAIN domain3; +DROP DOMAIN domain4; DROP FUNCTION foo(INT); -- Fall back to full rewrite for volatile expressions diff --git a/parser/src/test/resources/postgres/test/regress/sql/foreign_data.sql b/parser/src/test/resources/postgres/test/regress/sql/foreign_data.sql index 92de65c..eaf572c 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/foreign_data.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/foreign_data.sql @@ -75,6 +75,11 @@ CREATE FUNCTION invalid_fdw_handler() RETURNS int LANGUAGE SQL AS 'SELECT 1;'; CREATE FOREIGN DATA WRAPPER test_fdw HANDLER invalid_fdw_handler; -- ERROR CREATE FOREIGN DATA WRAPPER test_fdw HANDLER test_fdw_handler HANDLER invalid_fdw_handler; -- ERROR CREATE FOREIGN DATA WRAPPER test_fdw HANDLER test_fdw_handler; + +-- should preserve dependency on test_fdw_handler +ALTER FOREIGN DATA WRAPPER test_fdw VALIDATOR postgresql_fdw_validator; +DROP FUNCTION test_fdw_handler(); -- ERROR + DROP FOREIGN DATA WRAPPER test_fdw; -- ALTER FOREIGN DATA WRAPPER @@ -391,10 +396,12 @@ COMMENT ON COLUMN ft1.c1 IS NULL; ALTER FOREIGN TABLE ft1 ADD COLUMN c4 integer; ALTER FOREIGN TABLE ft1 ADD COLUMN c5 integer DEFAULT 0; ALTER FOREIGN TABLE ft1 ADD COLUMN c6 integer; +ALTER FOREIGN TABLE ft1 ADD COLUMN IF NOT EXISTS c6 integer; ALTER FOREIGN TABLE ft1 ADD COLUMN c7 integer NOT NULL; ALTER FOREIGN TABLE ft1 ADD COLUMN c8 integer; ALTER FOREIGN TABLE ft1 ADD COLUMN c9 integer; -- Deactivated for SplendidDataTest: ALTER FOREIGN TABLE ft1 ADD COLUMN c10 integer OPTIONS (p1 'v1'); +ALTER FOREIGN TABLE ft1 ADD c11 integer; ALTER FOREIGN TABLE ft1 ALTER COLUMN c4 SET DEFAULT 0; ALTER FOREIGN TABLE ft1 ALTER COLUMN c5 DROP DEFAULT; @@ -427,6 +434,7 @@ ALTER FOREIGN TABLE ft1 OPTIONS (DROP delimiter, SET quote '~', ADD escape '@'); ALTER FOREIGN TABLE ft1 DROP COLUMN no_column; -- ERROR ALTER FOREIGN TABLE ft1 DROP COLUMN IF EXISTS no_column; ALTER FOREIGN TABLE ft1 DROP COLUMN c9; +ALTER FOREIGN TABLE ft1 DROP c11; ALTER FOREIGN TABLE ft1 ADD COLUMN c11 serial; ALTER FOREIGN TABLE ft1 SET SCHEMA foreign_schema; ALTER FOREIGN TABLE ft1 SET TABLESPACE ts; -- ERROR @@ -438,10 +446,12 @@ ALTER FOREIGN TABLE foreign_schema.ft1 RENAME TO foreign_table_1; -- alter noexisting table ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ADD COLUMN c4 integer; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ADD COLUMN c6 integer; +ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ADD COLUMN IF NOT EXISTS c6 integer; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ADD COLUMN c7 integer NOT NULL; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ADD COLUMN c8 integer; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ADD COLUMN c9 integer; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ADD COLUMN c10 integer OPTIONS (p1 'v1'); +ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ADD c11 integer; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ALTER COLUMN c6 SET NOT NULL; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 ALTER COLUMN c7 DROP NOT NULL; @@ -455,8 +465,10 @@ ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 DROP CONSTRAINT IF EXISTS no_cons ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 DROP CONSTRAINT ft1_c1_check; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 OWNER TO regress_test_role; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 OPTIONS (DROP delimiter, SET quote '~', ADD escape '@'); +ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 DROP COLUMN no_column; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 DROP COLUMN IF EXISTS no_column; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 DROP COLUMN c9; +ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 DROP c11; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 SET SCHEMA foreign_schema; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 RENAME c1 TO foreign_column_1; ALTER FOREIGN TABLE IF EXISTS doesnt_exist_ft1 RENAME TO foreign_table_1; diff --git a/parser/src/test/resources/postgres/test/regress/sql/foreign_key.sql b/parser/src/test/resources/postgres/test/regress/sql/foreign_key.sql index 2f8f029..0f63540 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/foreign_key.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/foreign_key.sql @@ -481,7 +481,8 @@ CREATE TABLE FKTABLE ( fk_id_del_set_null int, fk_id_del_set_default int DEFAULT 0, FOREIGN KEY (tid, fk_id_del_set_null) REFERENCES PKTABLE ON DELETE SET NULL (fk_id_del_set_null), - FOREIGN KEY (tid, fk_id_del_set_default) REFERENCES PKTABLE ON DELETE SET DEFAULT (fk_id_del_set_default) + -- this tests handling of duplicate entries in SET DEFAULT column list + FOREIGN KEY (tid, fk_id_del_set_default) REFERENCES PKTABLE ON DELETE SET DEFAULT (fk_id_del_set_default, fk_id_del_set_default) ); SELECT pg_get_constraintdef(oid) FROM pg_constraint WHERE conrelid = 'fktable'::regclass::oid ORDER BY oid; @@ -978,8 +979,10 @@ COMMIT; -- try additional syntax ALTER TABLE fktable ALTER CONSTRAINT fktable_fk_fkey NOT DEFERRABLE; --- illegal option +-- illegal options -- Deactivated for SplendidDataTest: ALTER TABLE fktable ALTER CONSTRAINT fktable_fk_fkey NOT DEFERRABLE INITIALLY DEFERRED; +-- Deactivated for SplendidDataTest: ALTER TABLE fktable ALTER CONSTRAINT fktable_fk_fkey NO INHERIT; +-- Deactivated for SplendidDataTest: ALTER TABLE fktable ALTER CONSTRAINT fktable_fk_fkey NOT VALID; -- test order of firing of FK triggers when several RI-induced changes need to -- be made to the same row. This was broken by subtransaction-related @@ -1425,6 +1428,23 @@ ALTER TABLE fk_partitioned_fk ATTACH PARTITION fk_partitioned_fk_2 -- leave these tables around intentionally +-- Verify that attaching a table that's referenced by an existing FK +-- in the parent throws an error +CREATE TABLE fk_partitioned_pk_6 (a int PRIMARY KEY); +CREATE TABLE fk_partitioned_fk_6 (a int REFERENCES fk_partitioned_pk_6) PARTITION BY LIST (a); +ALTER TABLE fk_partitioned_fk_6 ATTACH PARTITION fk_partitioned_pk_6 FOR VALUES IN (1); +DROP TABLE fk_partitioned_pk_6, fk_partitioned_fk_6; + +-- This case is similar to above, but the referenced relation is one level +-- lower in the hierarchy. This one fails in a different way as the above, +-- because we don't bother to protect against this case explicitly. If the +-- current error stops happening, we'll need to add a better protection. +CREATE TABLE fk_partitioned_pk_6 (a int PRIMARY KEY) PARTITION BY list (a); +CREATE TABLE fk_partitioned_pk_61 PARTITION OF fk_partitioned_pk_6 FOR VALUES IN (1); +CREATE TABLE fk_partitioned_fk_6 (a int REFERENCES fk_partitioned_pk_61) PARTITION BY LIST (a); +ALTER TABLE fk_partitioned_fk_6 ATTACH PARTITION fk_partitioned_pk_6 FOR VALUES IN (1); +DROP TABLE fk_partitioned_pk_6, fk_partitioned_fk_6; + -- test the case when the referenced table is owned by a different user create role regress_other_partitioned_fk_owner; grant references on fk_notpartitioned_pk to regress_other_partitioned_fk_owner; @@ -1478,29 +1498,52 @@ CREATE TABLE part33_self_fk ( ); ALTER TABLE part3_self_fk ATTACH PARTITION part33_self_fk FOR VALUES FROM (30) TO (40); -SELECT cr.relname, co.conname, co.contype, co.convalidated, +-- verify that this constraint works +INSERT INTO parted_self_fk VALUES (1, NULL), (2, NULL), (3, NULL); +INSERT INTO parted_self_fk VALUES (10, 1), (11, 2), (12, 3) RETURNING tableoid::regclass; + +INSERT INTO parted_self_fk VALUES (4, 5); -- error: referenced doesn't exist +DELETE FROM parted_self_fk WHERE id = 1 RETURNING *; -- error: reference remains + +SELECT cr.relname, co.conname, co.convalidated, p.conname AS conparent, p.convalidated, cf.relname AS foreignrel FROM pg_constraint co JOIN pg_class cr ON cr.oid = co.conrelid LEFT JOIN pg_class cf ON cf.oid = co.confrelid LEFT JOIN pg_constraint p ON p.oid = co.conparentid -WHERE cr.oid IN (SELECT relid FROM pg_partition_tree('parted_self_fk')) -ORDER BY co.contype, cr.relname, co.conname, p.conname; +WHERE co.contype = 'f' AND + cr.oid IN (SELECT relid FROM pg_partition_tree('parted_self_fk')) +ORDER BY cr.relname, co.conname, p.conname; -- detach and re-attach multiple times just to ensure everything is kosher ALTER TABLE parted_self_fk DETACH PARTITION part2_self_fk; + +INSERT INTO part2_self_fk VALUES (16, 9); -- error: referenced doesn't exist +DELETE FROM parted_self_fk WHERE id = 2 RETURNING *; -- error: reference remains + ALTER TABLE parted_self_fk ATTACH PARTITION part2_self_fk FOR VALUES FROM (10) TO (20); + +INSERT INTO parted_self_fk VALUES (16, 9); -- error: referenced doesn't exist +DELETE FROM parted_self_fk WHERE id = 3 RETURNING *; -- error: reference remains + ALTER TABLE parted_self_fk DETACH PARTITION part2_self_fk; ALTER TABLE parted_self_fk ATTACH PARTITION part2_self_fk FOR VALUES FROM (10) TO (20); -SELECT cr.relname, co.conname, co.contype, co.convalidated, +ALTER TABLE parted_self_fk DETACH PARTITION part3_self_fk; +ALTER TABLE parted_self_fk ATTACH PARTITION part3_self_fk FOR VALUES FROM (30) TO (40); + +ALTER TABLE part3_self_fk DETACH PARTITION part33_self_fk; +ALTER TABLE part3_self_fk ATTACH PARTITION part33_self_fk FOR VALUES FROM (30) TO (40); + +SELECT cr.relname, co.conname, co.convalidated, p.conname AS conparent, p.convalidated, cf.relname AS foreignrel FROM pg_constraint co JOIN pg_class cr ON cr.oid = co.conrelid LEFT JOIN pg_class cf ON cf.oid = co.confrelid LEFT JOIN pg_constraint p ON p.oid = co.conparentid -WHERE cr.oid IN (SELECT relid FROM pg_partition_tree('parted_self_fk')) -ORDER BY co.contype, cr.relname, co.conname, p.conname; +WHERE co.contype = 'f' AND + cr.oid IN (SELECT relid FROM pg_partition_tree('parted_self_fk')) +ORDER BY cr.relname, co.conname, p.conname; -- Leave this table around, for pg_upgrade/pg_dump tests @@ -1663,6 +1706,14 @@ DELETE FROM pk WHERE a = 4002; UPDATE pk SET a = 4502 WHERE a = 4500; DELETE FROM pk WHERE a = 4502; +-- Also, detaching a partition that has the FK itself should work +-- https://postgr.es/m/CAAJ_b97GuPh6wQPbxQS-Zpy16Oh+0aMv-w64QcGrLhCOZZ6p+g@mail.gmail.com +CREATE TABLE ffk (a int, b int REFERENCES pk) PARTITION BY list (a); +CREATE TABLE ffk1 PARTITION OF ffk FOR VALUES IN (1); +ALTER TABLE ffk1 ADD FOREIGN KEY (a) REFERENCES pk; +ALTER TABLE ffk DETACH PARTITION ffk1; +DROP TABLE ffk, ffk1; + CREATE SCHEMA fkpart4; SET search_path TO fkpart4; -- dropping/detaching PARTITIONs is prevented if that would break @@ -2077,3 +2128,112 @@ UPDATE fkpart11.pk SET a = 3 WHERE a = 4; UPDATE fkpart11.pk SET a = 1 WHERE a = 2; DROP SCHEMA fkpart11 CASCADE; + +-- When a table is attached as partition to a partitioned table that has +-- a foreign key to another partitioned table, it acquires a clone of the +-- FK. Upon detach, this clone is not removed, but instead becomes an +-- independent FK. If it then attaches to the partitioned table again, +-- the FK from the parent "takes over" ownership of the independent FK rather +-- than creating a separate one. +CREATE SCHEMA fkpart12 + CREATE TABLE fk_p ( id int, jd int, PRIMARY KEY(id, jd)) PARTITION BY list (id) + CREATE TABLE fk_p_1 PARTITION OF fk_p FOR VALUES IN (1) PARTITION BY list (jd) + CREATE TABLE fk_p_1_1 PARTITION OF fk_p_1 FOR VALUES IN (1) + CREATE TABLE fk_p_1_2 (x int, y int, jd int NOT NULL, id int NOT NULL) + CREATE TABLE fk_p_2 PARTITION OF fk_p FOR VALUES IN (2) PARTITION BY list (jd) + CREATE TABLE fk_p_2_1 PARTITION OF fk_p_2 FOR VALUES IN (1) + CREATE TABLE fk_p_2_2 PARTITION OF fk_p_2 FOR VALUES IN (2) + CREATE TABLE fk_r_1 ( p_jd int NOT NULL, x int, id int PRIMARY KEY, p_id int NOT NULL) + CREATE TABLE fk_r_2 ( id int PRIMARY KEY, p_id int NOT NULL, p_jd int NOT NULL) PARTITION BY list (id) + CREATE TABLE fk_r_2_1 PARTITION OF fk_r_2 FOR VALUES IN (2, 1) + CREATE TABLE fk_r ( id int PRIMARY KEY, p_id int NOT NULL, p_jd int NOT NULL, + FOREIGN KEY (p_id, p_jd) REFERENCES fk_p (id, jd) + ) PARTITION BY list (id); +SET search_path TO fkpart12; + +ALTER TABLE fk_p_1_2 DROP COLUMN x, DROP COLUMN y; +ALTER TABLE fk_p_1 ATTACH PARTITION fk_p_1_2 FOR VALUES IN (2); +ALTER TABLE fk_r_1 DROP COLUMN x; + +INSERT INTO fk_p VALUES (1, 1); + +ALTER TABLE fk_r ATTACH PARTITION fk_r_1 FOR VALUES IN (1); +ALTER TABLE fk_r ATTACH PARTITION fk_r_2 FOR VALUES IN (2); + +\d fk_r_2 + +INSERT INTO fk_r VALUES (1, 1, 1); +INSERT INTO fk_r VALUES (2, 2, 1); + +ALTER TABLE fk_r DETACH PARTITION fk_r_1; +ALTER TABLE fk_r DETACH PARTITION fk_r_2; + +\d fk_r_2 + +INSERT INTO fk_r_1 (id, p_id, p_jd) VALUES (2, 1, 2); -- should fail +DELETE FROM fk_p; -- should fail + +ALTER TABLE fk_r ATTACH PARTITION fk_r_1 FOR VALUES IN (1); +ALTER TABLE fk_r ATTACH PARTITION fk_r_2 FOR VALUES IN (2); + +\d fk_r_2 + +DELETE FROM fk_p; -- should fail + +-- these should all fail +ALTER TABLE fk_r_1 DROP CONSTRAINT fk_r_p_id_p_jd_fkey; +ALTER TABLE fk_r DROP CONSTRAINT fk_r_p_id_p_jd_fkey1; +ALTER TABLE fk_r_2 DROP CONSTRAINT fk_r_p_id_p_jd_fkey; + +SET client_min_messages TO warning; +DROP SCHEMA fkpart12 CASCADE; +RESET client_min_messages; +RESET search_path; + +-- Exercise the column mapping code with foreign keys. In this test we'll +-- create a partitioned table which has a partition with a dropped column and +-- check to ensure that an UPDATE cascades the changes correctly to the +-- partitioned table. +CREATE SCHEMA fkpart13; +SET search_path TO fkpart13; + +CREATE TABLE fkpart13_t1 (a int PRIMARY KEY); + +CREATE TABLE fkpart13_t2 ( + part_id int PRIMARY KEY, + column_to_drop int, + FOREIGN KEY (part_id) REFERENCES fkpart13_t1 ON UPDATE CASCADE ON DELETE CASCADE +) PARTITION BY LIST (part_id); + +CREATE TABLE fkpart13_t2_p1 PARTITION OF fkpart13_t2 FOR VALUES IN (1); + +-- drop the column +ALTER TABLE fkpart13_t2 DROP COLUMN column_to_drop; + +-- create a new partition without the dropped column +CREATE TABLE fkpart13_t2_p2 PARTITION OF fkpart13_t2 FOR VALUES IN (2); + +CREATE TABLE fkpart13_t3 ( + a int NOT NULL, + FOREIGN KEY (a) + REFERENCES fkpart13_t2 + ON UPDATE CASCADE ON DELETE CASCADE +); + +INSERT INTO fkpart13_t1 (a) VALUES (1); +INSERT INTO fkpart13_t2 (part_id) VALUES (1); +INSERT INTO fkpart13_t3 (a) VALUES (1); + +-- Test a cascading update works correctly with with the dropped column +UPDATE fkpart13_t1 SET a = 2 WHERE a = 1; +SELECT tableoid::regclass,* FROM fkpart13_t2; +SELECT tableoid::regclass,* FROM fkpart13_t3; + +-- Exercise code in ExecGetTriggerResultRel() as there's been previous issues +-- with ResultRelInfos being returned with the incorrect ri_RootResultRelInfo +WITH cte AS ( + UPDATE fkpart13_t2_p1 SET part_id = part_id +) UPDATE fkpart13_t1 SET a = 2 WHERE a = 1; + +DROP SCHEMA fkpart13 CASCADE; +RESET search_path; diff --git a/parser/src/test/resources/postgres/test/regress/sql/generated.sql b/parser/src/test/resources/postgres/test/regress/sql/generated.sql index 28160bc..1331848 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/generated.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/generated.sql @@ -203,10 +203,14 @@ COPY gtest1 (a, b) TO stdout; COPY gtest1 FROM stdin; -- Deactivated for SplendidDataTest: 3 -- Deactivated for SplendidDataTest: 4 --- Deactivated for SplendidDataTest: \. +\. COPY gtest1 (a, b) FROM stdin; +COPY gtest1 FROM stdin WHERE b <> 10; + +COPY gtest1 FROM stdin WHERE gtest1 IS NULL; + SELECT * FROM gtest1 ORDER BY a; TRUNCATE gtest3; @@ -219,7 +223,7 @@ COPY gtest3 (a, b) TO stdout; COPY gtest3 FROM stdin; -- Deactivated for SplendidDataTest: 3 -- Deactivated for SplendidDataTest: 4 --- Deactivated for SplendidDataTest: \. +\. COPY gtest3 (a, b) FROM stdin; @@ -395,6 +399,11 @@ CREATE TABLE gtest24 (a int PRIMARY KEY, b gtestdomain1 GENERATED ALWAYS AS (a * INSERT INTO gtest24 (a) VALUES (4); -- ok INSERT INTO gtest24 (a) VALUES (6); -- error +CREATE DOMAIN gtestdomainnn AS int CHECK (VALUE IS NOT NULL); +CREATE TABLE gtest24nn (a int, b gtestdomainnn GENERATED ALWAYS AS (a * 2) STORED); +INSERT INTO gtest24nn (a) VALUES (4); -- ok +INSERT INTO gtest24nn (a) VALUES (NULL); -- error + -- typed tables (currently not supported) CREATE TYPE gtest_type AS (f1 integer, f2 text, f3 bigint); CREATE TABLE gtest28 OF gtest_type (f1 WITH OPTIONS GENERATED ALWAYS AS (f2 *2) STORED); @@ -462,7 +471,10 @@ SELECT tableoid::regclass, * FROM gtest_parent ORDER BY 1, 2, 3; -- generated columns in partition key (not allowed) CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE (f3); +CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE ((f3)); CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE ((f3 * 3)); +CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE ((gtest_part_key)); +CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) STORED) PARTITION BY RANGE ((gtest_part_key is not null)); -- ALTER TABLE ... ADD COLUMN CREATE TABLE gtest25 (a int PRIMARY KEY); @@ -553,6 +565,31 @@ ALTER TABLE ONLY gtest30 ALTER COLUMN b DROP EXPRESSION; -- error \d gtest30_1 ALTER TABLE gtest30_1 ALTER COLUMN b DROP EXPRESSION; -- error +-- composite type dependencies +CREATE TABLE gtest31_1 (a int, b text GENERATED ALWAYS AS ('hello') STORED, c text); +CREATE TABLE gtest31_2 (x int, y gtest31_1); +ALTER TABLE gtest31_1 ALTER COLUMN b TYPE varchar; -- fails + +-- bug #18970: these cases are unsupported, but make sure they fail cleanly +ALTER TABLE gtest31_2 ADD CONSTRAINT cc CHECK ((y).b IS NOT NULL); +ALTER TABLE gtest31_1 ALTER COLUMN b SET EXPRESSION AS ('hello1'); +ALTER TABLE gtest31_2 DROP CONSTRAINT cc; + +CREATE STATISTICS gtest31_2_stat ON ((y).b is not null) FROM gtest31_2; +ALTER TABLE gtest31_1 ALTER COLUMN b SET EXPRESSION AS ('hello2'); +DROP STATISTICS gtest31_2_stat; + +CREATE INDEX gtest31_2_y_idx ON gtest31_2(((y).b)); +ALTER TABLE gtest31_1 ALTER COLUMN b SET EXPRESSION AS ('hello3'); + +DROP TABLE gtest31_1, gtest31_2; + +-- Check it for a partitioned table, too +CREATE TABLE gtest31_1 (a int, b text GENERATED ALWAYS AS ('hello') STORED, c text) PARTITION BY LIST (a); +CREATE TABLE gtest31_2 (x int, y gtest31_1); +ALTER TABLE gtest31_1 ALTER COLUMN b TYPE varchar; -- fails +DROP TABLE gtest31_1, gtest31_2; + -- triggers CREATE TABLE gtest26 ( a int PRIMARY KEY, @@ -679,3 +716,27 @@ ALTER TABLE gtest28a DROP COLUMN a; CREATE TABLE gtest28b (LIKE gtest28a INCLUDING GENERATED); \d gtest28* + +-- rule actions referring to generated columns: +-- NEW.b in a rule action should reflect the generated column's new value +CREATE TABLE gtest_rule (a int, b int GENERATED ALWAYS AS (a * 2) STORED); +CREATE TABLE gtest_rule_log (op text, old_b int, new_b int); +CREATE RULE gtest_rule_upd AS ON UPDATE TO gtest_rule + DO ALSO INSERT INTO gtest_rule_log VALUES ('UPD', OLD.b, NEW.b); +CREATE RULE gtest_rule_ins AS ON INSERT TO gtest_rule + DO ALSO INSERT INTO gtest_rule_log VALUES ('INS', NULL, NEW.b); +INSERT INTO gtest_rule (a) VALUES (1); +UPDATE gtest_rule SET a = 10; +UPDATE gtest_rule SET a = (SELECT max(b) FROM gtest_rule); +SELECT * FROM gtest_rule_log; +DROP RULE gtest_rule_upd ON gtest_rule; +DROP RULE gtest_rule_ins ON gtest_rule; +DROP TABLE gtest_rule_log; + +-- rule quals referring to generated columns: +-- NEW.b in the rule qual should reflect the generated column's new value +CREATE RULE gtest_rule_qual AS ON UPDATE TO gtest_rule WHERE NEW.b > 100 + DO INSTEAD NOTHING; +UPDATE gtest_rule SET a = 100; +SELECT * FROM gtest_rule; +DROP TABLE gtest_rule; diff --git a/parser/src/test/resources/postgres/test/regress/sql/hash_index.sql b/parser/src/test/resources/postgres/test/regress/sql/hash_index.sql index 219da82..60571f6 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/hash_index.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/hash_index.sql @@ -53,6 +53,9 @@ CREATE INDEX hash_txt_index ON hash_txt_heap USING hash (random text_ops); CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=60); +CREATE INDEX hash_i4_partial_index ON hash_i4_heap USING hash (seqno) + WHERE seqno = 9999; + -- -- Also try building functional, expressional, and partial indexes on -- tables that already contain data. @@ -117,6 +120,16 @@ SELECT * FROM hash_f8_heap SELECT * FROM hash_f8_heap WHERE hash_f8_heap.random = '88888888'::float8; +-- +-- partial hash index +-- +EXPLAIN (COSTS OFF) +SELECT * FROM hash_i4_heap + WHERE seqno = 9999; + +SELECT * FROM hash_i4_heap + WHERE seqno = 9999; + -- -- hash index -- grep '^90[^0-9]' hashovfl.data diff --git a/parser/src/test/resources/postgres/test/regress/sql/horology.sql b/parser/src/test/resources/postgres/test/regress/sql/horology.sql index e5cf12f..8648163 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/horology.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/horology.sql @@ -1,9 +1,9 @@ -- -- HOROLOGY -- -SET DateStyle = 'Postgres, MDY'; -SHOW TimeZone; -- Many of these tests depend on the prevailing setting +SHOW TimeZone; -- Many of these tests depend on the prevailing settings +SHOW DateStyle; -- -- Test various input formats @@ -558,6 +558,7 @@ SELECT i, to_timestamp('2018-11-02 12:34:56.1234', 'YYYY-MM-DD HH24:MI:SS.FF' || SELECT i, to_timestamp('2018-11-02 12:34:56.12345', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i; SELECT i, to_timestamp('2018-11-02 12:34:56.123456', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i; SELECT i, to_timestamp('2018-11-02 12:34:56.123456789', 'YYYY-MM-DD HH24:MI:SS.FF' || i) FROM generate_series(1, 6) i; +SELECT i, to_timestamp('20181102123456123456', 'YYYYMMDDHH24MISSFF' || i) FROM generate_series(1, 6) i; SELECT to_date('1 4 1902', 'Q MM YYYY'); -- Q is ignored SELECT to_date('3 4 21 01', 'W MM CC YY'); diff --git a/parser/src/test/resources/postgres/test/regress/sql/indexing.sql b/parser/src/test/resources/postgres/test/regress/sql/indexing.sql index 5f1f4b8..4b3b6f4 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/indexing.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/indexing.sql @@ -246,6 +246,65 @@ select relname, indisvalid from pg_class join pg_index on indexrelid = oid where relname like 'idxpart%' order by relname; drop table idxpart; +-- Verify that re-attaching an already-attached partition index can +-- validate the parent index if it was still invalid, including +-- indirect ancestors in subpartitions. +create table idxpart (a int, b int) partition by range (a); +create table idxpart1 partition of idxpart for values from (0) to (1000) partition by range (a); +create table idxpart11 partition of idxpart1 for values from (0) to (500); +-- Partitioned table with no partitions +create table idxpart2 partition of idxpart for values from (1000) to (2000) partition by range (a); +-- create parent indexes +create index on only idxpart ((a/b)); +create index on only idxpart1 ((a/b)); +create index on only idxpart2 ((a/b)); +-- fail, leaves behind an invalid index on the leaf partition +insert into idxpart11 values (1, 0); +create index concurrently on idxpart11 ((a/b)); +select relname, indisvalid from pg_class join pg_index on indexrelid = oid + where relname like 'idxpart%' order by relname; +-- attach the indexes; parents stay invalid +alter index idxpart1_expr_idx attach partition idxpart11_expr_idx; +alter index idxpart_expr_idx attach partition idxpart1_expr_idx; +alter index idxpart_expr_idx attach partition idxpart2_expr_idx; +select relname, indisvalid from pg_class join pg_index on indexrelid = oid + where relname like 'idxpart%' order by relname; +-- fix the index on the leaf partition +delete from idxpart11 where b = 0; +reindex index concurrently idxpart11_expr_idx; +-- reattach the leaf partition index; parents should now be valid +alter index idxpart1_expr_idx attach partition idxpart11_expr_idx; +select relname, indisvalid from pg_class join pg_index on indexrelid = oid + where relname like 'idxpart%' order by relname; +drop table idxpart; + +-- Verify that re-attaching does not validate the parent when another +-- child index is still invalid. +create table idxpart (a int, b int) partition by range (a); +create table idxpart1 partition of idxpart for values from (0) to (500); +create table idxpart2 partition of idxpart for values from (500) to (1000); +create index on only idxpart ((a/b)); +-- create invalid indexes on both children +insert into idxpart1 values (1, 0); +insert into idxpart2 values (501, 0); +create index concurrently on idxpart1 ((a/b)); +create index concurrently on idxpart2 ((a/b)); +select relname, indisvalid from pg_class join pg_index on indexrelid = oid + where relname like 'idxpart%' order by relname; +-- attach both; parent stays invalid +alter index idxpart_expr_idx attach partition idxpart1_expr_idx; +alter index idxpart_expr_idx attach partition idxpart2_expr_idx; +select relname, indisvalid from pg_class join pg_index on indexrelid = oid + where relname like 'idxpart%' order by relname; +-- fix only idxpart1's index, leave idxpart2's still invalid +delete from idxpart1 where b = 0; +reindex index concurrently idxpart1_expr_idx; +-- re-attach the fixed child; parent should stay invalid +alter index idxpart_expr_idx attach partition idxpart1_expr_idx; +select relname, indisvalid from pg_class join pg_index on indexrelid = oid + where relname like 'idxpart%' order by relname; +drop table idxpart; + -- verify dependency handling during ALTER TABLE DETACH PARTITION create table idxpart (a int) partition by range (a); create table idxpart1 (like idxpart); diff --git a/parser/src/test/resources/postgres/test/regress/sql/inherit.sql b/parser/src/test/resources/postgres/test/regress/sql/inherit.sql index e3bcfdb..572512c 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/inherit.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/inherit.sql @@ -377,8 +377,9 @@ CREATE TABLE inhta (); CREATE TABLE inhtb () INHERITS (inhta); CREATE TABLE inhtc () INHERITS (inhtb); CREATE TABLE inhtd () INHERITS (inhta, inhtb, inhtc); -ALTER TABLE inhta ADD COLUMN i int; +ALTER TABLE inhta ADD COLUMN i int, ADD COLUMN j bigint DEFAULT 1; \d+ inhta +\d+ inhtd DROP TABLE inhta, inhtb, inhtc, inhtd; -- Test for renaming in diamond inheritance @@ -601,6 +602,14 @@ select min(1-id) from matest0; reset enable_seqscan; reset enable_parallel_append; +explain (verbose, costs off) -- bug #18652 +select 1 - id as c from +(select id from matest3 t1 union all select id * 2 from matest3 t2) ss +order by c; +select 1 - id as c from +(select id from matest3 t1 union all select id * 2 from matest3 t2) ss +order by c; + drop table matest0 cascade; -- diff --git a/parser/src/test/resources/postgres/test/regress/sql/join.sql b/parser/src/test/resources/postgres/test/regress/sql/join.sql index 8bfe3b7..e47de0e 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/join.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/join.sql @@ -693,6 +693,30 @@ select tt1.*, tt2.* from tt2 right join tt1 on tt1.joincol = tt2.joincol; reset enable_hashjoin; reset enable_nestloop; +-- +-- regression test for bug #18522 (merge-right-anti-join in inner_unique cases) +-- + +create temp table tbl_ra(a int unique, b int); +insert into tbl_ra select i, i%100 from generate_series(1,1000)i; +create index on tbl_ra (b); +analyze tbl_ra; + +set enable_hashjoin to off; +set enable_nestloop to off; + +-- ensure we get a merge right anti join +explain (costs off) +select * from tbl_ra t1 +where not exists (select 1 from tbl_ra t2 where t2.b = t1.a) and t1.b < 2; + +-- and check we get the expected results +select * from tbl_ra t1 +where not exists (select 1 from tbl_ra t2 where t2.b = t1.a) and t1.b < 2; + +reset enable_hashjoin; +reset enable_nestloop; + -- -- regression test for bug #13908 (hash join with skew tuples & nbatch increase) -- @@ -1305,6 +1329,30 @@ select * from int4_tbl left join ( ) ss(x) on true where ss.x is null; +-- Test computation of varnullingrels when translating appendrel Var +begin; + +create temp table t_append (a int not null, b int); +insert into t_append values (1, 1); +insert into t_append values (2, 3); + +explain (verbose, costs off) +select t1.a, s.a from t_append t1 + left join t_append t2 on t1.a = t2.b + join lateral ( + select t1.a as a union all select t2.a as a + ) s on true +where s.a is not null; + +select t1.a, s.a from t_append t1 + left join t_append t2 on t1.a = t2.b + join lateral ( + select t1.a as a union all select t2.a as a + ) s on true +where s.a is not null; + +rollback; + -- -- test inlining of immutable functions -- @@ -2011,6 +2059,29 @@ from int8_tbl t1 left join onek t4 on t2.q2 < t3.unique2; +-- bug #19460: we need to clean up RestrictInfos more than we had been doing +explain (costs off) +select * from + (select 1::int as id) as lhs +full join + (select dummy_source.id + from (select null::int as id) as dummy_source + left join (select a.id from a where a.id = 42) as sub + on sub.id = dummy_source.id + ) as rhs +on lhs.id = rhs.id; + +explain (costs off) +select * from + (select 1::int as id) as lhs +full join + (select dummy_source.id + from (select 2::int as id) as dummy_source + left join (select a.id from a) as sub + on sub.id = dummy_source.id + ) as rhs +on lhs.id = rhs.id; + -- More tests of correct placement of pseudoconstant quals -- simple constant-false condition @@ -2928,3 +2999,16 @@ SELECT t1.a FROM skip_fetch t1 LEFT JOIN skip_fetch t2 ON t2.a = 1 WHERE t2.a IS RESET enable_indexonlyscan; RESET enable_seqscan; + +-- Test that we do not account for nullingrels when looking up statistics +CREATE TABLE group_tbl (a INT, b INT); +INSERT INTO group_tbl SELECT 1, 1; +CREATE STATISTICS group_tbl_stat (ndistinct) ON a, b FROM group_tbl; +ANALYZE group_tbl; + +EXPLAIN (COSTS OFF) +SELECT 1 FROM group_tbl t1 + LEFT JOIN (SELECT a c1, COALESCE(a) c2 FROM group_tbl t2) s ON TRUE +GROUP BY s.c1, s.c2; + +DROP TABLE group_tbl; diff --git a/parser/src/test/resources/postgres/test/regress/sql/jsonb_jsonpath.sql b/parser/src/test/resources/postgres/test/regress/sql/jsonb_jsonpath.sql index 5e14f77..acb508c 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/jsonb_jsonpath.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/jsonb_jsonpath.sql @@ -587,10 +587,30 @@ select jsonb_path_query('1234', '$.string()'); select jsonb_path_query('true', '$.string()'); select jsonb_path_query('1234', '$.string().type()'); select jsonb_path_query('[2, true]', '$.string()'); -select jsonb_path_query('"2023-08-15 12:34:56 +5:30"', '$.timestamp().string()'); -select jsonb_path_query_tz('"2023-08-15 12:34:56 +5:30"', '$.timestamp().string()'); -- should work select jsonb_path_query_array('[1.23, "yes", false]', '$[*].string()'); select jsonb_path_query_array('[1.23, "yes", false]', '$[*].string().type()'); +select jsonb_path_query('"2023-08-15 12:34:56 +5:30"', '$.timestamp().string()'); +select jsonb_path_query_tz('"2023-08-15 12:34:56 +5:30"', '$.timestamp().string()'); -- should work +select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp_tz().string()'); +select jsonb_path_query_tz('"2023-08-15 12:34:56"', '$.timestamp_tz().string()'); -- should work +select jsonb_path_query('"2023-08-15 12:34:56 +5:30"', '$.timestamp_tz().string()'); +select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp().string()'); +select jsonb_path_query('"12:34:56 +5:30"', '$.time_tz().string()'); +-- this timetz usage will absorb the UTC offset of the current timezone setting +begin; +set local timezone = 'UTC-10'; +select jsonb_path_query_tz('"12:34:56"', '$.time_tz().string()'); +rollback; +select jsonb_path_query('"12:34:56"', '$.time().string()'); +select jsonb_path_query('"2023-08-15"', '$.date().string()'); + +-- .string() does not react to timezone or datestyle +begin; +set local timezone = 'UTC'; +set local datestyle = 'German'; +select jsonb_path_query('"2023-08-15 12:34:56 +5:30"', '$.timestamp_tz().string()'); +select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp().string()'); +rollback; -- Test .time() select jsonb_path_query('null', '$.time()'); diff --git a/parser/src/test/resources/postgres/test/regress/sql/limit.sql b/parser/src/test/resources/postgres/test/regress/sql/limit.sql index df2edd2..514b531 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/limit.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/limit.sql @@ -204,6 +204,9 @@ CREATE VIEW limit_thousand_v_3 AS SELECT thousand FROM onek WHERE thousand < 995 ORDER BY thousand FETCH FIRST (NULL+1) ROWS WITH TIES; \d+ limit_thousand_v_3 CREATE VIEW limit_thousand_v_4 AS SELECT thousand FROM onek WHERE thousand < 995 - ORDER BY thousand FETCH FIRST NULL ROWS ONLY; + ORDER BY thousand FETCH FIRST (5::bigint) ROWS WITH TIES; \d+ limit_thousand_v_4 +CREATE VIEW limit_thousand_v_5 AS SELECT thousand FROM onek WHERE thousand < 995 + ORDER BY thousand FETCH FIRST NULL ROWS ONLY; +\d+ limit_thousand_v_5 -- leave these views diff --git a/parser/src/test/resources/postgres/test/regress/sql/maintain_every.sql b/parser/src/test/resources/postgres/test/regress/sql/maintain_every.sql new file mode 100644 index 0000000..263e972 --- /dev/null +++ b/parser/src/test/resources/postgres/test/regress/sql/maintain_every.sql @@ -0,0 +1,26 @@ +-- Test maintenance commands that visit every eligible relation. Run as a +-- non-superuser, to skip other users' tables. + +CREATE ROLE regress_maintain; +SET ROLE regress_maintain; + +-- Test database-wide ANALYZE ("use_own_xacts" mode) setting relhassubclass=f +-- for non-partitioning inheritance, w/ ON COMMIT DELETE ROWS building an +-- empty index. +CREATE TEMP TABLE past_inh_db_other (); -- need 2 tables for "use_own_xacts" +CREATE TEMP TABLE past_inh_db_parent () ON COMMIT DELETE ROWS; +CREATE TEMP TABLE past_inh_db_child () INHERITS (past_inh_db_parent); +CREATE INDEX ON past_inh_db_parent ((1)); +ANALYZE past_inh_db_parent; +SELECT reltuples, relhassubclass + FROM pg_class WHERE oid = 'past_inh_db_parent'::regclass; +DROP TABLE past_inh_db_child; +SET client_min_messages = error; -- hide WARNINGs for other users' tables +ANALYZE; +RESET client_min_messages; +SELECT reltuples, relhassubclass + FROM pg_class WHERE oid = 'past_inh_db_parent'::regclass; +DROP TABLE past_inh_db_parent, past_inh_db_other; + +RESET ROLE; +DROP ROLE regress_maintain; diff --git a/parser/src/test/resources/postgres/test/regress/sql/merge.sql b/parser/src/test/resources/postgres/test/regress/sql/merge.sql index 6df9cad..de7e099 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/merge.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/merge.sql @@ -1277,6 +1277,19 @@ MERGE INTO pa_target t SELECT * FROM pa_target ORDER BY tid; ROLLBACK; +-- bug #18871: ExecInitPartitionInfo()'s handling of DO NOTHING actions +BEGIN; +TRUNCATE pa_target; +MERGE INTO pa_target t + USING (VALUES (10, 100)) AS s(sid, delta) + ON t.tid = s.sid + WHEN NOT MATCHED THEN + INSERT VALUES (1, 10, 'inserted by merge') + WHEN MATCHED THEN + DO NOTHING; +SELECT * FROM pa_target ORDER BY tid, val; +ROLLBACK; + DROP TABLE pa_target CASCADE; -- The target table is partitioned in the same way, but this time by attaching @@ -1715,9 +1728,97 @@ WHEN MATCHED THEN DELETE; SELECT * FROM new_measurement ORDER BY city_id, logdate; +-- MERGE into inheritance root table +DROP TRIGGER insert_measurement_trigger ON measurement; +ALTER TABLE measurement ADD CONSTRAINT mcheck CHECK (city_id = 0) NO INHERIT; + +EXPLAIN (COSTS OFF) +MERGE INTO measurement m + USING (VALUES (1, '01-17-2007'::date)) nm(city_id, logdate) ON + (m.city_id = nm.city_id and m.logdate=nm.logdate) +WHEN NOT MATCHED THEN INSERT + (city_id, logdate, peaktemp, unitsales) + VALUES (city_id - 1, logdate, 25, 100); + +BEGIN; +MERGE INTO measurement m + USING (VALUES (1, '01-17-2007'::date)) nm(city_id, logdate) ON + (m.city_id = nm.city_id and m.logdate=nm.logdate) +WHEN NOT MATCHED THEN INSERT + (city_id, logdate, peaktemp, unitsales) + VALUES (city_id - 1, logdate, 25, 100); +SELECT * FROM ONLY measurement ORDER BY city_id, logdate; +ROLLBACK; + +ALTER TABLE measurement ENABLE ROW LEVEL SECURITY; +ALTER TABLE measurement FORCE ROW LEVEL SECURITY; +CREATE POLICY measurement_p ON measurement USING (peaktemp IS NOT NULL); + +MERGE INTO measurement m + USING (VALUES (1, '01-17-2007'::date)) nm(city_id, logdate) ON + (m.city_id = nm.city_id and m.logdate=nm.logdate) +WHEN NOT MATCHED THEN INSERT + (city_id, logdate, peaktemp, unitsales) + VALUES (city_id - 1, logdate, NULL, 100); -- should fail + +MERGE INTO measurement m + USING (VALUES (1, '01-17-2007'::date)) nm(city_id, logdate) ON + (m.city_id = nm.city_id and m.logdate=nm.logdate) +WHEN NOT MATCHED THEN INSERT + (city_id, logdate, peaktemp, unitsales) + VALUES (city_id - 1, logdate, 25, 100); -- ok +SELECT * FROM ONLY measurement ORDER BY city_id, logdate; + +MERGE INTO measurement m + USING (VALUES (1, '01-18-2007'::date)) nm(city_id, logdate) ON + (m.city_id = nm.city_id and m.logdate=nm.logdate) +WHEN NOT MATCHED THEN INSERT + (city_id, logdate, peaktemp, unitsales) + VALUES (city_id - 1, logdate, 25, 200) +RETURNING merge_action(), m.*; + DROP TABLE measurement, new_measurement CASCADE; DROP FUNCTION measurement_insert_trigger(); +-- +-- test non-strict join clause +-- +CREATE TABLE src (a int, b text); +INSERT INTO src VALUES (1, 'src row'); + +CREATE TABLE tgt (a int, b text); +INSERT INTO tgt VALUES (NULL, 'tgt row'); + +MERGE INTO tgt USING src ON tgt.a IS NOT DISTINCT FROM src.a + WHEN MATCHED THEN UPDATE SET a = src.a, b = src.b + WHEN NOT MATCHED BY SOURCE THEN DELETE + RETURNING merge_action(), src.*, tgt.*; + +SELECT * FROM tgt; + +DROP TABLE src, tgt; + +-- +-- test for bug #18634 (wrong varnullingrels error) +-- +CREATE TABLE bug18634t (a int, b int, c text); +INSERT INTO bug18634t VALUES(1, 10, 'tgt1'), (2, 20, 'tgt2'); +CREATE VIEW bug18634v AS + SELECT * FROM bug18634t WHERE EXISTS (SELECT 1 FROM bug18634t); + +CREATE TABLE bug18634s (a int, b int, c text); +INSERT INTO bug18634s VALUES (1, 2, 'src1'); + +MERGE INTO bug18634v t USING bug18634s s ON s.a = t.a + WHEN MATCHED THEN UPDATE SET b = s.b + WHEN NOT MATCHED BY SOURCE THEN DELETE + RETURNING merge_action(), s.c, t.*; + +SELECT * FROM bug18634t; + +DROP TABLE bug18634t CASCADE; +DROP TABLE bug18634s; + -- prepare RESET SESSION AUTHORIZATION; diff --git a/parser/src/test/resources/postgres/test/regress/sql/multirangetypes.sql b/parser/src/test/resources/postgres/test/regress/sql/multirangetypes.sql index 41d5524..c7953aa 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/multirangetypes.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/multirangetypes.sql @@ -721,6 +721,22 @@ drop type textrange1; reset role; drop role regress_multirange_owner; +-- +-- CREATE TYPE checks for CREATE on multirange schema +-- +create role regress_mr; +create schema mr_sch; +set role regress_mr; +create type mytype as range (subtype=int4, multirange_type_name=mr_sch.mr_type); +reset role; +grant create on schema mr_sch to regress_mr; +set role regress_mr; +create type mytype as range (subtype=int4, multirange_type_name=mr_sch.mr_type); +reset role; +drop type mytype; +drop schema mr_sch; +drop role regress_mr; + -- -- Test polymorphic type system -- @@ -815,6 +831,9 @@ select *, row_to_json(upper(t)) as u from (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))), (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t); +-- this must be rejected to avoid self-inclusion issues: +alter type two_ints add attribute c two_ints_multirange; + drop type two_ints cascade; -- diff --git a/parser/src/test/resources/postgres/test/regress/sql/partition_join.sql b/parser/src/test/resources/postgres/test/regress/sql/partition_join.sql index 128ce83..afadefd 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/partition_join.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/partition_join.sql @@ -138,6 +138,10 @@ SELECT a, b FROM prt1 FULL JOIN prt2 p2(b,a,c) USING(a,b) RESET enable_partitionwise_aggregate; RESET enable_hashjoin; +-- bug in freeing the SpecialJoinInfo of a child-join +EXPLAIN (COSTS OFF) +SELECT * FROM prt1 t1 JOIN prt1 t2 ON t1.a = t2.a WHERE t1.a IN (SELECT a FROM prt1 t3); + -- -- partitioned by expression -- diff --git a/parser/src/test/resources/postgres/test/regress/sql/partition_prune.sql b/parser/src/test/resources/postgres/test/regress/sql/partition_prune.sql index a09b27d..63f1bb5 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/partition_prune.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/partition_prune.sql @@ -369,6 +369,21 @@ explain (costs off) select * from rparted_by_int2 where a > 100_000_000_000_000; drop table lp, coll_pruning, rlp, mc3p, mc2p, boolpart, iboolpart, boolrangep, rp, coll_pruning_multi, like_op_noprune, lparted_by_int2, rparted_by_int2; +-- check that AlternativeSubPlan within a pruning expression gets cleaned up + +create table asptab (id int primary key) partition by range (id); +create table asptab0 partition of asptab for values from (0) to (1); +create table asptab1 partition of asptab for values from (1) to (2); + +explain (costs off) +select * from + (select exists (select 1 from int4_tbl tinner where f1 = touter.f1) as b + from int4_tbl touter) ss, + asptab +where asptab.id > ss.b::int; + +drop table asptab; + -- -- Test Partition pruning for HASH partitioning -- diff --git a/parser/src/test/resources/postgres/test/regress/sql/predicate.sql b/parser/src/test/resources/postgres/test/regress/sql/predicate.sql index 63f6a77..9dcb81b 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/predicate.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/predicate.sql @@ -64,22 +64,20 @@ SELECT * FROM pred_tab t WHERE t.b IS NULL OR t.c IS NULL; -- and b) its Var is not nullable by any outer joins EXPLAIN (COSTS OFF) SELECT * FROM pred_tab t1 - LEFT JOIN pred_tab t2 ON TRUE - LEFT JOIN pred_tab t3 ON t2.a IS NOT NULL; + LEFT JOIN pred_tab t2 ON t1.a IS NOT NULL; -- Ensure the IS_NOT_NULL qual is not ignored when columns are made nullable -- by an outer join EXPLAIN (COSTS OFF) SELECT * FROM pred_tab t1 - LEFT JOIN pred_tab t2 ON t1.a = 1 + FULL JOIN pred_tab t2 ON t1.a = t2.a LEFT JOIN pred_tab t3 ON t2.a IS NOT NULL; -- Ensure the IS_NULL qual is reduced to constant-FALSE, since a) it's on a NOT -- NULL column, and b) its Var is not nullable by any outer joins EXPLAIN (COSTS OFF) SELECT * FROM pred_tab t1 - LEFT JOIN pred_tab t2 ON TRUE - LEFT JOIN pred_tab t3 ON t2.a IS NULL AND t2.b = 1; + LEFT JOIN pred_tab t2 ON t1.a IS NULL; -- Ensure the IS_NULL qual is not reduced to constant-FALSE when the column is -- nullable by an outer join @@ -95,22 +93,20 @@ SELECT * FROM pred_tab t1 -- Ensure the OR clause is ignored when an OR branch is provably always true EXPLAIN (COSTS OFF) SELECT * FROM pred_tab t1 - LEFT JOIN pred_tab t2 ON TRUE - LEFT JOIN pred_tab t3 ON t2.a IS NOT NULL OR t2.b = 1; + LEFT JOIN pred_tab t2 ON t1.a IS NOT NULL OR t2.b = 1; -- Ensure the NullTest is not ignored when the column is nullable by an outer -- join EXPLAIN (COSTS OFF) SELECT * FROM pred_tab t1 - LEFT JOIN pred_tab t2 ON t1.a = 1 + FULL JOIN pred_tab t2 ON t1.a = t2.a LEFT JOIN pred_tab t3 ON t2.a IS NOT NULL OR t2.b = 1; -- Ensure the OR clause is reduced to constant-FALSE when all OR branches are -- provably false EXPLAIN (COSTS OFF) SELECT * FROM pred_tab t1 - LEFT JOIN pred_tab t2 ON TRUE - LEFT JOIN pred_tab t3 ON (t2.a IS NULL OR t2.c IS NULL) AND t2.b = 1; + LEFT JOIN pred_tab t2 ON (t1.a IS NULL OR t1.c IS NULL); -- Ensure the OR clause is not reduced to constant-FALSE when a column is -- made nullable from an outer join @@ -147,3 +143,43 @@ EXPLAIN (COSTS OFF) SELECT * FROM pred_parent WHERE a IS NULL; DROP TABLE pred_parent, pred_child; + +-- Validate we do not reduce a clone clause to a constant true or false +CREATE TABLE pred_tab (a int, b int); +CREATE TABLE pred_tab_notnull (a int, b int NOT NULL); + +INSERT INTO pred_tab VALUES (1, 1); +INSERT INTO pred_tab VALUES (2, 2); + +INSERT INTO pred_tab_notnull VALUES (2, 2); +INSERT INTO pred_tab_notnull VALUES (3, 3); + +ANALYZE pred_tab; +ANALYZE pred_tab_notnull; + +-- Ensure the IS_NOT_NULL qual is not reduced to constant true and removed +EXPLAIN (COSTS OFF) +SELECT * FROM pred_tab t1 + LEFT JOIN pred_tab t2 ON TRUE + LEFT JOIN pred_tab_notnull t3 ON t2.a = t3.a + LEFT JOIN pred_tab t4 ON t3.b IS NOT NULL; + +SELECT * FROM pred_tab t1 + LEFT JOIN pred_tab t2 ON TRUE + LEFT JOIN pred_tab_notnull t3 ON t2.a = t3.a + LEFT JOIN pred_tab t4 ON t3.b IS NOT NULL; + +-- Ensure the IS_NULL qual is not reduced to constant false +EXPLAIN (COSTS OFF) +SELECT * FROM pred_tab t1 + LEFT JOIN pred_tab t2 ON TRUE + LEFT JOIN pred_tab_notnull t3 ON t2.a = t3.a + LEFT JOIN pred_tab t4 ON t3.b IS NULL AND t3.a IS NOT NULL; + +SELECT * FROM pred_tab t1 + LEFT JOIN pred_tab t2 ON TRUE + LEFT JOIN pred_tab_notnull t3 ON t2.a = t3.a + LEFT JOIN pred_tab t4 ON t3.b IS NULL AND t3.a IS NOT NULL; + +DROP TABLE pred_tab; +DROP TABLE pred_tab_notnull; diff --git a/parser/src/test/resources/postgres/test/regress/sql/prepared_xacts.sql b/parser/src/test/resources/postgres/test/regress/sql/prepared_xacts.sql index ade3a26..b0712b1 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/prepared_xacts.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/prepared_xacts.sql @@ -1,3 +1,8 @@ +SELECT current_setting('max_prepared_transactions')::integer < 2 AS skip_test \gset +\if :skip_test +\quit +\endif + -- -- PREPARED TRANSACTIONS (two-phase commit) -- @@ -158,7 +163,32 @@ SELECT * FROM pxtest3; -- There should be no prepared transactions SELECT gid FROM pg_prepared_xacts WHERE gid ~ '^regress_' ORDER BY gid; + +-- Test row-level locks held by prepared transactions +CREATE TABLE pxtest_rowlock (id int PRIMARY KEY, data text); +INSERT INTO pxtest_rowlock VALUES (1, 'test data'); + +BEGIN; +SELECT * FROM pxtest_rowlock WHERE id = 1 FOR SHARE; +PREPARE TRANSACTION 'regress_p1'; + +-- Should fail because the row is locked +SELECT * FROM pxtest_rowlock WHERE id = 1 FOR UPDATE NOWAIT; + +-- Test prepared transactions that participate in multixacts. For +-- that, lock the same row again, creating a multixid. +BEGIN; +SELECT * FROM pxtest_rowlock WHERE id = 1 FOR SHARE; +PREPARE TRANSACTION 'regress_p2'; + +-- Should fail because the row is locked +SELECT * FROM pxtest_rowlock WHERE id = 1 FOR UPDATE NOWAIT; + +ROLLBACK PREPARED 'regress_p1'; +ROLLBACK PREPARED 'regress_p2'; + -- Clean up DROP TABLE pxtest2; -DROP TABLE pxtest3; -- will still be there if prepared xacts are disabled +-- pxtest3 was already dropped DROP TABLE pxtest4; +DROP TABLE pxtest_rowlock; diff --git a/parser/src/test/resources/postgres/test/regress/sql/privileges.sql b/parser/src/test/resources/postgres/test/regress/sql/privileges.sql index 39b2d31..a5e03c2 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/privileges.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/privileges.sql @@ -132,6 +132,39 @@ SET ROLE pg_read_all_stats; -- fail, granted without SET option RESET ROLE; RESET SESSION AUTHORIZATION; + +-- test interaction of SET SESSION AUTHORIZATION and SET ROLE, +-- as well as propagation of these settings to parallel workers +GRANT regress_priv_user9 TO regress_priv_user8; + +SET SESSION AUTHORIZATION regress_priv_user8; +SET ROLE regress_priv_user9; +SET debug_parallel_query = 0; +SELECT session_user, current_role, current_user, current_setting('role') as role; +SET debug_parallel_query = 1; +SELECT session_user, current_role, current_user, current_setting('role') as role; + +BEGIN; +SET SESSION AUTHORIZATION regress_priv_user10; +SET debug_parallel_query = 0; +SELECT session_user, current_role, current_user, current_setting('role') as role; +SET debug_parallel_query = 1; +SELECT session_user, current_role, current_user, current_setting('role') as role; +ROLLBACK; +SET debug_parallel_query = 0; +SELECT session_user, current_role, current_user, current_setting('role') as role; +SET debug_parallel_query = 1; +SELECT session_user, current_role, current_user, current_setting('role') as role; + +RESET SESSION AUTHORIZATION; +-- session_user at this point is installation-dependent +SET debug_parallel_query = 0; +SELECT session_user = current_role as c_r_ok, session_user = current_user as c_u_ok, current_setting('role') as role; +SET debug_parallel_query = 1; +SELECT session_user = current_role as c_r_ok, session_user = current_user as c_u_ok, current_setting('role') as role; + +RESET debug_parallel_query; + REVOKE pg_read_all_settings FROM regress_priv_user8; DROP USER regress_priv_user10; @@ -144,6 +177,9 @@ CREATE GROUP regress_priv_group2 WITH ADMIN regress_priv_user1 USER regress_priv ALTER GROUP regress_priv_group1 ADD USER regress_priv_user4; GRANT regress_priv_group2 TO regress_priv_user2 GRANTED BY regress_priv_user1; +SET SESSION AUTHORIZATION regress_priv_user3; +ALTER GROUP regress_priv_group2 ADD USER regress_priv_user2; -- fail +ALTER GROUP regress_priv_group2 DROP USER regress_priv_user2; -- fail SET SESSION AUTHORIZATION regress_priv_user1; ALTER GROUP regress_priv_group2 ADD USER regress_priv_user2; ALTER GROUP regress_priv_group2 ADD USER regress_priv_user2; -- duplicate @@ -270,7 +306,7 @@ SELECT * FROM atest2 WHERE ( col1 IN ( SELECT b FROM atest1 ) ); SET SESSION AUTHORIZATION regress_priv_user4; COPY atest2 FROM stdin; -- ok -- Deactivated for SplendidDataTest: bar true --- Deactivated for SplendidDataTest: \. +\. SELECT * FROM atest1; -- ok @@ -297,8 +333,6 @@ CREATE VIEW atest12v AS SELECT * FROM atest12 WHERE b <<< 5; CREATE VIEW atest12sbv WITH (security_barrier=true) AS SELECT * FROM atest12 WHERE b <<< 5; -GRANT SELECT ON atest12v TO PUBLIC; -GRANT SELECT ON atest12sbv TO PUBLIC; -- This plan should use nestloop, knowing that few rows will be selected. EXPLAIN (COSTS OFF) SELECT * FROM atest12v x, atest12v y WHERE x.a = y.b; @@ -320,8 +354,16 @@ CREATE FUNCTION leak2(integer,integer) RETURNS boolean CREATE OPERATOR >>> (procedure = leak2, leftarg = integer, rightarg = integer, restrict = scalargtsel); --- This should not show any "leak" notices before failing. +-- These should not show any "leak" notices before failing. EXPLAIN (COSTS OFF) SELECT * FROM atest12 WHERE a >>> 0; +EXPLAIN (COSTS OFF) SELECT * FROM atest12v WHERE a >>> 0; +EXPLAIN (COSTS OFF) SELECT * FROM atest12sbv WHERE a >>> 0; + +-- Now regress_priv_user1 grants access to regress_priv_user2 via the views. +SET SESSION AUTHORIZATION regress_priv_user1; +GRANT SELECT ON atest12v TO PUBLIC; +GRANT SELECT ON atest12sbv TO PUBLIC; +SET SESSION AUTHORIZATION regress_priv_user2; -- These plans should continue to use a nestloop, since they execute with the -- privileges of the view owner. @@ -501,7 +543,7 @@ INSERT INTO atest5 (two) VALUES (3); -- ok COPY atest5 FROM stdin; -- fail COPY atest5 (two) FROM stdin; -- ok -- Deactivated for SplendidDataTest: 1 --- Deactivated for SplendidDataTest: \. +\. INSERT INTO atest5 (three) VALUES (4); -- fail INSERT INTO atest5 VALUES (5,5,5); -- fail UPDATE atest5 SET three = 10; -- ok @@ -1453,6 +1495,14 @@ SELECT makeaclitem('regress_priv_user1'::regrole, 'regress_priv_user2'::regrole, SELECT makeaclitem('regress_priv_user1'::regrole, 'regress_priv_user2'::regrole, 'SELECT, fake_privilege', FALSE); -- error +-- Test quoting and dequoting of user names in ACLs +CREATE ROLE "regress_""quoted"; +SELECT makeaclitem('regress_"quoted'::regrole, 'regress_"quoted'::regrole, + 'SELECT', TRUE); +SELECT '"regress_""quoted"=r*/"regress_""quoted"'::aclitem; +SELECT '""=r*/""'::aclitem; -- used to be misparsed as """" +DROP ROLE "regress_""quoted"; + -- Test non-throwing aclitem I/O SELECT pg_input_is_valid('regress_priv_user1=r/regress_priv_user2', 'aclitem'); SELECT pg_input_is_valid('regress_priv_user1=r/', 'aclitem'); @@ -1722,6 +1772,13 @@ DROP USER regress_priv_user7; DROP USER regress_priv_user8; -- does not exist +-- leave some default ACLs for pg_upgrade's dump-restore test input. +ALTER DEFAULT PRIVILEGES FOR ROLE pg_signal_backend + REVOKE USAGE ON TYPES FROM pg_signal_backend; +ALTER DEFAULT PRIVILEGES FOR ROLE pg_read_all_settings + REVOKE USAGE ON TYPES FROM pg_read_all_settings; + + -- permissions with LOCK TABLE CREATE USER regress_locktable_user; CREATE TABLE lock_table (a int); diff --git a/parser/src/test/resources/postgres/test/regress/sql/psql.sql b/parser/src/test/resources/postgres/test/regress/sql/psql.sql index 4809476..3233eb7 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/psql.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/psql.sql @@ -1,10 +1,8 @@ /* - * This file has been altered by SplendidData. - * It is only used for happy flow syntax checking, so erroneous statements are commented out here. - * The deactivated lines are marked by: -- Deactivated for SplendidDataTest: - */ - - + * This file is completely commented out as it is there to test psql. + * It hardly has added value for testing sql. + * + * -- @@ -32,41 +30,41 @@ -- \g and \gx --- Deactivated for SplendidDataTest: SELECT 1 as one, 2 as two \g --- Deactivated for SplendidDataTest: \gx --- Deactivated for SplendidDataTest: SELECT 3 as three, 4 as four \gx --- Deactivated for SplendidDataTest: \g +SELECT 1 as one, 2 as two \g +\gx +SELECT 3 as three, 4 as four \gx +\g -- \gx should work in FETCH_COUNT mode too \set FETCH_COUNT 1 --- Deactivated for SplendidDataTest: SELECT 1 as one, 2 as two \g --- Deactivated for SplendidDataTest: \gx --- Deactivated for SplendidDataTest: SELECT 3 as three, 4 as four \gx --- Deactivated for SplendidDataTest: \g +SELECT 1 as one, 2 as two \g +\gx +SELECT 3 as three, 4 as four \gx +\g \unset FETCH_COUNT -- \g/\gx with pset options --- Deactivated for SplendidDataTest: SELECT 1 as one, 2 as two \g (format=csv csv_fieldsep='\t') --- Deactivated for SplendidDataTest: \g --- Deactivated for SplendidDataTest: SELECT 1 as one, 2 as two \gx (title='foo bar') --- Deactivated for SplendidDataTest: \g +SELECT 1 as one, 2 as two \g (format=csv csv_fieldsep='\t') +\g +SELECT 1 as one, 2 as two \gx (title='foo bar') +\g -- \bind (extended query protocol) --- Deactivated for SplendidDataTest: SELECT 1 \bind \g --- Deactivated for SplendidDataTest: SELECT $1 \bind 'foo' \g --- Deactivated for SplendidDataTest: SELECT $1, $2 \bind 'foo' 'bar' \g +SELECT 1 \bind \g +SELECT $1 \bind 'foo' \g +SELECT $1, $2 \bind 'foo' 'bar' \g -- errors -- parse error --- Deactivated for SplendidDataTest: SELECT foo \bind \g +SELECT foo \bind \g -- tcop error --- Deactivated for SplendidDataTest: SELECT 1 \; SELECT 2 \bind \g +SELECT 1 \; SELECT 2 \bind \g -- bind error --- Deactivated for SplendidDataTest: SELECT $1, $2 \bind 'foo' \g +SELECT $1, $2 \bind 'foo' \g -- \gset @@ -85,7 +83,7 @@ select 97 as "EOF", 'ok' as _foo \gset IGNORE select 1 as x, 2 as y \gset pref01_ \\ \echo :pref01_x select 3 as x, 4 as y \gset pref01_ \echo :pref01_x \echo :pref01_y select 5 as x, 6 as y \gset pref01_ \\ \g \echo :pref01_x :pref01_y --- Deactivated for SplendidDataTest: select 7 as x, 8 as y \g \gset pref01_ \echo :pref01_x :pref01_y +select 7 as x, 8 as y \g \gset pref01_ \echo :pref01_x :pref01_y -- NULL should unset the variable \set var2 xyz @@ -97,7 +95,7 @@ select 10 as test01, 20 as test02 from generate_series(1,3) \gset select 10 as test01, 20 as test02 from generate_series(1,0) \gset -- \gset returns no tuples --- Deactivated for SplendidDataTest: select a from generate_series(1, 10) as a where a = 11 \gset +select a from generate_series(1, 10) as a where a = 11 \gset \echo :ROW_COUNT -- \gset should work in FETCH_COUNT mode too @@ -112,44 +110,44 @@ select 10 as test01, 20 as test02 from generate_series(1,0) \gset -- \gdesc --- Deactivated for SplendidDataTest: SELECT --- Deactivated for SplendidDataTest: NULL AS zero, --- Deactivated for SplendidDataTest: 1 AS one, --- Deactivated for SplendidDataTest: 2.0 AS two, --- Deactivated for SplendidDataTest: 'three' AS three, --- Deactivated for SplendidDataTest: $1 AS four, --- Deactivated for SplendidDataTest: sin($2) as five, --- Deactivated for SplendidDataTest: 'foo'::varchar(4) as six, --- Deactivated for SplendidDataTest: CURRENT_DATE AS now +SELECT + NULL AS zero, + 1 AS one, + 2.0 AS two, + 'three' AS three, + $1 AS four, + sin($2) as five, + 'foo'::varchar(4) as six, + CURRENT_DATE AS now \gdesc -- should work with tuple-returning utilities, such as EXECUTE --- Deactivated for SplendidDataTest: PREPARE test AS SELECT 1 AS first, 2 AS second; --- Deactivated for SplendidDataTest: EXECUTE test \gdesc --- Deactivated for SplendidDataTest: EXPLAIN EXECUTE test \gdesc +PREPARE test AS SELECT 1 AS first, 2 AS second; +EXECUTE test \gdesc +EXPLAIN EXECUTE test \gdesc -- should fail cleanly - syntax error --- Deactivated for SplendidDataTest: SELECT 1 + \gdesc +SELECT 1 + \gdesc -- check behavior with empty results --- Deactivated for SplendidDataTest: SELECT \gdesc --- Deactivated for SplendidDataTest: CREATE TABLE bububu(a int) \gdesc +SELECT \gdesc +CREATE TABLE bububu(a int) \gdesc -- subject command should not have executed --- Deactivated for SplendidDataTest: TABLE bububu; -- fail +TABLE bububu; -- fail -- query buffer should remain unchanged --- Deactivated for SplendidDataTest: SELECT 1 AS x, 'Hello', 2 AS y, true AS "dirty\name" +SELECT 1 AS x, 'Hello', 2 AS y, true AS "dirty\name" \gdesc \g -- all on one line --- Deactivated for SplendidDataTest: SELECT 3 AS x, 'Hello', 4 AS y, true AS "dirty\name" \gdesc \g +SELECT 3 AS x, 'Hello', 4 AS y, true AS "dirty\name" \gdesc \g -- test for server bug #17983 with empty statement in aborted transaction set search_path = default; begin; --- Deactivated for SplendidDataTest: bogus; +bogus; ; \gdesc rollback; @@ -157,22 +155,22 @@ rollback; -- \gexec create temporary table gexec_test(a int, b text, c date, d float); --- Deactivated for SplendidDataTest: select format('create index on gexec_test(%I)', attname) --- Deactivated for SplendidDataTest: from pg_attribute --- Deactivated for SplendidDataTest: where attrelid = 'gexec_test'::regclass and attnum > 0 --- Deactivated for SplendidDataTest: rder by attnum +select format('create index on gexec_test(%I)', attname) +from pg_attribute +where attrelid = 'gexec_test'::regclass and attnum > 0 +order by attnum \gexec -- \gexec should work in FETCH_COUNT mode too -- (though the fetch limit applies to the executed queries not the meta query) \set FETCH_COUNT 1 --- Deactivated for SplendidDataTest: select 'select 1 as ones', 'select x.y, x.y*2 as double from generate_series(1,4) as x(y)' --- Deactivated for SplendidDataTest: union all --- Deactivated for SplendidDataTest: select 'drop table gexec_test', NULL --- Deactivated for SplendidDataTest: union all --- Deactivated for SplendidDataTest: select 'drop table gexec_test', 'select ''2000-01-01''::date as party_over' --- Deactivated for SplendidDataTest: \gexec +select 'select 1 as ones', 'select x.y, x.y*2 as double from generate_series(1,4) as x(y)' +union all +select 'drop table gexec_test', NULL +union all +select 'drop table gexec_test', 'select ''2000-01-01''::date as party_over' +\gexec \unset FETCH_COUNT @@ -193,10 +191,10 @@ create temporary table gexec_test(a int, b text, c date, d float); -- test multi-line headers, wrapping, and newline indicators -- in aligned, unaligned, and wrapped formats --- Deactivated for SplendidDataTest: prepare q as select array_to_string(array_agg(repeat('x',2*n)),E'\n') as "ab +prepare q as select array_to_string(array_agg(repeat('x',2*n)),E'\n') as "ab --- Deactivated for SplendidDataTest: c", array_to_string(array_agg(repeat('y',20-2*n)),E'\n') as "a --- Deactivated for SplendidDataTest: bc" from generate_series(1,10) as n(n) group by n>1 order by n>1; +c", array_to_string(array_agg(repeat('y',20-2*n)),E'\n') as "a +bc" from generate_series(1,10) as n(n) group by n>1 order by n>1; \pset linestyle ascii @@ -205,54 +203,54 @@ create temporary table gexec_test(a int, b text, c date, d float); \pset border 0 \pset format unaligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format aligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format wrapped --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 1 \pset format unaligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format aligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format wrapped --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 2 \pset format unaligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format aligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format wrapped --- Deactivated for SplendidDataTest: execute q; +execute q; \pset expanded on \pset columns 20 \pset border 0 \pset format unaligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format aligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format wrapped --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 1 \pset format unaligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format aligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format wrapped --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 2 \pset format unaligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format aligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format wrapped --- Deactivated for SplendidDataTest: execute q; +execute q; \pset linestyle old-ascii @@ -261,54 +259,54 @@ create temporary table gexec_test(a int, b text, c date, d float); \pset border 0 \pset format unaligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format aligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format wrapped --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 1 \pset format unaligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format aligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format wrapped --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 2 \pset format unaligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format aligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format wrapped --- Deactivated for SplendidDataTest: execute q; +execute q; \pset expanded on \pset columns 20 \pset border 0 \pset format unaligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format aligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format wrapped --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 1 \pset format unaligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format aligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format wrapped --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 2 \pset format unaligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format aligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format wrapped --- Deactivated for SplendidDataTest: execute q; +execute q; deallocate q; @@ -322,81 +320,81 @@ prepare q as select repeat('x',2*n) as "0123456789abcdef", repeat('y',20-2*n) as \pset border 0 \pset format unaligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format aligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format wrapped --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 1 \pset format unaligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format aligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format wrapped --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 2 \pset format unaligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format aligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format wrapped --- Deactivated for SplendidDataTest: execute q; +execute q; \pset expanded on \pset columns 30 \pset border 0 \pset format unaligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format aligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format wrapped --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 1 \pset format unaligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format aligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format wrapped --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 2 \pset format unaligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format aligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format wrapped --- Deactivated for SplendidDataTest: execute q; +execute q; \pset expanded on \pset columns 20 \pset border 0 \pset format unaligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format aligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format wrapped --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 1 \pset format unaligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format aligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format wrapped --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 2 \pset format unaligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format aligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format wrapped --- Deactivated for SplendidDataTest: execute q; +execute q; \pset linestyle old-ascii @@ -405,56 +403,67 @@ prepare q as select repeat('x',2*n) as "0123456789abcdef", repeat('y',20-2*n) as \pset border 0 \pset format unaligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format aligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format wrapped --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 1 \pset format unaligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format aligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format wrapped --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 2 \pset format unaligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format aligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format wrapped --- Deactivated for SplendidDataTest: execute q; +execute q; \pset expanded on \pset border 0 \pset format unaligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format aligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format wrapped --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 1 \pset format unaligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format aligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format wrapped --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 2 \pset format unaligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format aligned --- Deactivated for SplendidDataTest: execute q; +execute q; \pset format wrapped --- Deactivated for SplendidDataTest: execute q; +execute q; deallocate q; +-- expanded output with short-width columns +\pset border 2 +\pset expanded on +create table psql_short_tab(a int, b int); +insert into psql_short_tab values(10,20),(30,40); +\pset format aligned +select * from psql_short_tab; +\pset format wrapped +select * from psql_short_tab; +drop table psql_short_tab; + \pset linestyle ascii \pset border 1 @@ -571,23 +580,23 @@ prepare q as \pset expanded off \pset border 0 --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 1 --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 2 --- Deactivated for SplendidDataTest: execute q; +execute q; \pset expanded on \pset border 0 --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 1 --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 2 --- Deactivated for SplendidDataTest: execute q; +execute q; deallocate q; @@ -613,10 +622,10 @@ prepare q as from generate_series(1,2) as n; \pset expanded off --- Deactivated for SplendidDataTest: execute q; +execute q; \pset expanded on --- Deactivated for SplendidDataTest: execute q; +execute q; deallocate q; @@ -662,24 +671,24 @@ prepare q as \pset expanded off \pset border 0 --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 1 --- Deactivated for SplendidDataTest: execute q; +execute q; \pset tableattr foobar --- Deactivated for SplendidDataTest: execute q; +execute q; \pset tableattr \pset expanded on \pset border 0 --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 1 --- Deactivated for SplendidDataTest: execute q; +execute q; \pset tableattr foobar --- Deactivated for SplendidDataTest: execute q; +execute q; \pset tableattr deallocate q; @@ -707,29 +716,29 @@ prepare q as \pset expanded off \pset border 0 --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 1 --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 2 --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 3 --- Deactivated for SplendidDataTest: execute q; +execute q; \pset expanded on \pset border 0 --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 1 --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 2 --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 3 --- Deactivated for SplendidDataTest: execute q; +execute q; deallocate q; @@ -756,36 +765,36 @@ prepare q as \pset expanded off \pset border 0 --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 1 --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 2 --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 3 --- Deactivated for SplendidDataTest: execute q; +execute q; \pset tableattr lr --- Deactivated for SplendidDataTest: execute q; +execute q; \pset tableattr \pset expanded on \pset border 0 --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 1 --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 2 --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 3 --- Deactivated for SplendidDataTest: execute q; +execute q; \pset tableattr lr --- Deactivated for SplendidDataTest: execute q; +execute q; \pset tableattr deallocate q; @@ -813,25 +822,25 @@ prepare q as \pset expanded off \pset border 0 --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 1 --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 2 --- Deactivated for SplendidDataTest: execute q; +execute q; \pset expanded on \pset border 0 --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 1 --- Deactivated for SplendidDataTest: execute q; +execute q; \pset border 2 --- Deactivated for SplendidDataTest: execute q; +execute q; --- Deactivated for SplendidDataTest: deallocate q; +deallocate q; -- check ambiguous format requests @@ -868,8 +877,8 @@ drop table psql_serial_tab; select 'okay'; select 'still okay'; \else --- Deactivated for SplendidDataTest: not okay; --- Deactivated for SplendidDataTest: still not okay + not okay; + still not okay \endif -- at this point query buffer should still have last valid line @@ -877,80 +886,80 @@ drop table psql_serial_tab; -- \if should work okay on part of a query select --- Deactivated for SplendidDataTest: \if true --- Deactivated for SplendidDataTest: 42 --- Deactivated for SplendidDataTest: \else --- Deactivated for SplendidDataTest: (bogus --- Deactivated for SplendidDataTest: \endif + \if true + 42 + \else + (bogus + \endif forty_two; --- Deactivated for SplendidDataTest: select \if false \\ (bogus \else \\ 42 \endif \\ forty_two; +select \if false \\ (bogus \else \\ 42 \endif \\ forty_two; -- test a large nested if using a variety of true-equivalents --- Deactivated for SplendidDataTest: \if true --- Deactivated for SplendidDataTest: \if 1 --- Deactivated for SplendidDataTest: \if yes --- Deactivated for SplendidDataTest: \if on --- Deactivated for SplendidDataTest: \echo 'all true' --- Deactivated for SplendidDataTest: \else --- Deactivated for SplendidDataTest: \echo 'should not print #1-1' --- Deactivated for SplendidDataTest: \endif --- Deactivated for SplendidDataTest: \else --- Deactivated for SplendidDataTest: \echo 'should not print #1-2' --- Deactivated for SplendidDataTest: \endif --- Deactivated for SplendidDataTest: -- Deactivated for SplendidDataTest: \else --- Deactivated for SplendidDataTest: \echo 'should not print #1-3' --- Deactivated for SplendidDataTest: \endif --- Deactivated for SplendidDataTest: \else --- Deactivated for SplendidDataTest: \echo 'should not print #1-4' --- Deactivated for SplendidDataTest: \endif +\if true + \if 1 + \if yes + \if on + \echo 'all true' + \else + \echo 'should not print #1-1' + \endif + \else + \echo 'should not print #1-2' + \endif + \else + \echo 'should not print #1-3' + \endif +\else + \echo 'should not print #1-4' +\endif -- test a variety of false-equivalents in an if/elif/else structure --- Deactivated for SplendidDataTest: \if false --- Deactivated for SplendidDataTest: \echo 'should not print #2-1' --- Deactivated for SplendidDataTest: \elif 0 --- Deactivated for SplendidDataTest: \echo 'should not print #2-2' --- Deactivated for SplendidDataTest: \elif no --- Deactivated for SplendidDataTest: \echo 'should not print #2-3' --- Deactivated for SplendidDataTest: \elif off --- Deactivated for SplendidDataTest: \echo 'should not print #2-4' --- Deactivated for SplendidDataTest: \else --- Deactivated for SplendidDataTest: \echo 'all false' --- Deactivated for SplendidDataTest: \endif +\if false + \echo 'should not print #2-1' +\elif 0 + \echo 'should not print #2-2' +\elif no + \echo 'should not print #2-3' +\elif off + \echo 'should not print #2-4' +\else + \echo 'all false' +\endif -- test true-false elif after initial true branch --- Deactivated for SplendidDataTest: \if true --- Deactivated for SplendidDataTest: \echo 'should print #2-5' --- Deactivated for SplendidDataTest: \elif true --- Deactivated for SplendidDataTest: \echo 'should not print #2-6' --- Deactivated for SplendidDataTest: \elif false --- Deactivated for SplendidDataTest: \echo 'should not print #2-7' --- Deactivated for SplendidDataTest: \else --- Deactivated for SplendidDataTest: \echo 'should not print #2-8' --- Deactivated for SplendidDataTest: \endif +\if true + \echo 'should print #2-5' +\elif true + \echo 'should not print #2-6' +\elif false + \echo 'should not print #2-7' +\else + \echo 'should not print #2-8' +\endif -- test simple true-then-else --- Deactivated for SplendidDataTest: \if true --- Deactivated for SplendidDataTest: \echo 'first thing true' --- Deactivated for SplendidDataTest: \else --- Deactivated for SplendidDataTest: \echo 'should not print #3-1' --- Deactivated for SplendidDataTest: \endif +\if true + \echo 'first thing true' +\else + \echo 'should not print #3-1' +\endif -- test simple false-true-else --- Deactivated for SplendidDataTest: \if false --- Deactivated for SplendidDataTest: \echo 'should not print #4-1' --- Deactivated for SplendidDataTest: \elif true --- Deactivated for SplendidDataTest: \echo 'second thing true' --- Deactivated for SplendidDataTest: \else --- Deactivated for SplendidDataTest: \echo 'should not print #5-1' --- Deactivated for SplendidDataTest: \endif +\if false + \echo 'should not print #4-1' +\elif true + \echo 'second thing true' +\else + \echo 'should not print #5-1' +\endif -- invalid boolean expressions are false --- Deactivated for SplendidDataTest: \if invalid boolean expression --- Deactivated for SplendidDataTest: \echo 'will not print #6-1' --- Deactivated for SplendidDataTest: \else --- Deactivated for SplendidDataTest: \echo 'will print anyway #6-2' --- Deactivated for SplendidDataTest: \endif +\if invalid boolean expression + \echo 'will not print #6-1' +\else + \echo 'will print anyway #6-2' +\endif -- test un-matched endif \endif @@ -973,17 +982,17 @@ select \elif \endif --- Deactivated for SplendidDataTest: -- test if-endif matching in a false branch --- Deactivated for SplendidDataTest: \if false --- Deactivated for SplendidDataTest: \if false --- Deactivated for SplendidDataTest: \echo 'should not print #7-1' --- Deactivated for SplendidDataTest: \else --- Deactivated for SplendidDataTest: \echo 'should not print #7-2' --- Deactivated for SplendidDataTest: \endif --- Deactivated for SplendidDataTest: \echo 'should not print #7-3' --- Deactivated for SplendidDataTest: \else --- Deactivated for SplendidDataTest: \echo 'should print #7-4' --- Deactivated for SplendidDataTest: \endif +-- test if-endif matching in a false branch +\if false + \if false + \echo 'should not print #7-1' + \else + \echo 'should not print #7-2' + \endif + \echo 'should not print #7-3' +\else + \echo 'should print #7-4' +\endif -- show that vars and backticks are not expanded when ignoring extra args \set foo bar @@ -992,85 +1001,88 @@ select -- show that vars and backticks are not expanded and commands are ignored -- when in a false if-branch --- Deactivated for SplendidDataTest: \set try_to_quit '\\q' --- Deactivated for SplendidDataTest: \if false --- Deactivated for SplendidDataTest: :try_to_quit --- Deactivated for SplendidDataTest: \echo `nosuchcommand` :foo :'foo' :"foo" --- Deactivated for SplendidDataTest: \pset fieldsep | `nosuchcommand` :foo :'foo' :"foo" --- Deactivated for SplendidDataTest: \a --- Deactivated for SplendidDataTest: \C arg1 --- Deactivated for SplendidDataTest: \c arg1 arg2 arg3 arg4 --- Deactivated for SplendidDataTest: \cd arg1 --- Deactivated for SplendidDataTest: \conninfo --- Deactivated for SplendidDataTest: \copy arg1 arg2 arg3 arg4 arg5 arg6 --- Deactivated for SplendidDataTest: \copyright --- Deactivated for SplendidDataTest: SELECT 1 as one, 2, 3 \crosstabview --- Deactivated for SplendidDataTest: \dt arg1 --- Deactivated for SplendidDataTest: \e arg1 arg2 --- Deactivated for SplendidDataTest: \ef whole_line --- Deactivated for SplendidDataTest: \ev whole_line --- Deactivated for SplendidDataTest: \echo arg1 arg2 arg3 arg4 arg5 --- Deactivated for SplendidDataTest: \echo arg1 --- Deactivated for SplendidDataTest: \encoding arg1 --- Deactivated for SplendidDataTest: \errverbose --- Deactivated for SplendidDataTest: \f arg1 --- Deactivated for SplendidDataTest: \g arg1 --- Deactivated for SplendidDataTest: \gx arg1 --- Deactivated for SplendidDataTest: \gexec --- Deactivated for SplendidDataTest: SELECT 1 AS one \gset --- Deactivated for SplendidDataTest: \h --- Deactivated for SplendidDataTest: \? --- Deactivated for SplendidDataTest: \html --- Deactivated for SplendidDataTest: \i arg1 --- Deactivated for SplendidDataTest: \ir arg1 --- Deactivated for SplendidDataTest: \l arg1 --- Deactivated for SplendidDataTest: \lo arg1 arg2 --- Deactivated for SplendidDataTest: \lo_list --- Deactivated for SplendidDataTest: \o arg1 --- Deactivated for SplendidDataTest: \p --- Deactivated for SplendidDataTest: \password arg1 --- Deactivated for SplendidDataTest: \prompt arg1 arg2 --- Deactivated for SplendidDataTest: \pset arg1 arg2 --- Deactivated for SplendidDataTest: \q --- Deactivated for SplendidDataTest: \reset --- Deactivated for SplendidDataTest: \s arg1 --- Deactivated for SplendidDataTest: \set arg1 arg2 arg3 arg4 arg5 arg6 arg7 --- Deactivated for SplendidDataTest: \setenv arg1 arg2 --- Deactivated for SplendidDataTest: \sf whole_line --- Deactivated for SplendidDataTest: \sv whole_line --- Deactivated for SplendidDataTest: \t arg1 --- Deactivated for SplendidDataTest: \T arg1 --- Deactivated for SplendidDataTest: \timing arg1 --- Deactivated for SplendidDataTest: \unset arg1 --- Deactivated for SplendidDataTest: \w arg1 --- Deactivated for SplendidDataTest: \watch arg1 arg2 --- Deactivated for SplendidDataTest: \x arg1 --- Deactivated for SplendidDataTest: -- \else here is eaten as part of OT_FILEPIPE argument --- Deactivated for SplendidDataTest: \w |/no/such/file \else --- Deactivated for SplendidDataTest: -- \endif here is eaten as part of whole-line argument --- Deactivated for SplendidDataTest: \! whole_line \endif --- Deactivated for SplendidDataTest: \z --- Deactivated for SplendidDataTest: \else --- Deactivated for SplendidDataTest: \echo 'should print #8-1' --- Deactivated for SplendidDataTest: \endif +\set try_to_quit '\\q' +\if false + :try_to_quit + \echo `nosuchcommand` :foo :'foo' :"foo" + \pset fieldsep | `nosuchcommand` :foo :'foo' :"foo" + \a + SELECT $1 \bind 1 \g + \C arg1 + \c arg1 arg2 arg3 arg4 + \cd arg1 + \conninfo + \copy arg1 arg2 arg3 arg4 arg5 arg6 + \copyright + SELECT 1 as one, 2, 3 \crosstabview + \dt arg1 + \e arg1 arg2 + \ef whole_line + \ev whole_line + \echo arg1 arg2 arg3 arg4 arg5 + \echo arg1 + \encoding arg1 + \errverbose + \f arg1 + \g arg1 + \gx arg1 + \gexec + SELECT 1 AS one \gset + \h + \? + \html + \i arg1 + \ir arg1 + \l arg1 + \lo arg1 arg2 + \lo_list + \o arg1 + \p + \password arg1 + \prompt arg1 arg2 + \pset arg1 arg2 + \q + \reset + \restrict test + \s arg1 + \set arg1 arg2 arg3 arg4 arg5 arg6 arg7 + \setenv arg1 arg2 + \sf whole_line + \sv whole_line + \t arg1 + \T arg1 + \timing arg1 + \unrestrict not_valid + \unset arg1 + \w arg1 + \watch arg1 arg2 + \x arg1 + -- \else here is eaten as part of OT_FILEPIPE argument + \w |/no/such/file \else + -- \endif here is eaten as part of whole-line argument + \! whole_line \endif + \z +\else + \echo 'should print #8-1' +\endif -- :{?...} defined variable test --- Deactivated for SplendidDataTest: \set i 1 --- Deactivated for SplendidDataTest: \if :{?i} --- Deactivated for SplendidDataTest: \echo '#9-1 ok, variable i is defined' --- Deactivated for SplendidDataTest: \else --- Deactivated for SplendidDataTest: \echo 'should not print #9-2' --- Deactivated for SplendidDataTest: \endif +\set i 1 +\if :{?i} + \echo '#9-1 ok, variable i is defined' +\else + \echo 'should not print #9-2' +\endif --- Deactivated for SplendidDataTest: \if :{?no_such_variable} --- Deactivated for SplendidDataTest: \echo 'should not print #10-1' --- Deactivated for SplendidDataTest: \else --- Deactivated for SplendidDataTest: \echo '#10-2 ok, variable no_such_variable is not defined' --- Deactivated for SplendidDataTest: \endif +\if :{?no_such_variable} + \echo 'should not print #10-1' +\else + \echo '#10-2 ok, variable no_such_variable is not defined' +\endif --- Deactivated for SplendidDataTest: SELECT :{?i} AS i_is_defined; +SELECT :{?i} AS i_is_defined; --- Deactivated for SplendidDataTest: SELECT NOT :{?no_such_var} AS no_such_var_is_not_defined; +SELECT NOT :{?no_such_var} AS no_such_var_is_not_defined; -- SHOW_CONTEXT @@ -1098,12 +1110,12 @@ end $$; -- test printing and clearing the query buffer SELECT 1; \p --- Deactivated for SplendidDataTest: SELECT 2 \r +SELECT 2 \r \p --- Deactivated for SplendidDataTest: SELECT 3 \p --- Deactivated for SplendidDataTest: UNION SELECT 4 \p --- Deactivated for SplendidDataTest: UNION SELECT 5 --- Deactivated for SplendidDataTest: ORDER BY 1; +SELECT 3 \p +UNION SELECT 4 \p +UNION SELECT 5 +ORDER BY 1; \r \p @@ -1116,7 +1128,7 @@ SELECT 1 AS stuff UNION SELECT 2; \echo 'number of rows:' :ROW_COUNT -- syntax error --- Deactivated for SplendidDataTest: SELECT 1 UNION; +SELECT 1 UNION; \echo 'error:' :ERROR \echo 'error code:' :SQLSTATE \echo 'number of rows:' :ROW_COUNT @@ -1142,7 +1154,7 @@ DROP TABLE this_table_does_not_exist; -- nondefault verbosity error settings (except verbose, which is too unstable) \set VERBOSITY terse --- Deactivated for SplendidDataTest: SELECT 1 UNION; +SELECT 1 UNION; \echo 'error:' :ERROR \echo 'error code:' :SQLSTATE \echo 'last error message:' :LAST_ERROR_MESSAGE @@ -1156,20 +1168,20 @@ SELECT 1/0; \set VERBOSITY default -- working \gdesc --- Deactivated for SplendidDataTest: SELECT 3 AS three, 4 AS four \gdesc +SELECT 3 AS three, 4 AS four \gdesc \echo 'error:' :ERROR \echo 'error code:' :SQLSTATE \echo 'number of rows:' :ROW_COUNT -- \gdesc with an error --- Deactivated for SplendidDataTest: SELECT 4 AS \gdesc +SELECT 4 AS \gdesc \echo 'error:' :ERROR \echo 'error code:' :SQLSTATE \echo 'number of rows:' :ROW_COUNT \echo 'last error message:' :LAST_ERROR_MESSAGE \echo 'last error code:' :LAST_ERROR_SQLSTATE --- check row count for a cursor-fetched query +-- check row count for a query with chunked results \set FETCH_COUNT 10 select unique2 from tenk1 order by unique2 limit 19; \echo 'error:' :ERROR @@ -1407,7 +1419,7 @@ SELECT 1 AS one \; SELECT warn('1.5') \; SELECT 2 AS two ; SELECT 3 AS three \; SELECT warn('3.5') \; SELECT 4 AS four \gset \echo :three :four -- syntax error stops all processing --- Deactivated for SplendidDataTest: SELECT 5 \; SELECT 6 + \; SELECT warn('6.5') \; SELECT 7 ; +SELECT 5 \; SELECT 6 + \; SELECT warn('6.5') \; SELECT 7 ; -- with aborted transaction, stop on first error BEGIN \; SELECT 8 AS eight \; SELECT 9/0 AS nine \; ROLLBACK \; SELECT 10 AS ten ; -- close previously aborted transaction @@ -1425,9 +1437,9 @@ COPY psql_comics TO STDOUT \; TRUNCATE psql_comics \; DROP TABLE psql_comics \; SELECT 'ok' AS "done" ; --- Deactivated for SplendidDataTest: Moe --- Deactivated for SplendidDataTest: Susie --- Deactivated for SplendidDataTest: \. +Moe +Susie +\. \set SHOW_ALL_RESULTS off SELECT 1 AS one \; SELECT warn('1.5') \; SELECT 2 AS two ; @@ -1446,11 +1458,11 @@ CREATE TEMPORARY TABLE reload_output( line text ); --- Deactivated for SplendidDataTest: SELECT 1 AS a \g :g_out_file +SELECT 1 AS a \g :g_out_file COPY reload_output(line) FROM :'g_out_file'; --- Deactivated for SplendidDataTest: SELECT 2 AS b\; SELECT 3 AS c\; SELECT 4 AS d \g :g_out_file +SELECT 2 AS b\; SELECT 3 AS c\; SELECT 4 AS d \g :g_out_file COPY reload_output(line) FROM :'g_out_file'; --- Deactivated for SplendidDataTest: COPY (SELECT 'foo') TO STDOUT \; COPY (SELECT 'bar') TO STDOUT \g :g_out_file +COPY (SELECT 'foo') TO STDOUT \; COPY (SELECT 'bar') TO STDOUT \g :g_out_file COPY reload_output(line) FROM :'g_out_file'; SELECT line FROM reload_output ORDER BY lineno; @@ -1487,7 +1499,7 @@ TRUNCATE TABLE reload_output; -- The data goes to :o_out_file with no status generated. COPY (SELECT 'foo1') TO STDOUT \; COPY (SELECT 'bar1') TO STDOUT; -- Combination of \o and \g file with multiple COPY queries. --- Deactivated for SplendidDataTest: COPY (SELECT 'foo2') TO STDOUT \; COPY (SELECT 'bar2') TO STDOUT \g :g_out_file +COPY (SELECT 'foo2') TO STDOUT \; COPY (SELECT 'bar2') TO STDOUT \g :g_out_file \o -- Check the contents of the files generated. @@ -1897,3 +1909,8 @@ CREATE TABLE defprivs (a int); \z defprivs \pset null '' DROP TABLE defprivs; + +* +* +*/ +select true; \ No newline at end of file diff --git a/parser/src/test/resources/postgres/test/regress/sql/publication.sql b/parser/src/test/resources/postgres/test/regress/sql/publication.sql index 91cc833..b1aabb1 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/publication.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/publication.sql @@ -1114,6 +1114,85 @@ DROP TABLE sch1.tbl1; DROP SCHEMA sch1 cascade; DROP SCHEMA sch2 cascade; +-- Test that the INSERT ON CONFLICT command correctly checks REPLICA IDENTITY +-- when the target table is published. +CREATE TABLE testpub_insert_onconfl_no_ri (a int unique, b int); +CREATE TABLE testpub_insert_onconfl_parted (a int unique, b int) PARTITION by RANGE (a); +CREATE TABLE testpub_insert_onconfl_part_no_ri PARTITION OF testpub_insert_onconfl_parted FOR VALUES FROM (1) TO (10); + +SET client_min_messages = 'ERROR'; +CREATE PUBLICATION pub1 FOR ALL TABLES; +RESET client_min_messages; + +-- fail - missing REPLICA IDENTITY +INSERT INTO testpub_insert_onconfl_no_ri VALUES (1, 1) ON CONFLICT (a) DO UPDATE SET b = 2; + +-- ok - no updates +INSERT INTO testpub_insert_onconfl_no_ri VALUES (1, 1) ON CONFLICT DO NOTHING; + +-- fail - missing REPLICA IDENTITY in partition testpub_insert_onconfl_no_ri +INSERT INTO testpub_insert_onconfl_parted VALUES (1, 1) ON CONFLICT (a) DO UPDATE SET b = 2; + +-- ok - no updates +INSERT INTO testpub_insert_onconfl_parted VALUES (1, 1) ON CONFLICT DO NOTHING; + +DROP PUBLICATION pub1; +DROP TABLE testpub_insert_onconfl_no_ri; +DROP TABLE testpub_insert_onconfl_parted; + +-- Test that the MERGE command correctly checks REPLICA IDENTITY when the +-- target table is published. +CREATE TABLE testpub_merge_no_ri (a int, b int); +CREATE TABLE testpub_merge_pk (a int primary key, b int); + +SET client_min_messages = 'ERROR'; +CREATE PUBLICATION pub1 FOR ALL TABLES; +RESET client_min_messages; + +-- fail - missing REPLICA IDENTITY +MERGE INTO testpub_merge_no_ri USING testpub_merge_pk s ON s.a >= 1 + WHEN MATCHED THEN UPDATE SET b = s.b; + +-- fail - missing REPLICA IDENTITY +MERGE INTO testpub_merge_no_ri USING testpub_merge_pk s ON s.a >= 1 + WHEN MATCHED THEN DELETE; + +-- ok - insert and do nothing are not restricted +MERGE INTO testpub_merge_no_ri USING testpub_merge_pk s ON s.a >= 1 + WHEN MATCHED THEN DO NOTHING + WHEN NOT MATCHED THEN INSERT (a, b) VALUES (0, 0); + +-- ok - REPLICA IDENTITY is DEFAULT and table has a PK +MERGE INTO testpub_merge_pk USING testpub_merge_no_ri s ON s.a >= 1 + WHEN MATCHED AND s.a > 0 THEN UPDATE SET b = s.b + WHEN MATCHED THEN DELETE; + +DROP PUBLICATION pub1; +DROP TABLE testpub_merge_no_ri; +DROP TABLE testpub_merge_pk; + RESET SESSION AUTHORIZATION; DROP ROLE regress_publication_user, regress_publication_user2; DROP ROLE regress_publication_user_dummy; + +-- stage objects for pg_dump tests +CREATE SCHEMA pubme CREATE TABLE t0 (c int, d int) CREATE TABLE t1 (c int); +CREATE SCHEMA pubme2 CREATE TABLE t0 (c int, d int); +SET client_min_messages = 'ERROR'; +CREATE PUBLICATION dump_pub_qual_1ct FOR + TABLE ONLY pubme.t0 (c, d) WHERE (c > 0); +CREATE PUBLICATION dump_pub_qual_2ct FOR + TABLE ONLY pubme.t0 (c) WHERE (c > 0), + TABLE ONLY pubme.t1 (c); +CREATE PUBLICATION dump_pub_nsp_1ct FOR + TABLES IN SCHEMA pubme; +CREATE PUBLICATION dump_pub_nsp_2ct FOR + TABLES IN SCHEMA pubme, + TABLES IN SCHEMA pubme2; +CREATE PUBLICATION dump_pub_all FOR + TABLE ONLY pubme.t0, + TABLE ONLY pubme.t1 WHERE (c < 0), + TABLES IN SCHEMA pubme, + TABLES IN SCHEMA pubme2 + WITH (publish_via_partition_root = true); +RESET client_min_messages; diff --git a/parser/src/test/resources/postgres/test/regress/sql/returning.sql b/parser/src/test/resources/postgres/test/regress/sql/returning.sql index a460f82..8a2a2a5 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/returning.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/returning.sql @@ -132,6 +132,30 @@ DELETE FROM foo WHERE f2 = 'zit' RETURNING *; SELECT * FROM foo; SELECT * FROM voo; +-- Check use of a whole-row variable for an un-flattenable view +CREATE TEMP VIEW foo_v AS SELECT * FROM foo OFFSET 0; +UPDATE foo SET f2 = foo_v.f2 FROM foo_v WHERE foo_v.f1 = foo.f1 + RETURNING foo_v; +SELECT * FROM foo; + +-- Check use of a whole-row variable for an inlined set-returning function +CREATE FUNCTION foo_f() RETURNS SETOF foo AS + $$ SELECT * FROM foo OFFSET 0 $$ LANGUAGE sql STABLE; +UPDATE foo SET f2 = foo_f.f2 FROM foo_f() WHERE foo_f.f1 = foo.f1 + RETURNING foo_f; +SELECT * FROM foo; +DROP FUNCTION foo_f(); + +-- As above, but SRF is defined to return a composite type +CREATE TYPE foo_t AS (f1 int, f2 text, f3 int, f4 int8); +CREATE FUNCTION foo_f() RETURNS SETOF foo_t AS + $$ SELECT * FROM foo OFFSET 0 $$ LANGUAGE sql STABLE; +UPDATE foo SET f2 = foo_f.f2 FROM foo_f() WHERE foo_f.f1 = foo.f1 + RETURNING foo_f; +SELECT * FROM foo; +DROP FUNCTION foo_f(); +DROP TYPE foo_t; + -- Try a join case CREATE TEMP TABLE joinme (f2j text, other int); diff --git a/parser/src/test/resources/postgres/test/regress/sql/rowsecurity.sql b/parser/src/test/resources/postgres/test/regress/sql/rowsecurity.sql index b58ec5e..ef85899 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/rowsecurity.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/rowsecurity.sql @@ -249,7 +249,7 @@ COPY t1 FROM stdin WITH ; -- Deactivated for SplendidDataTest: 102 2 bbb -- Deactivated for SplendidDataTest: 103 3 ccc -- Deactivated for SplendidDataTest: 104 4 dad --- Deactivated for SplendidDataTest: \. +\. CREATE TABLE t2 (c float) INHERITS (t1); GRANT ALL ON t2 TO public; @@ -259,7 +259,7 @@ COPY t2 FROM stdin; -- Deactivated for SplendidDataTest: 202 2 bcd 2.2 -- Deactivated for SplendidDataTest: 203 3 cde 3.3 -- Deactivated for SplendidDataTest: 204 4 def 4.4 --- Deactivated for SplendidDataTest: \. +\. CREATE TABLE t3 (id int not null primary key, c text, b text, a int); ALTER TABLE t3 INHERIT t1; @@ -269,7 +269,7 @@ COPY t3(id, a,b,c) FROM stdin; -- Deactivated for SplendidDataTest: 301 1 xxx X -- Deactivated for SplendidDataTest: 302 2 yyy Y -- Deactivated for SplendidDataTest: 303 3 zzz Z --- Deactivated for SplendidDataTest: \. +\. CREATE POLICY p1 ON t1 FOR ALL TO PUBLIC USING (a % 2 = 0); -- be even number CREATE POLICY p2 ON t2 FOR ALL TO PUBLIC USING (a % 2 = 1); -- be odd number @@ -1648,14 +1648,14 @@ COPY copy_t FROM STDIN; --ok -- Deactivated for SplendidDataTest: 2 bcd -- Deactivated for SplendidDataTest: 3 cde -- Deactivated for SplendidDataTest: 4 def --- Deactivated for SplendidDataTest: \. +\. SET row_security TO ON; COPY copy_t FROM STDIN; --ok -- Deactivated for SplendidDataTest: 1 abc -- Deactivated for SplendidDataTest: 2 bcd -- Deactivated for SplendidDataTest: 3 cde -- Deactivated for SplendidDataTest: 4 def --- Deactivated for SplendidDataTest: \. +\. -- Check COPY FROM as user with permissions. SET SESSION AUTHORIZATION regress_rls_bob; @@ -1672,7 +1672,7 @@ COPY copy_t FROM STDIN; --ok -- Deactivated for SplendidDataTest: 2 bcd -- Deactivated for SplendidDataTest: 3 cde -- Deactivated for SplendidDataTest: 4 def --- Deactivated for SplendidDataTest: \. +\. -- Check COPY FROM as user without permissions. SET SESSION AUTHORIZATION regress_rls_carol; @@ -2170,7 +2170,7 @@ DROP VIEW rls_view; DROP TABLE rls_tbl; DROP TABLE ref_tbl; --- Leaky operator test +-- Leaky operator tests CREATE TABLE rls_tbl (a int); INSERT INTO rls_tbl SELECT x/10 FROM generate_series(1, 100) x; ANALYZE rls_tbl; @@ -2185,9 +2185,58 @@ CREATE FUNCTION op_leak(int, int) RETURNS bool CREATE OPERATOR <<< (procedure = op_leak, leftarg = int, rightarg = int, restrict = scalarltsel); SELECT * FROM rls_tbl WHERE a <<< 1000; +RESET SESSION AUTHORIZATION; + +CREATE TABLE rls_child_tbl () INHERITS (rls_tbl); +INSERT INTO rls_child_tbl SELECT x/10 FROM generate_series(1, 100) x; +ANALYZE rls_child_tbl; + +CREATE TABLE rls_ptbl (a int) PARTITION BY RANGE (a); +CREATE TABLE rls_part PARTITION OF rls_ptbl FOR VALUES FROM (-100) TO (100); +INSERT INTO rls_ptbl SELECT x/10 FROM generate_series(1, 100) x; +ANALYZE rls_ptbl, rls_part; + +ALTER TABLE rls_ptbl ENABLE ROW LEVEL SECURITY; +ALTER TABLE rls_part ENABLE ROW LEVEL SECURITY; +GRANT SELECT ON rls_ptbl TO regress_rls_alice; +GRANT SELECT ON rls_part TO regress_rls_alice; +CREATE POLICY p1 ON rls_tbl USING (a < 0); +CREATE POLICY p2 ON rls_ptbl USING (a < 0); +CREATE POLICY p3 ON rls_part USING (a < 0); + +SET SESSION AUTHORIZATION regress_rls_alice; +SELECT * FROM rls_tbl WHERE a <<< 1000; +SELECT * FROM rls_child_tbl WHERE a <<< 1000; +SELECT * FROM rls_ptbl WHERE a <<< 1000; +SELECT * FROM rls_part WHERE a <<< 1000; +SELECT * FROM (SELECT * FROM rls_tbl UNION ALL + SELECT * FROM rls_tbl) t WHERE a <<< 1000; +SELECT * FROM (SELECT * FROM rls_child_tbl UNION ALL + SELECT * FROM rls_child_tbl) t WHERE a <<< 1000; +RESET SESSION AUTHORIZATION; + +REVOKE SELECT ON rls_tbl FROM regress_rls_alice; +CREATE VIEW rls_tbl_view AS SELECT * FROM rls_tbl; + +ALTER TABLE rls_child_tbl ENABLE ROW LEVEL SECURITY; +GRANT SELECT ON rls_child_tbl TO regress_rls_alice; +CREATE POLICY p4 ON rls_child_tbl USING (a < 0); + +SET SESSION AUTHORIZATION regress_rls_alice; +SELECT * FROM rls_tbl WHERE a <<< 1000; +SELECT * FROM rls_tbl_view WHERE a <<< 1000; +SELECT * FROM rls_child_tbl WHERE a <<< 1000; +SELECT * FROM (SELECT * FROM rls_tbl UNION ALL + SELECT * FROM rls_tbl) t WHERE a <<< 1000; +SELECT * FROM (SELECT * FROM rls_child_tbl UNION ALL + SELECT * FROM rls_child_tbl) t WHERE a <<< 1000; DROP OPERATOR <<< (int, int); DROP FUNCTION op_leak(int, int); RESET SESSION AUTHORIZATION; +DROP TABLE rls_part; +DROP TABLE rls_ptbl; +DROP TABLE rls_child_tbl; +DROP VIEW rls_tbl_view; DROP TABLE rls_tbl; -- Bug #16006: whole-row Vars in a policy don't play nice with sub-selects @@ -2225,8 +2274,66 @@ execute q; set role regress_rls_bob; execute q; +-- make sure RLS dependencies in CTEs are handled +reset role; +create or replace function rls_f() returns setof rls_t + stable language sql + as $$ with cte as (select * from rls_t) select * from cte $$; +prepare r as select current_user, * from rls_f(); +set role regress_rls_alice; +execute r; +set role regress_rls_bob; +execute r; + +-- make sure RLS dependencies in subqueries are handled +reset role; +create or replace function rls_f() returns setof rls_t + stable language sql + as $$ select * from (select * from rls_t) _ $$; +prepare s as select current_user, * from rls_f(); +set role regress_rls_alice; +execute s; +set role regress_rls_bob; +execute s; + +-- make sure RLS dependencies in sublinks are handled +reset role; +create or replace function rls_f() returns setof rls_t + stable language sql + as $$ select exists(select * from rls_t)::text $$; +prepare t as select current_user, * from rls_f(); +set role regress_rls_alice; +execute t; +set role regress_rls_bob; +execute t; + +-- make sure RLS dependencies are handled when coercion projections are inserted +reset role; +create or replace function rls_f() returns setof rls_t + stable language sql + as $$ select * from (select array_agg(c) as cs from rls_t) _ group by cs $$; +prepare u as select current_user, * from rls_f(); +set role regress_rls_alice; +execute u; +set role regress_rls_bob; +execute u; + +-- make sure RLS dependencies in security invoker views are handled +reset role; +create view rls_v with (security_invoker) as select * from rls_t; +grant select on rls_v to regress_rls_alice, regress_rls_bob; +create or replace function rls_f() returns setof rls_t + stable language sql + as $$ select * from rls_v $$; +prepare v as select current_user, * from rls_f(); +set role regress_rls_alice; +execute v; +set role regress_rls_bob; +execute v; + RESET ROLE; DROP FUNCTION rls_f(); +DROP VIEW rls_v; DROP TABLE rls_t; -- diff --git a/parser/src/test/resources/postgres/test/regress/sql/sqljson.sql b/parser/src/test/resources/postgres/test/regress/sql/sqljson.sql index a8848e7..0ed94ae 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/sqljson.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/sqljson.sql @@ -146,6 +146,8 @@ SELECT JSON_OBJECT('a': '1', 'b': NULL, 'c': 2); SELECT JSON_OBJECT('a': '1', 'b': NULL, 'c': 2 NULL ON NULL); SELECT JSON_OBJECT('a': '1', 'b': NULL, 'c': 2 ABSENT ON NULL); +SELECT JSON_OBJECT(1: 1, '2': NULL, '3': 1, repeat('x', 1000): 1, 2: repeat('a', 100) WITH UNIQUE); + SELECT JSON_OBJECT(1: 1, '1': NULL WITH UNIQUE); SELECT JSON_OBJECT(1: 1, '1': NULL ABSENT ON NULL WITH UNIQUE); SELECT JSON_OBJECT(1: 1, '1': NULL NULL ON NULL WITH UNIQUE RETURNING jsonb); @@ -158,6 +160,17 @@ SELECT JSON_OBJECT(1: 1, '2': NULL, '1': 1 ABSENT ON NULL WITH UNIQUE RETURNING SELECT JSON_OBJECT(1: 1, '2': NULL, '1': 1 ABSENT ON NULL WITHOUT UNIQUE RETURNING jsonb); SELECT JSON_OBJECT(1: 1, '2': NULL, '3': 1, 4: NULL, '5': 'a' ABSENT ON NULL WITH UNIQUE RETURNING jsonb); +-- BUG: https://postgr.es/m/CADXhmgTJtJZK9A3Na_ry%2BXrq-ghjcejBRhcRMzWZvbd__QdgJA%40mail.gmail.com +-- datum_to_jsonb_internal() didn't catch keys that are casts instead of a simple scalar +CREATE TYPE mood AS ENUM ('happy', 'sad', 'neutral'); +CREATE FUNCTION mood_to_json(mood) RETURNS json AS $$ + SELECT to_json($1::text); +$$ LANGUAGE sql IMMUTABLE; +CREATE CAST (mood AS json) WITH FUNCTION mood_to_json(mood) AS IMPLICIT; +SELECT JSON_OBJECT('happy'::mood: '123'::jsonb); +DROP CAST (mood AS json); +DROP FUNCTION mood_to_json; +DROP TYPE mood; -- JSON_ARRAY() SELECT JSON_ARRAY(); @@ -194,6 +207,8 @@ SELECT JSON_ARRAY(SELECT i FROM (VALUES (NULL::int[]), ('{1,2}'), (NULL), (NULL) --SELECT JSON_ARRAY(SELECT i FROM (VALUES (NULL::int[]), ('{1,2}'), (NULL), (NULL), ('{3,4}'), (NULL)) foo(i) NULL ON NULL); --SELECT JSON_ARRAY(SELECT i FROM (VALUES (NULL::int[]), ('{1,2}'), (NULL), (NULL), ('{3,4}'), (NULL)) foo(i) NULL ON NULL RETURNING jsonb); SELECT JSON_ARRAY(SELECT i FROM (VALUES (3), (1), (NULL), (2)) foo(i) ORDER BY i); +SELECT JSON_ARRAY(WITH x AS (SELECT 1) VALUES (TRUE)); + -- Should fail SELECT JSON_ARRAY(SELECT FROM (VALUES (1)) foo(i)); SELECT JSON_ARRAY(SELECT i, i FROM (VALUES (1)) foo(i)); @@ -291,6 +306,9 @@ FROM (VALUES (1, 1), (1, NULL), (2, 2)) foo(k, v); SELECT JSON_OBJECTAGG(k: v ABSENT ON NULL WITH UNIQUE KEYS RETURNING jsonb) FROM (VALUES (1, 1), (0, NULL),(4, null), (5, null),(6, null),(2, 2)) foo(k, v); +SELECT JSON_OBJECTAGG(mod(i,100): (i)::text FORMAT JSON WITH UNIQUE) +FROM generate_series(0, 199) i; + -- Test JSON_OBJECT deparsing EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_OBJECT('foo' : '1' FORMAT JSON, 'bar' : 'baz' RETURNING json); @@ -385,6 +403,28 @@ SELECT NULL::text IS JSON; SELECT NULL::bytea IS JSON; SELECT NULL::int IS JSON; +-- A user-defined string-category type with no implicit cast to text must +-- produce a clean error rather than crash for IS JSON / JSON() input +-- (per bug #19491). +CREATE FUNCTION sqljson_mystr_in(cstring) RETURNS sqljson_mystr + AS 'textin' LANGUAGE internal IMMUTABLE STRICT; +CREATE FUNCTION sqljson_mystr_out(sqljson_mystr) RETURNS cstring + AS 'textout' LANGUAGE internal IMMUTABLE STRICT; +CREATE TYPE sqljson_mystr ( + INPUT = sqljson_mystr_in, + OUTPUT = sqljson_mystr_out, + LIKE = text, + CATEGORY = 'S' +); +SELECT '{"a":1}'::sqljson_mystr IS JSON; -- error +SELECT JSON('{"a":1}'::sqljson_mystr WITH UNIQUE KEYS); -- error +-- An implicit cast to text lets the same query work normally. +CREATE CAST (sqljson_mystr AS text) WITHOUT FUNCTION AS IMPLICIT; +SELECT '{"a":1}'::sqljson_mystr IS JSON; +\set VERBOSITY terse +DROP TYPE sqljson_mystr CASCADE; +\set VERBOSITY default + SELECT '' IS JSON; SELECT bytea '\x00' IS JSON; @@ -483,3 +523,17 @@ SELECT JSON_OBJECTAGG(i: ('111' || i)::bytea FORMAT JSON WITH UNIQUE RETURNING v CREATE DOMAIN sqljson_char2 AS char(2) CHECK (VALUE NOT IN ('12')); SELECT JSON_SERIALIZE('123' RETURNING sqljson_char2); SELECT JSON_SERIALIZE('12' RETURNING sqljson_char2); + +-- Bug #18657: JsonValueExpr.raw_expr was not initialized in ExecInitExprRec() +-- causing the Aggrefs contained in it to also not be initialized, which led +-- to a crash in ExecBuildAggTrans() as mentioned in the bug report: +-- https://postgr.es/m/18657-1b90ccce2b16bdb8@postgresql.org +CREATE FUNCTION volatile_one() RETURNS int AS $$ BEGIN RETURN 1; END; $$ LANGUAGE plpgsql VOLATILE; +CREATE FUNCTION stable_one() RETURNS int AS $$ BEGIN RETURN 1; END; $$ LANGUAGE plpgsql STABLE; +EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_OBJECT('a': JSON_OBJECTAGG('b': volatile_one() RETURNING text) FORMAT JSON); +SELECT JSON_OBJECT('a': JSON_OBJECTAGG('b': volatile_one() RETURNING text) FORMAT JSON); +EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_OBJECT('a': JSON_OBJECTAGG('b': stable_one() RETURNING text) FORMAT JSON); +SELECT JSON_OBJECT('a': JSON_OBJECTAGG('b': stable_one() RETURNING text) FORMAT JSON); +EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_OBJECT('a': JSON_OBJECTAGG('b': 1 RETURNING text) FORMAT JSON); +SELECT JSON_OBJECT('a': JSON_OBJECTAGG('b': 1 RETURNING text) FORMAT JSON); +DROP FUNCTION volatile_one, stable_one; diff --git a/parser/src/test/resources/postgres/test/regress/sql/sqljson_queryfuncs.sql b/parser/src/test/resources/postgres/test/regress/sql/sqljson_queryfuncs.sql index 21ff778..a5d5e25 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/sqljson_queryfuncs.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/sqljson_queryfuncs.sql @@ -146,11 +146,11 @@ select json_value('{"a": "1.234"}', '$.a' returning int error on error); SELECT JSON_VALUE(NULL::jsonb, '$'); SELECT - JSON_QUERY(js, '$'), - JSON_QUERY(js, '$' WITHOUT WRAPPER), - JSON_QUERY(js, '$' WITH CONDITIONAL WRAPPER), - JSON_QUERY(js, '$' WITH UNCONDITIONAL ARRAY WRAPPER), - JSON_QUERY(js, '$' WITH ARRAY WRAPPER) + JSON_QUERY(js, '$') AS "unspec", + JSON_QUERY(js, '$' WITHOUT WRAPPER) AS "without", + JSON_QUERY(js, '$' WITH CONDITIONAL WRAPPER) AS "with cond", + JSON_QUERY(js, '$' WITH UNCONDITIONAL ARRAY WRAPPER) AS "with uncond", + JSON_QUERY(js, '$' WITH ARRAY WRAPPER) AS "with" FROM (VALUES (jsonb 'null'), @@ -327,11 +327,11 @@ CREATE TABLE test_jsonb_constraints ( CONSTRAINT test_jsonb_constraint1 CHECK (js IS JSON) CONSTRAINT test_jsonb_constraint2 - CHECK (JSON_EXISTS(js::jsonb, '$.a' PASSING i + 5 AS int, i::text AS txt, array[1,2,3] as arr)) + CHECK (JSON_EXISTS(js::jsonb, '$.a' PASSING i + 5 AS int, i::text AS "TXT", array[1,2,3] as arr)) CONSTRAINT test_jsonb_constraint3 CHECK (JSON_VALUE(js::jsonb, '$.a' RETURNING int DEFAULT '12' ON EMPTY ERROR ON ERROR) > i) CONSTRAINT test_jsonb_constraint4 - CHECK (JSON_QUERY(js::jsonb, '$.a' WITH CONDITIONAL WRAPPER EMPTY OBJECT ON ERROR) < jsonb '[10]') + CHECK (JSON_QUERY(js::jsonb, '$.a' WITH CONDITIONAL WRAPPER EMPTY OBJECT ON ERROR) = jsonb '[10]') CONSTRAINT test_jsonb_constraint5 CHECK (JSON_QUERY(js::jsonb, '$.a' RETURNING char(5) OMIT QUOTES EMPTY ARRAY ON EMPTY) > 'a' COLLATE "C") ); @@ -353,7 +353,6 @@ INSERT INTO test_jsonb_constraints VALUES ('1', 1); INSERT INTO test_jsonb_constraints VALUES ('[]'); INSERT INTO test_jsonb_constraints VALUES ('{"b": 1}', 1); INSERT INTO test_jsonb_constraints VALUES ('{"a": 1}', 1); -INSERT INTO test_jsonb_constraints VALUES ('{"a": 7}', 1); INSERT INTO test_jsonb_constraints VALUES ('{"a": 10}', 1); DROP TABLE test_jsonb_constraints; @@ -451,6 +450,7 @@ SELECT JSON_VALUE(jsonb '{"a": 123}', '$' || '.' || 'a'); SELECT JSON_VALUE(jsonb '{"a": 123}', '$' || '.' || 'b' DEFAULT 'foo' ON EMPTY); SELECT JSON_QUERY(jsonb '{"a": 123}', '$' || '.' || 'a'); SELECT JSON_QUERY(jsonb '{"a": 123}', '$' || '.' || 'a' WITH WRAPPER); +SELECT JSON_QUERY(jsonb '{"a": 123}', ('$' || '.' || 'a' || NULL)::date WITH WRAPPER); -- Should fail (invalid path) SELECT JSON_QUERY(jsonb '{"a": 123}', 'error' || ' ' || 'error'); @@ -461,11 +461,15 @@ SELECT JSON_QUERY(NULL FORMAT JSON, '$'); -- Test non-const jsonpath CREATE TEMP TABLE jsonpaths (path) AS SELECT '$'; SELECT json_value('"aaa"', path RETURNING json) FROM jsonpaths; +SELECT json_value('"aaa"', jsonpaths RETURNING json) FROM jsonpaths; -- Test PASSING argument parsing SELECT JSON_QUERY(jsonb 'null', '$xyz' PASSING 1 AS xy); SELECT JSON_QUERY(jsonb 'null', '$xy' PASSING 1 AS xyz); SELECT JSON_QUERY(jsonb 'null', '$xyz' PASSING 1 AS xyz); +SELECT JSON_QUERY(jsonb 'null', '$Xyz' PASSING 1 AS Xyz); +SELECT JSON_QUERY(jsonb 'null', '$Xyz' PASSING 1 AS "Xyz"); +SELECT JSON_QUERY(jsonb 'null', '$"Xyz"' PASSING 1 AS "Xyz"); -- Test ON ERROR / EMPTY value validity for the function; all fail. SELECT JSON_EXISTS(jsonb '1', '$' DEFAULT 1 ON ERROR); diff --git a/parser/src/test/resources/postgres/test/regress/sql/stats.sql b/parser/src/test/resources/postgres/test/regress/sql/stats.sql index b78066a..67066af 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/stats.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/stats.sql @@ -404,9 +404,17 @@ COMMIT; -- check that the stats are reset. SELECT (n_tup_ins + n_tup_upd) > 0 AS has_data FROM pg_stat_all_tables WHERE relid = 'pg_shdescription'::regclass; +-- stats_reset may not be set for datid=0 and shared objects in +-- pg_stat_database, so reset once. SELECT pg_stat_reset_single_table_counters('pg_shdescription'::regclass); SELECT (n_tup_ins + n_tup_upd) > 0 AS has_data FROM pg_stat_all_tables WHERE relid = 'pg_shdescription'::regclass; +SELECT stats_reset AS shared_db_reset_before + FROM pg_stat_database WHERE datid = 0 \gset +-- Second reset for comparison. +SELECT pg_stat_reset_single_table_counters('pg_shdescription'::regclass); +SELECT stats_reset > :'shared_db_reset_before'::timestamptz AS has_updated + FROM pg_stat_database WHERE datid = 0; -- set back comment \if :{?description_before} @@ -857,4 +865,20 @@ DROP TABLE brin_hot_3; SET enable_seqscan = on; +-- Test that estimation of relation size works with tuples wider than the +-- relation fillfactor. We create a table with wide inline attributes and +-- low fillfactor, insert rows and then see how many rows EXPLAIN shows +-- before running analyze. We disable autovacuum so that it does not +-- interfere with the test. +CREATE TABLE table_fillfactor ( + n char(1000) +) with (fillfactor=10, autovacuum_enabled=off); + +INSERT INTO table_fillfactor +SELECT 'x' FROM generate_series(1,1000); + +SELECT * FROM check_estimated_rows('SELECT * FROM table_fillfactor'); + +DROP TABLE table_fillfactor; + -- End of Stats Test diff --git a/parser/src/test/resources/postgres/test/regress/sql/stats_ext.sql b/parser/src/test/resources/postgres/test/regress/sql/stats_ext.sql index 6059c4a..032b5db 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/stats_ext.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/stats_ext.sql @@ -48,6 +48,18 @@ CREATE STATISTICS tst ON x, x, y, x, x, (x || 'x'), (y + 1), (x || 'x'), (x || ' CREATE STATISTICS tst ON (x || 'x'), (x || 'x'), (y + 1), (x || 'x'), (x || 'x'), (y + 1), (x || 'x'), (x || 'x'), (y + 1) FROM ext_stats_test; CREATE STATISTICS tst ON (x || 'x'), (x || 'x'), y FROM ext_stats_test; CREATE STATISTICS tst (unrecognized) ON x, y FROM ext_stats_test; +-- unsupported targets +CREATE STATISTICS tst ON a FROM (VALUES (x)) AS foo; +CREATE STATISTICS tst ON a FROM foo NATURAL JOIN bar; +CREATE STATISTICS tst ON a FROM (SELECT * FROM ext_stats_test) AS foo; +CREATE STATISTICS tst ON a FROM ext_stats_test s TABLESAMPLE system (x); +CREATE STATISTICS tst ON a FROM XMLTABLE('foo' PASSING 'bar' COLUMNS a text); +CREATE STATISTICS tst ON a FROM JSON_TABLE(jsonb '123', '$' COLUMNS (item int)); +CREATE FUNCTION tftest(int) returns table(a int, b int) as $$ +SELECT $1, $1+i FROM generate_series(1,5) g(i); +$$ LANGUAGE sql IMMUTABLE STRICT; +CREATE STATISTICS alt_stat2 ON a FROM tftest(1); +DROP FUNCTION tftest; -- incorrect expressions CREATE STATISTICS tst ON (y) FROM ext_stats_test; -- single column reference -- Deactivated for SplendidDataTest: CREATE STATISTICS tst ON y + z FROM ext_stats_test; -- missing parentheses @@ -65,6 +77,14 @@ DROP STATISTICS ab1_a_b_stats; ALTER STATISTICS ab1_a_b_stats RENAME TO ab1_a_b_stats_new; RESET SESSION AUTHORIZATION; DROP ROLE regress_stats_ext; +CREATE STATISTICS pg_temp.stats_ext_temp ON a, b FROM ab1; +SELECT regexp_replace(pg_describe_object(tableoid, oid, 0), + 'pg_temp_[0-9]*', 'pg_temp_REDACTED') AS descr, + pg_statistics_obj_is_visible(oid) AS visible + FROM pg_statistic_ext + WHERE stxname = 'stats_ext_temp'; +DROP STATISTICS stats_ext_temp; -- shall fail +DROP STATISTICS pg_temp.stats_ext_temp; CREATE STATISTICS IF NOT EXISTS ab1_a_b_stats ON a, b FROM ab1; DROP STATISTICS ab1_a_b_stats; @@ -1641,7 +1661,14 @@ CREATE FUNCTION op_leak(int, int) RETURNS bool LANGUAGE plpgsql; CREATE OPERATOR <<< (procedure = op_leak, leftarg = int, rightarg = int, restrict = scalarltsel); +CREATE FUNCTION op_leak(record, record) RETURNS bool + AS 'BEGIN RAISE NOTICE ''op_leak => %, %'', $1, $2; RETURN $1 < $2; END' + LANGUAGE plpgsql; +CREATE OPERATOR <<< (procedure = op_leak, leftarg = record, rightarg = record, + restrict = scalarltsel); SELECT * FROM tststats.priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Permission denied +SELECT * FROM tststats.priv_test_tbl t + WHERE a <<< 0 AND (b <<< 0 OR t.* <<< (1, 1) IS NOT NULL); -- Permission denied DELETE FROM tststats.priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Permission denied -- Grant access via a security barrier view, but hide all data @@ -1653,18 +1680,48 @@ GRANT SELECT, DELETE ON tststats.priv_test_view TO regress_stats_user1; -- Should now have access via the view, but see nothing and leak nothing SET SESSION AUTHORIZATION regress_stats_user1; SELECT * FROM tststats.priv_test_view WHERE a <<< 0 AND b <<< 0; -- Should not leak +SELECT * FROM tststats.priv_test_view t + WHERE a <<< 0 AND (b <<< 0 OR t.* <<< (1, 1) IS NOT NULL); -- Should not leak DELETE FROM tststats.priv_test_view WHERE a <<< 0 AND b <<< 0; -- Should not leak -- Grant table access, but hide all data with RLS RESET SESSION AUTHORIZATION; ALTER TABLE tststats.priv_test_tbl ENABLE ROW LEVEL SECURITY; +CREATE POLICY priv_test_tbl_pol ON tststats.priv_test_tbl USING (2 * a < 0); GRANT SELECT, DELETE ON tststats.priv_test_tbl TO regress_stats_user1; -- Should now have direct table access, but see nothing and leak nothing SET SESSION AUTHORIZATION regress_stats_user1; SELECT * FROM tststats.priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Should not leak +SELECT * FROM tststats.priv_test_tbl t + WHERE a <<< 0 AND (b <<< 0 OR t.* <<< (1, 1) IS NOT NULL); -- Should not leak DELETE FROM tststats.priv_test_tbl WHERE a <<< 0 AND b <<< 0; -- Should not leak +-- Create plain inheritance parent table with no access permissions +RESET SESSION AUTHORIZATION; +CREATE TABLE tststats.priv_test_parent_tbl (a int, b int); +ALTER TABLE tststats.priv_test_tbl INHERIT tststats.priv_test_parent_tbl; + +-- Should not have access to parent, and should leak nothing +SET SESSION AUTHORIZATION regress_stats_user1; +SELECT * FROM tststats.priv_test_parent_tbl WHERE a <<< 0 AND b <<< 0; -- Permission denied +SELECT * FROM tststats.priv_test_parent_tbl t + WHERE a <<< 0 AND (b <<< 0 OR t.* <<< (1, 1) IS NOT NULL); -- Permission denied +DELETE FROM tststats.priv_test_parent_tbl WHERE a <<< 0 AND b <<< 0; -- Permission denied + +-- Grant table access to parent, but hide all data with RLS +RESET SESSION AUTHORIZATION; +ALTER TABLE tststats.priv_test_parent_tbl ENABLE ROW LEVEL SECURITY; +CREATE POLICY priv_test_parent_tbl_pol ON tststats.priv_test_parent_tbl USING (2 * a < 0); +GRANT SELECT, DELETE ON tststats.priv_test_parent_tbl TO regress_stats_user1; + +-- Should now have direct table access to parent, but see nothing and leak nothing +SET SESSION AUTHORIZATION regress_stats_user1; +SELECT * FROM tststats.priv_test_parent_tbl WHERE a <<< 0 AND b <<< 0; -- Should not leak +SELECT * FROM tststats.priv_test_parent_tbl t + WHERE a <<< 0 AND (b <<< 0 OR t.* <<< (1, 1) IS NOT NULL); -- Should not leak +DELETE FROM tststats.priv_test_parent_tbl WHERE a <<< 0 AND b <<< 0; -- Should not leak + -- privilege checks for pg_stats_ext and pg_stats_ext_exprs RESET SESSION AUTHORIZATION; CREATE TABLE stats_ext_tbl (id INT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, col TEXT); @@ -1691,10 +1748,46 @@ SELECT statistics_name, most_common_vals FROM pg_stats_ext x SELECT statistics_name, most_common_vals FROM pg_stats_ext_exprs x WHERE tablename = 'stats_ext_tbl' ORDER BY ROW(x.*); +-- CREATE STATISTICS checks for CREATE on the schema +RESET SESSION AUTHORIZATION; +CREATE SCHEMA sts_sch1 CREATE TABLE sts_sch1.tbl (a INT, b INT, c INT GENERATED ALWAYS AS (b * 2) STORED); +CREATE SCHEMA sts_sch2; +GRANT USAGE ON SCHEMA sts_sch1, sts_sch2 TO regress_stats_user1; +ALTER TABLE sts_sch1.tbl OWNER TO regress_stats_user1; +SET SESSION AUTHORIZATION regress_stats_user1; +CREATE STATISTICS ON a, b, c FROM sts_sch1.tbl; +CREATE STATISTICS sts_sch2.fail ON a, b, c FROM sts_sch1.tbl; +RESET SESSION AUTHORIZATION; +GRANT CREATE ON SCHEMA sts_sch1 TO regress_stats_user1; +SET SESSION AUTHORIZATION regress_stats_user1; +CREATE STATISTICS ON a, b, c FROM sts_sch1.tbl; +CREATE STATISTICS sts_sch2.fail ON a, b, c FROM sts_sch1.tbl; +RESET SESSION AUTHORIZATION; +REVOKE CREATE ON SCHEMA sts_sch1 FROM regress_stats_user1; +GRANT CREATE ON SCHEMA sts_sch2 TO regress_stats_user1; +SET SESSION AUTHORIZATION regress_stats_user1; +CREATE STATISTICS ON a, b, c FROM sts_sch1.tbl; +CREATE STATISTICS sts_sch2.pass1 ON a, b, c FROM sts_sch1.tbl; +RESET SESSION AUTHORIZATION; +GRANT CREATE ON SCHEMA sts_sch1, sts_sch2 TO regress_stats_user1; +SET SESSION AUTHORIZATION regress_stats_user1; +CREATE STATISTICS ON a, b, c FROM sts_sch1.tbl; +CREATE STATISTICS sts_sch2.pass2 ON a, b, c FROM sts_sch1.tbl; + +-- re-creating statistics via ALTER TABLE bypasses checks for CREATE on schema +RESET SESSION AUTHORIZATION; +REVOKE CREATE ON SCHEMA sts_sch1, sts_sch2 FROM regress_stats_user1; +SET SESSION AUTHORIZATION regress_stats_user1; +ALTER TABLE sts_sch1.tbl ALTER COLUMN a TYPE SMALLINT; +ALTER TABLE sts_sch1.tbl ALTER COLUMN c SET EXPRESSION AS (a * 3); + -- Tidy up DROP OPERATOR <<< (int, int); DROP FUNCTION op_leak(int, int); +DROP OPERATOR <<< (record, record); +DROP FUNCTION op_leak(record, record); RESET SESSION AUTHORIZATION; DROP TABLE stats_ext_tbl; DROP SCHEMA tststats CASCADE; +DROP SCHEMA sts_sch1, sts_sch2 CASCADE; DROP USER regress_stats_user1; diff --git a/parser/src/test/resources/postgres/test/regress/sql/strings.sql b/parser/src/test/resources/postgres/test/regress/sql/strings.sql index 8228b2b..a1744c2 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/strings.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/strings.sql @@ -201,6 +201,29 @@ SELECT 'abcd\efg' SIMILAR TO '_bcd\%' ESCAPE '' AS true; SELECT 'abcdefg' SIMILAR TO '_bcd%' ESCAPE NULL AS null; SELECT 'abcdefg' SIMILAR TO '_bcd#%' ESCAPE '##' AS error; +-- Characters that should be left alone in character classes when a +-- SIMILAR TO regexp pattern is converted to POSIX style. +-- Underscore "_" +EXPLAIN (COSTS OFF) SELECT * FROM TEXT_TBL WHERE f1 SIMILAR TO '_[_[:alpha:]_]_'; +-- Percentage "%" +EXPLAIN (COSTS OFF) SELECT * FROM TEXT_TBL WHERE f1 SIMILAR TO '%[%[:alnum:]%]%'; +-- Dot "." +EXPLAIN (COSTS OFF) SELECT * FROM TEXT_TBL WHERE f1 SIMILAR TO '.[.[:alnum:].].'; +-- Dollar "$" +EXPLAIN (COSTS OFF) SELECT * FROM TEXT_TBL WHERE f1 SIMILAR TO '$[$[:alnum:]$]$'; +-- Opening parenthesis "(" +EXPLAIN (COSTS OFF) SELECT * FROM TEXT_TBL WHERE f1 SIMILAR TO '()[([:alnum:](]()'; +-- Caret "^" +EXPLAIN (COSTS OFF) SELECT * FROM TEXT_TBL WHERE f1 SIMILAR TO '^[^[:alnum:]^[^^][[^^]][\^][[\^]]\^]^'; +-- Closing square bracket "]" at the beginning of character class +EXPLAIN (COSTS OFF) SELECT * FROM TEXT_TBL WHERE f1 SIMILAR TO '[]%][^]%][^%]%'; +-- Closing square bracket effective after two carets at the beginning +-- of character class. +EXPLAIN (COSTS OFF) SELECT * FROM TEXT_TBL WHERE f1 SIMILAR TO '[^^]^'; +-- Closing square bracket after an escape sequence at the beginning of +-- a character closes the character class +EXPLAIN (COSTS OFF) SELECT * FROM TEXT_TBL WHERE f1 SIMILAR TO '[|a]%' ESCAPE '|'; + -- Test backslash escapes in regexp_replace's replacement string SELECT regexp_replace('1112223333', E'(\\d{3})(\\d{3})(\\d{4})', E'(\\1) \\2-\\3'); SELECT regexp_replace('foobarrbazz', E'(.)\\1', E'X\\&Y', 'g'); diff --git a/parser/src/test/resources/postgres/test/regress/sql/subselect.sql b/parser/src/test/resources/postgres/test/regress/sql/subselect.sql index af6e157..583cbd6 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/subselect.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/subselect.sql @@ -412,6 +412,15 @@ select (select view_a) from view_a; select (select (select view_a)) from view_a; select (select (a.*)::text) from view_a a; +-- +-- Test case for bug #19037: no relation entry for relid N +-- + +explain (costs off) +select (1 = any(array_agg(f1))) = any (select false) from int4_tbl; + +select (1 = any(array_agg(f1))) = any (select false) from int4_tbl; + -- -- Check that whole-row Vars reading the result of a subselect don't include -- any junk columns therein @@ -890,6 +899,23 @@ fetch backward all in c1; commit; +-- +-- Check that JsonConstructorExpr is treated as non-strict, and thus can be +-- wrapped in a PlaceHolderVar +-- + +begin; + +create temp table json_tab (a int); +insert into json_tab values (1); + +explain (verbose, costs off) +select * from json_tab t1 left join (select json_array(1, a) from json_tab t2) s on false; + +select * from json_tab t1 left join (select json_array(1, a) from json_tab t2) s on false; + +rollback; + -- -- Verify that we correctly flatten cases involving a subquery output -- expression that doesn't need to be wrapped in a PlaceHolderVar @@ -908,6 +934,37 @@ select relname::information_schema.sql_identifier as tname, * from right join pg_attribute a on a.attrelid = ss2.oid where tname = 'tenk1' and attnum = 1; +-- Check behavior when there's a lateral reference in the output expression +explain (verbose, costs off) +select t1.ten, sum(x) from + tenk1 t1 left join lateral ( + select t1.ten + t2.ten as x, t2.fivethous from tenk1 t2 + ) ss on t1.unique1 = ss.fivethous +group by t1.ten +order by t1.ten; + +select t1.ten, sum(x) from + tenk1 t1 left join lateral ( + select t1.ten + t2.ten as x, t2.fivethous from tenk1 t2 + ) ss on t1.unique1 = ss.fivethous +group by t1.ten +order by t1.ten; + +explain (verbose, costs off) +select t1.q1, x from + int8_tbl t1 left join + (int8_tbl t2 left join + lateral (select t2.q1+t3.q1 as x, * from int8_tbl t3) t3 on t2.q2 = t3.q2) + on t1.q2 = t2.q2 +order by 1, 2; + +select t1.q1, x from + int8_tbl t1 left join + (int8_tbl t2 left join + lateral (select t2.q1+t3.q1 as x, * from int8_tbl t3) t3 on t2.q2 = t3.q2) + on t1.q2 = t2.q2 +order by 1, 2; + -- -- Tests for CTE inlining behavior -- diff --git a/parser/src/test/resources/postgres/test/regress/sql/tablespace.sql b/parser/src/test/resources/postgres/test/regress/sql/tablespace.sql index d274d96..0f9c136 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/tablespace.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/tablespace.sql @@ -396,6 +396,12 @@ ALTER INDEX testschema.part_a_idx SET TABLESPACE pg_default; -- Fail, not empty DROP TABLESPACE regress_tblspace; +-- Adequate cache initialization before GRANT +\c - +BEGIN; +GRANT ALL ON TABLESPACE regress_tblspace TO PUBLIC; +ROLLBACK; + CREATE ROLE regress_tablespace_user1 login; CREATE ROLE regress_tablespace_user2 login; GRANT USAGE ON SCHEMA testschema TO regress_tablespace_user2; diff --git a/parser/src/test/resources/postgres/test/regress/sql/timestamptz.sql b/parser/src/test/resources/postgres/test/regress/sql/timestamptz.sql index ccfd90d..1aacf78 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/timestamptz.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/timestamptz.sql @@ -442,14 +442,18 @@ SELECT make_timestamptz(1973, 07, 15, 08, 15, 55.33, '+2') = '1973-07-15 08:15:5 -- full timezone names SELECT make_timestamptz(2014, 12, 10, 0, 0, 0, 'Europe/Prague') = timestamptz '2014-12-10 00:00:00 Europe/Prague'; SELECT make_timestamptz(2014, 12, 10, 0, 0, 0, 'Europe/Prague') AT TIME ZONE 'UTC'; -SELECT make_timestamptz(1846, 12, 10, 0, 0, 0, 'Asia/Manila') AT TIME ZONE 'UTC'; +SELECT make_timestamptz(1881, 12, 10, 0, 0, 0, 'Asia/Singapore') AT TIME ZONE 'UTC'; +SELECT make_timestamptz(1881, 12, 10, 0, 0, 0, 'Pacific/Honolulu') AT TIME ZONE 'UTC'; SELECT make_timestamptz(1881, 12, 10, 0, 0, 0, 'Europe/Paris') AT TIME ZONE 'UTC'; SELECT make_timestamptz(1910, 12, 24, 0, 0, 0, 'Nehwon/Lankhmar'); -- abbreviations SELECT make_timestamptz(2008, 12, 10, 10, 10, 10, 'EST'); SELECT make_timestamptz(2008, 12, 10, 10, 10, 10, 'EDT'); -SELECT make_timestamptz(2014, 12, 10, 10, 10, 10, 'PST8PDT'); +SELECT make_timestamptz(2014, 12, 10, 10, 10, 10, 'FOO8BAR'); + +-- POSIX +SELECT make_timestamptz(2014, 12, 10, 10, 10, 10, 'PST8PDT,M3.2.0,M11.1.0'); RESET TimeZone; diff --git a/parser/src/test/resources/postgres/test/regress/sql/triggers.sql b/parser/src/test/resources/postgres/test/regress/sql/triggers.sql index 267cf3c..92d700a 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/triggers.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/triggers.sql @@ -65,13 +65,13 @@ create unique index pkeys_i on pkeys (pkey1, pkey2); -- (fkey3) --> fkeys2 (pkey23) -- create trigger check_fkeys_pkey_exist - before insert or update on fkeys + after insert or update on fkeys for each row execute function check_primary_key ('fkey1', 'fkey2', 'pkeys', 'pkey1', 'pkey2'); create trigger check_fkeys_pkey2_exist - before insert or update on fkeys + after insert or update on fkeys for each row execute function check_primary_key ('fkey3', 'fkeys2', 'pkey23'); @@ -80,7 +80,7 @@ create trigger check_fkeys_pkey2_exist -- (fkey21, fkey22) --> pkeys (pkey1, pkey2) -- create trigger check_fkeys2_pkey_exist - before insert or update on fkeys2 + after insert or update on fkeys2 for each row execute procedure check_primary_key ('fkey21', 'fkey22', 'pkeys', 'pkey1', 'pkey2'); @@ -96,7 +96,7 @@ COMMENT ON TRIGGER check_fkeys2_pkey_exist ON fkeys2 IS NULL; -- fkeys (fkey1, fkey2) and fkeys2 (fkey21, fkey22) -- create trigger check_pkeys_fkey_cascade - before delete or update on pkeys + after delete or update on pkeys for each row execute procedure check_foreign_key (2, 'cascade', 'pkey1', 'pkey2', @@ -108,7 +108,7 @@ create trigger check_pkeys_fkey_cascade -- fkeys (fkey3) -- create trigger check_fkeys2_fkey_restrict - before delete or update on fkeys2 + after delete or update on fkeys2 for each row execute procedure check_foreign_key (1, 'restrict', 'pkey23', 'fkeys', 'fkey3'); @@ -307,7 +307,7 @@ COPY main_table (a,b) FROM stdin; -- Deactivated for SplendidDataTest: 30 10 -- Deactivated for SplendidDataTest: 50 35 -- Deactivated for SplendidDataTest: 80 15 --- Deactivated for SplendidDataTest: \. +\. CREATE FUNCTION trigger_func() RETURNS trigger LANGUAGE plpgsql AS ' BEGIN @@ -350,7 +350,7 @@ ALTER TABLE main_table DROP CONSTRAINT main_table_a_key; COPY main_table (a, b) FROM stdin; -- Deactivated for SplendidDataTest: 30 40 -- Deactivated for SplendidDataTest: 50 60 --- Deactivated for SplendidDataTest: \. +\. SELECT * FROM main_table ORDER BY a, b; @@ -380,7 +380,7 @@ INSERT INTO main_table (a) VALUES (123), (456); COPY main_table FROM stdin; -- Deactivated for SplendidDataTest: 123 999 -- Deactivated for SplendidDataTest: 456 999 --- Deactivated for SplendidDataTest: \. +\. DELETE FROM main_table WHERE a IN (123, 456); UPDATE main_table SET a = 50, b = 60; SELECT * FROM main_table ORDER BY a, b; @@ -1566,12 +1566,12 @@ delete from parted_stmt_trig; copy parted_stmt_trig(a) from stdin; -- Deactivated for SplendidDataTest: 1 -- Deactivated for SplendidDataTest: 2 --- Deactivated for SplendidDataTest: \. +\. -- insert via copy on the first partition copy parted_stmt_trig1(a) from stdin; -- Deactivated for SplendidDataTest: 1 --- Deactivated for SplendidDataTest: \. +\. -- Disabling a trigger in the parent table should disable children triggers too alter table parted_stmt_trig disable trigger trig_ins_after_parent; @@ -2116,7 +2116,12 @@ copy parent (a, b) from stdin; -- Deactivated for SplendidDataTest: AAA 42 -- Deactivated for SplendidDataTest: BBB 42 -- Deactivated for SplendidDataTest: CCC 42 --- Deactivated for SplendidDataTest: \. +\. + +-- check detach/reattach behavior; statement triggers with transition tables +-- should not prevent a table from becoming a partition again +alter table parent detach partition child1; +alter table parent attach partition child1 for values in ('AAA'); -- DML affecting parent sees tuples collected from children even if -- there is no transition table trigger on the children @@ -2137,7 +2142,7 @@ copy parent (a, b) from stdin; -- Deactivated for SplendidDataTest: AAA 42 -- Deactivated for SplendidDataTest: BBB 42 -- Deactivated for SplendidDataTest: CCC 42 --- Deactivated for SplendidDataTest: \. +\. -- insert into parent with a before trigger on a child tuple before -- insertion, and we capture the newly modified row in parent format @@ -2162,7 +2167,7 @@ copy parent (a, b) from stdin; -- Deactivated for SplendidDataTest: AAA 42 -- Deactivated for SplendidDataTest: BBB 42 -- Deactivated for SplendidDataTest: CCC 234 --- Deactivated for SplendidDataTest: \. +\. drop table child1, child2, child3, parent; drop function intercept_insert(); @@ -2195,6 +2200,52 @@ alter table parent attach partition child for values in ('AAA'); drop table child, parent; +-- +-- Verify access of transition tables with UPDATE triggers and tuples +-- moved across partitions. +-- +create or replace function dump_update_new() returns trigger language plpgsql as +$$ + begin + raise notice 'trigger = %, new table = %', TG_NAME, + (select string_agg(new_table::text, ', ' order by a) from new_table); + return null; + end; +$$; +create or replace function dump_update_old() returns trigger language plpgsql as +$$ + begin + raise notice 'trigger = %, old table = %', TG_NAME, + (select string_agg(old_table::text, ', ' order by a) from old_table); + return null; + end; +$$; +create table trans_tab_parent (a text) partition by list (a); +create table trans_tab_child1 partition of trans_tab_parent for values in ('AAA1', 'AAA2'); +create table trans_tab_child2 partition of trans_tab_parent for values in ('BBB1', 'BBB2'); +create trigger trans_tab_parent_update_trig + after update on trans_tab_parent referencing old table as old_table + for each statement execute procedure dump_update_old(); +create trigger trans_tab_parent_insert_trig + after insert on trans_tab_parent referencing new table as new_table + for each statement execute procedure dump_insert(); +create trigger trans_tab_parent_delete_trig + after delete on trans_tab_parent referencing old table as old_table + for each statement execute procedure dump_delete(); +insert into trans_tab_parent values ('AAA1'), ('BBB1'); +-- should not trigger access to new table when moving across partitions. +update trans_tab_parent set a = 'BBB2' where a = 'AAA1'; +drop trigger trans_tab_parent_update_trig on trans_tab_parent; +create trigger trans_tab_parent_update_trig + after update on trans_tab_parent referencing new table as new_table + for each statement execute procedure dump_update_new(); +-- should not trigger access to old table when moving across partitions. +update trans_tab_parent set a = 'AAA2' where a = 'BBB1'; +delete from trans_tab_parent; +-- clean up +drop table trans_tab_parent, trans_tab_child1, trans_tab_child2; +drop function dump_update_new, dump_update_old; + -- -- Verify behavior of statement triggers on (non-partition) -- inheritance hierarchy with transition tables; similar to the @@ -2282,14 +2333,19 @@ copy parent (a, b) from stdin; -- Deactivated for SplendidDataTest: AAA 42 -- Deactivated for SplendidDataTest: BBB 42 -- Deactivated for SplendidDataTest: CCC 42 --- Deactivated for SplendidDataTest: \. +\. -- same behavior for copy if there is an index (interesting because rows are -- captured by a different code path in copyfrom.c if there are indexes) create index on parent(b); copy parent (a, b) from stdin; -- Deactivated for SplendidDataTest: DDD 42 --- Deactivated for SplendidDataTest: \. +\. + +-- check disinherit/reinherit behavior; statement triggers with transition +-- tables should not prevent a table from becoming an inheritance child again +alter table child1 no inherit parent; +alter table child1 inherit parent; -- DML affecting parent sees tuples collected from children even if -- there is no transition table trigger on the children @@ -2355,6 +2411,10 @@ with wcte as (insert into table1 values (42)) with wcte as (insert into table1 values (43)) insert into table1 values (44); +with wcte as (insert into table1 values (45)) + merge into table1 using (values (46)) as v(a) on table1.a = v.a + when not matched then insert values (v.a); + select * from table1; select * from table2; diff --git a/parser/src/test/resources/postgres/test/regress/sql/tsearch.sql b/parser/src/test/resources/postgres/test/regress/sql/tsearch.sql index 8f03cc6..3132a8a 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/tsearch.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/tsearch.sql @@ -654,6 +654,14 @@ SELECT ts_headline('english', SELECT ts_headline('english', 'foo bar', to_tsquery('english', '')); +-- Test for large values of StartSel, StopSel and FragmentDelimiter +SELECT ts_headline('english', 'foo barbar', to_tsquery('english', 'foo'), + 'StartSel=' || repeat('x', 32768)); +SELECT ts_headline('english', 'foo barbar', to_tsquery('english', 'foo'), + 'StopSel=' || repeat('x', 32768)); +SELECT ts_headline('english', 'foo barbar', to_tsquery('english', 'foo'), + 'FragmentDelimiter=' || repeat('x', 32768)); + --Rewrite sub system CREATE TABLE test_tsquery (txtkeyword TEXT, txtsample TEXT); diff --git a/parser/src/test/resources/postgres/test/regress/sql/unicode.sql b/parser/src/test/resources/postgres/test/regress/sql/unicode.sql index 5fc0386..19135ea 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/unicode.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/unicode.sql @@ -44,3 +44,23 @@ FROM ORDER BY num; SELECT is_normalized('abc', 'def'); -- run-time error + +-- Hangul NFC recomposition tests +-- L+V -> LV composition (first and last) +SELECT normalize(U&'\1100\1161', NFC) = U&'\AC00' COLLATE "C" AS hangul_lv_first; +SELECT normalize(U&'\1112\1175', NFC) = U&'\D788' COLLATE "C" AS hangul_lv_last; +-- LV+T -> LVT composition +SELECT normalize(U&'\AC00\11A8', NFC) = U&'\AC01' COLLATE "C" AS hangul_lvt_first_t; +SELECT normalize(U&'\AC00\11C2', NFC) = U&'\AC1B' COLLATE "C" AS hangul_lvt_last_t; +SELECT normalize(U&'\D788\11A8', NFC) = U&'\D789' COLLATE "C" AS hangul_lvt_last_lv; +-- L+V+T -> LVT composition +SELECT normalize(U&'\1100\1161\11A8', NFC) = U&'\AC01' COLLATE "C" AS hangul_full_lvt; +SELECT normalize(U&'\1112\1175\11C2', NFC) = U&'\D7A3' COLLATE "C" AS hangul_full_lvt; +-- TBASE invalid T syllable +SELECT normalize(U&'\AC00\11A7', NFC) = U&'\AC00\11A7' COLLATE "C" AS hangul_tbase_not_combined; +SELECT normalize(U&'\1100\1161\11A7', NFC) = U&'\AC00\11A7' COLLATE "C" AS hangul_lv_tbase_separate; + +-- Hangul NFD decomposition tests +SELECT normalize(U&'\AC00', NFD) = U&'\1100\1161' COLLATE "C" AS hangul_nfd_lv; +SELECT normalize(U&'\AC01', NFD) = U&'\1100\1161\11A8' COLLATE "C" AS hangul_nfd_lvt; +SELECT normalize(U&'\D7A3', NFD) = U&'\1112\1175\11C2' COLLATE "C" AS hangul_nfd_last; diff --git a/parser/src/test/resources/postgres/test/regress/sql/union.sql b/parser/src/test/resources/postgres/test/regress/sql/union.sql index f882651..c52ca01 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/union.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/union.sql @@ -577,3 +577,7 @@ select * from tenk1 t1 left join lateral (select t1.tenthous from tenk2 t2 union all (values(1))) on true limit 1; + +-- Test handling of Vars with varno 0 in estimate_array_length +explain (verbose, costs off) +select null::int[] union all select null::int[] union all select null::bigint[]; diff --git a/parser/src/test/resources/postgres/test/regress/sql/vacuum.sql b/parser/src/test/resources/postgres/test/regress/sql/vacuum.sql index c79fc35..ed8bb50 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/vacuum.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/vacuum.sql @@ -121,6 +121,10 @@ CREATE INDEX brin_pvactst ON pvactst USING brin (i); CREATE INDEX gin_pvactst ON pvactst USING gin (a); CREATE INDEX gist_pvactst ON pvactst USING gist (p); CREATE INDEX spgist_pvactst ON pvactst USING spgist (p); +CREATE TABLE pvactst2 (i INT) WITH (autovacuum_enabled = off); +INSERT INTO pvactst2 SELECT generate_series(1, 1000); +CREATE INDEX ON pvactst2 (i); +CREATE INDEX ON pvactst2 (i); -- VACUUM invokes parallel index cleanup SET min_parallel_index_scan_size to 0; @@ -138,6 +142,14 @@ VACUUM (PARALLEL 2, INDEX_CLEANUP FALSE) pvactst; VACUUM (PARALLEL 2, FULL TRUE) pvactst; -- error, cannot use both PARALLEL and FULL VACUUM (PARALLEL) pvactst; -- error, cannot use PARALLEL option without parallel degree +-- Test parallel vacuum using the minimum maintenance_work_mem with and without +-- dead tuples. +SET maintenance_work_mem TO 64; +VACUUM (PARALLEL 2) pvactst2; +DELETE FROM pvactst2 WHERE i < 1000; +VACUUM (PARALLEL 2) pvactst2; +RESET maintenance_work_mem; + -- Test different combinations of parallel and full options for temporary tables CREATE TEMPORARY TABLE tmp (a int PRIMARY KEY); CREATE INDEX tmp_idx1 ON tmp (a); @@ -145,6 +157,7 @@ VACUUM (PARALLEL 1, FULL FALSE) tmp; -- parallel vacuum disabled for temp tables VACUUM (PARALLEL 0, FULL TRUE) tmp; -- can specify parallel disabled (even though that's implied by FULL) RESET min_parallel_index_scan_size; DROP TABLE pvactst; +DROP TABLE pvactst2; -- INDEX_CLEANUP option CREATE TABLE no_index_cleanup (i INT PRIMARY KEY, t TEXT); diff --git a/parser/src/test/resources/postgres/test/regress/sql/window.sql b/parser/src/test/resources/postgres/test/regress/sql/window.sql index 219eb85..cafec98 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/window.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/window.sql @@ -338,6 +338,32 @@ CREATE TEMP VIEW v_window AS SELECT pg_get_viewdef('v_window'); +-- test overflow frame specifications +SELECT sum(unique1) over (rows between current row and 9223372036854775807 following exclude current row), + unique1, four +FROM tenk1 WHERE unique1 < 10; + +SELECT sum(unique1) over (rows between 9223372036854775807 following and 1 following), + unique1, four +FROM tenk1 WHERE unique1 < 10; + +SELECT last_value(unique1) over (ORDER BY four rows between current row and 9223372036854775807 following exclude current row), + unique1, four +FROM tenk1 WHERE unique1 < 10; + +-- These test GROUPS mode with an offset large enough to cause overflow when +-- added to currentgroup. Although the overflow doesn't produce visibly wrong +-- results (due to the incremental nature of group pointer advancement), we +-- still need to protect against it as signed integer overflow is undefined +-- behavior in C. +SELECT sum(unique1) over (ORDER BY four groups between current row and 9223372036854775807 following), + unique1, four +FROM tenk1 WHERE unique1 < 10; + +SELECT sum(unique1) over (ORDER BY four groups between 9223372036854775807 following and unbounded following), + unique1, four +FROM tenk1 WHERE unique1 < 10; + -- RANGE offset PRECEDING/FOLLOWING tests SELECT sum(unique1) over (order by four range between 2::int8 preceding and 1::int2 preceding), @@ -1353,6 +1379,13 @@ SELECT * FROM FROM empsalary) emp WHERE c = 1; +-- Try another case with a WindowFunc with a byref return type +SELECT * FROM + (SELECT row_number() OVER (PARTITION BY salary) AS rn, + lead(depname) OVER (PARTITION BY salary) || ' Department' AS n_dep + FROM empsalary) emp +WHERE rn < 1; + -- Some more complex cases with multiple window clauses EXPLAIN (COSTS OFF) SELECT * FROM diff --git a/parser/src/test/resources/postgres/test/regress/sql/with.sql b/parser/src/test/resources/postgres/test/regress/sql/with.sql index a41cce5..55f9c66 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/with.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/with.sql @@ -355,6 +355,18 @@ UNION ALL SELECT t1.id, t2.path, t2 FROM t AS t1 JOIN t AS t2 ON (t1.id=t2.id); +CREATE TEMP TABLE duplicates (a INT NOT NULL); +INSERT INTO duplicates VALUES(1), (1); + +-- Try out a recursive UNION case where the non-recursive part's table slot +-- uses TTSOpsBufferHeapTuple and contains duplicate rows. +WITH RECURSIVE cte (a) as ( + SELECT a FROM duplicates + UNION + SELECT a FROM cte +) +SELECT a FROM cte; + -- test that column statistics from a materialized CTE are available -- to upper planner (otherwise, we'd get a stupider plan) explain (costs off) @@ -945,6 +957,13 @@ WITH RECURSIVE x(n) AS ( ORDER BY (SELECT n FROM x)) SELECT * FROM x; +-- and this +WITH RECURSIVE x(n) AS ( + WITH sub_cte AS (SELECT * FROM x) + DELETE FROM graph RETURNING f) + SELECT * FROM x; + + CREATE TEMPORARY TABLE y (a INTEGER); INSERT INTO y SELECT generate_series(1, 10); @@ -1072,6 +1091,30 @@ select ( with cte(foo) as ( values(f1) ) values((select foo from cte)) ) from int4_tbl; +-- +-- test for bug #19055: interaction of WITH with aggregates +-- +-- For now, we just throw an error if there's a use of a CTE below the +-- semantic level that the SQL standard assigns to the aggregate. +-- It's not entirely clear what we could do instead that doesn't risk +-- breaking more things than it fixes. +select f1, (with cte1(x,y) as (select 1,2) + select count((select i4.f1 from cte1))) as ss +from int4_tbl i4; + +-- +-- test for bug #19106: interaction of WITH with aggregates +-- +-- the initial fix for #19055 was too aggressive and broke this case +explain (verbose, costs off) +with a as ( select id from (values (1), (2)) as v(id) ), + b as ( select max((select sum(id) from a)) as agg ) +select agg from b; + +with a as ( select id from (values (1), (2)) as v(id) ), + b as ( select max((select sum(id) from a)) as agg ) +select agg from b; + -- -- test for nested-recursive-WITH bug -- @@ -1311,6 +1354,29 @@ COMMIT; SELECT * FROM bug6051_3; +-- check that recursive CTE processing doesn't rewrite a CTE more than once +-- (must not try to expand GENERATED ALWAYS IDENTITY columns more than once) +CREATE TEMP TABLE id_alw1 (i int GENERATED ALWAYS AS IDENTITY); + +CREATE TEMP TABLE id_alw2 (i int GENERATED ALWAYS AS IDENTITY); +CREATE TEMP VIEW id_alw2_view AS SELECT * FROM id_alw2; + +CREATE TEMP TABLE id_alw3 (i int GENERATED ALWAYS AS IDENTITY); +CREATE RULE id_alw3_ins AS ON INSERT TO id_alw3 DO INSTEAD + WITH t1 AS (INSERT INTO id_alw1 DEFAULT VALUES RETURNING i) + INSERT INTO id_alw2_view DEFAULT VALUES RETURNING i; +CREATE TEMP VIEW id_alw3_view AS SELECT * FROM id_alw3; + +CREATE TEMP TABLE id_alw4 (i int GENERATED ALWAYS AS IDENTITY); + +WITH t4 AS (INSERT INTO id_alw4 DEFAULT VALUES RETURNING i) + INSERT INTO id_alw3_view DEFAULT VALUES RETURNING i; + +SELECT * from id_alw1; +SELECT * from id_alw2; +SELECT * from id_alw3; +SELECT * from id_alw4; + -- check case where CTE reference is removed due to optimization EXPLAIN (VERBOSE, COSTS OFF) SELECT q1 FROM diff --git a/parser/src/test/resources/postgres/test/regress/sql/xml.sql b/parser/src/test/resources/postgres/test/regress/sql/xml.sql index 953bac0..f8cdaf3 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/xml.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/xml.sql @@ -1,3 +1,11 @@ +/* + * This file has been altered by SplendidData. + * It is only used for syntax checking, not for the testing of a commandline paser. + * So input for the copy statements is removed. + * The deactivated lines are marked by: -- Deactivated for SplendidDataTest: + */ + + CREATE TABLE xmltest ( id int, data xml @@ -5,7 +13,14 @@ CREATE TABLE xmltest ( INSERT INTO xmltest VALUES (1, 'one'); INSERT INTO xmltest VALUES (2, 'two'); -INSERT INTO xmltest VALUES (3, 'three '); + +-- If no XML data could be inserted, skip the tests as the server has been +-- compiled without libxml support. +SELECT count(*) = 0 AS skip_test FROM xmltest \gset +\if :skip_test +\quit +\endif SELECT * FROM xmltest; @@ -30,7 +45,7 @@ SELECT xmlconcat(xmlcomment('hello'), SELECT xmlconcat('hello', 'you'); SELECT xmlconcat(1, 2); -SELECT xmlconcat('bad', ' '); SELECT xmlconcat('', NULL, ''); SELECT xmlconcat('', NULL, ''); SELECT xmlconcat(NULL); @@ -75,17 +90,17 @@ SELECT xmlparse(content '&'); SELECT xmlparse(content '&idontexist;'); SELECT xmlparse(content ''); SELECT xmlparse(content ''); -SELECT xmlparse(content '&idontexist;'); +SELECT xmlparse(content '&idontexist; '); SELECT xmlparse(content ''); -SELECT xmlparse(document ' '); +SELECT xmlparse(document '!'); SELECT xmlparse(document 'abc'); SELECT xmlparse(document 'x'); -SELECT xmlparse(document '&'); -SELECT xmlparse(document '&idontexist;'); +SELECT xmlparse(document '& '); +SELECT xmlparse(document '&idontexist; '); SELECT xmlparse(document ''); SELECT xmlparse(document ''); -SELECT xmlparse(document '&idontexist;'); +SELECT xmlparse(document '&idontexist; '); SELECT xmlparse(document ''); @@ -168,6 +183,9 @@ SELECT xmlserialize(CONTENT '' AS text INDENT); -- 'no indent' = not using 'no indent' SELECT xmlserialize(DOCUMENT '42' AS text) = xmlserialize(DOCUMENT '42' AS text NO INDENT); SELECT xmlserialize(CONTENT '42' AS text) = xmlserialize(CONTENT '42' AS text NO INDENT); +-- indent xml strings containing blank nodes +SELECT xmlserialize(DOCUMENT ' ' AS text INDENT); +SELECT xmlserialize(CONTENT 'text node ' AS text INDENT); SELECT xml 'bar' IS DOCUMENT; SELECT xml 'barfoo' IS DOCUMENT; @@ -216,6 +234,8 @@ CREATE VIEW xmlview6 AS SELECT xmlpi(name foo, 'bar'); CREATE VIEW xmlview7 AS SELECT xmlroot(xml '', version no value, standalone yes); CREATE VIEW xmlview8 AS SELECT xmlserialize(content 'good' as char(10)); CREATE VIEW xmlview9 AS SELECT xmlserialize(content 'good' as text); +CREATE VIEW xmlview10 AS SELECT xmlserialize(document '42' AS text indent); +CREATE VIEW xmlview11 AS SELECT xmlserialize(document '42' AS character varying no indent); SELECT table_name, view_definition FROM information_schema.views WHERE table_name LIKE 'xmlview%' ORDER BY 1; @@ -239,6 +259,7 @@ SELECT xpath('count(//*)=3', ''); SELECT xpath('name(/*)', ''); SELECT xpath('/nosuchtag', ''); SELECT xpath('root', ''); +SELECT xpath('//namespace::foo', ''); -- Round-trip non-ASCII data through xpath(). DO $$ @@ -430,19 +451,23 @@ EXPLAIN (COSTS OFF, VERBOSE) SELECT * FROM xmltableview1; -- errors SELECT * FROM XMLTABLE (ROW () PASSING null COLUMNS v1 timestamp) AS f (v1, v2); +-- Deactivated for SplendidDataTest: SELECT * FROM XMLTABLE (ROW () PASSING null COLUMNS v1 timestamp __pg__is_not_null 1) AS f (v1); + -- XMLNAMESPACES tests SELECT * FROM XMLTABLE(XMLNAMESPACES('http://x.y' AS zz), '/zz:rows/zz:row' PASSING '10' COLUMNS a int PATH 'zz:a'); -CREATE VIEW xmltableview2 AS SELECT * FROM XMLTABLE(XMLNAMESPACES('http://x.y' AS zz), - '/zz:rows/zz:row' +CREATE VIEW xmltableview2 AS SELECT * FROM XMLTABLE(XMLNAMESPACES('http://x.y' AS "Zz"), + '/Zz:rows/Zz:row' PASSING '10' - COLUMNS a int PATH 'zz:a'); + COLUMNS a int PATH 'Zz:a'); SELECT * FROM xmltableview2; +\sv xmltableview2 + SELECT * FROM XMLTABLE(XMLNAMESPACES(DEFAULT 'http://x.y'), '/rows/row' PASSING '10' diff --git a/pom.xml b/pom.xml index 8ce9b8e..ee8db54 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.splendiddata pg_sqlparser - 17.0 + 17.2 pom pg_sqlparser Java implementation of the Postgres sql parser. This version of @@ -42,9 +42,10 @@ ${java.version} UTF-8 file:**/generated-sources/** - 2.23.1 + 2.26.0 11.0.2 - 5.11.3 + + 5.14.1 3.9.1 2.8 @@ -95,40 +96,40 @@ org.apache.maven maven-plugin-api - 3.9.4 + 3.9.16 org.apache.maven.plugin-tools maven-plugin-annotations - 3.13.1 + 3.15.2 provided org.apache.maven.plugin-tools maven-plugin-tools-api - 3.13.1 + 3.15.2 org.apache.maven maven-core - 3.9.4 + 3.9.16 com.github.javaparser javaparser-core - 3.26.2 + 3.28.2 jakarta.xml.bind jakarta.xml.bind-api - 4.0.2 + 4.0.5 org.glassfish.jaxb jaxb-runtime - 4.0.5 + 4.0.9 @@ -159,18 +160,18 @@ org.checkerframework checker-qual - 3.48.2 + 4.2.0 org.apache.maven maven-model - 3.9.7 + 3.9.16 provided org.apache.maven maven-artifact - 3.9.7 + 3.9.16 provided @@ -187,7 +188,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.13.0 + 3.15.0 ${java.version} @@ -198,17 +199,17 @@ org.apache.maven.plugins maven-antrun-plugin - 3.1.0 + 3.2.0 org.codehaus.mojo exec-maven-plugin - 3.5.0 + 3.6.3 org.codehaus.mojo build-helper-maven-plugin - 3.6.0 + 3.6.1 de.jflex @@ -218,12 +219,12 @@ org.apache.maven.plugins maven-dependency-plugin - 3.8.1 + 3.11.0 org.apache.maven.plugins maven-jar-plugin - 3.4.2 + 3.5.0 org.openjfx @@ -233,37 +234,37 @@ org.jacoco jacoco-maven-plugin - 0.8.12 + 0.8.15 org.apache.maven.plugins maven-plugin-plugin - 3.13.1 + 3.15.2 org.apache.maven.plugins maven-site-plugin - 3.12.1 + 3.22.0 org.apache.maven.plugins maven-javadoc-plugin - 3.11.2 + 3.12.0 org.apache.maven.plugins maven-surefire-plugin - 3.5.2 + 3.5.6 org.apache.maven.plugins maven-clean-plugin - 3.4.0 + 3.5.0 org.apache.maven.plugins maven-deploy-plugin - 3.1.3 + 3.1.4 true @@ -271,17 +272,17 @@ org.apache.maven.plugins maven-install-plugin - 3.1.3 + 3.1.4 org.apache.maven.plugins maven-resources-plugin - 3.3.1 + 3.5.0 org.apache.maven.plugins maven-enforcer-plugin - 3.5.0 + 3.6.3 @@ -339,7 +340,7 @@ org.apache.maven.plugins maven-source-plugin - 3.3.1 + 3.4.0 attach-sources @@ -393,7 +394,7 @@ org.apache.maven.plugins maven-surefire-report-plugin - 3.5.2 + 3.5.6 @@ -421,7 +422,7 @@ - aggreagte + aggregate report-aggregate @@ -432,9 +433,10 @@ - - www.splendiddata.com - scp://www.splediddata.com/www/docs/pg_sqlparser/ - + + github + GitHub Splendid Data Apache Maven Packages + https://maven.pkg.github.com//splendiddata/pg_sqlparser + \ No newline at end of file