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 @@
- * 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
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, '