diff --git a/grammar-to-java/pom.xml b/grammar-to-java/pom.xml index 7e4bbfb..1cab65f 100644 --- a/grammar-to-java/pom.xml +++ b/grammar-to-java/pom.xml @@ -5,7 +5,7 @@ com.splendiddata pg_sqlparser - 18.1 + 18.2 com.splendiddata.pg_sqlparser grammar-to-java diff --git a/parser-enums/pom.xml b/parser-enums/pom.xml index 49ad9dc..3f734be 100644 --- a/parser-enums/pom.xml +++ b/parser-enums/pom.xml @@ -5,7 +5,7 @@ com.splendiddata pg_sqlparser - 18.1 + 18.2 com.splendiddata.pg_sqlparser parser-enums diff --git a/parser-test-gui/pom.xml b/parser-test-gui/pom.xml index 8b49a7e..107a4a8 100644 --- a/parser-test-gui/pom.xml +++ b/parser-test-gui/pom.xml @@ -5,7 +5,7 @@ com.splendiddata pg_sqlparser - 18.1 + 18.2 parser-test-gui com.splendiddata.pg_sqlparser diff --git a/parser/pom.xml b/parser/pom.xml index d5b3b9e..eeb3b96 100644 --- a/parser/pom.xml +++ b/parser/pom.xml @@ -5,7 +5,7 @@ com.splendiddata pg_sqlparser - 18.1 + 18.2 com.splendiddata.pg_sqlparser parser diff --git a/parser/src/main/java/com/splendiddata/sqlparser/structure/CollateClause.java b/parser/src/main/java/com/splendiddata/sqlparser/structure/CollateClause.java index 9695db6..efe2861 100644 --- a/parser/src/main/java/com/splendiddata/sqlparser/structure/CollateClause.java +++ b/parser/src/main/java/com/splendiddata/sqlparser/structure/CollateClause.java @@ -30,7 +30,7 @@ * @since 0.0.1 */ @XmlRootElement(namespace = "parser") -public class CollateClause extends Node { +public class CollateClause extends Expr { /** input expression */ @XmlElement 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/JsonAggConstructor.java b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonAggConstructor.java index 7997fcd..c199cd4 100644 --- a/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonAggConstructor.java +++ b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonAggConstructor.java @@ -1,9 +1,15 @@ /* - * Copyright (c) Splendid Data Product Development B.V. 2023 - 2024 + * Copyright (c) Splendid Data Product Development B.V. 2020 - 2026 * - * This unpublished material is proprietary to Splendid Data Product Development B.V. All rights reserved. The methods - * and techniques described herein are considered trade secrets and/or confidential. Reproduction or distribution, in - * whole or in part, is forbidden except by express written permission of Splendid Data Product Development B.V. + * 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 + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, Client should + * obtain one via www.gnu.org/licenses/. */ package com.splendiddata.sqlparser.structure; diff --git a/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonArgument.java b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonArgument.java index 9ea5f9b..0b3f81c 100644 --- a/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonArgument.java +++ b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonArgument.java @@ -1,9 +1,15 @@ /* - * Copyright (c) Splendid Data Product Development B.V. 2020 - 2024 + * Copyright (c) Splendid Data Product Development B.V. 2020 - 2026 * - * This unpublished material is proprietary to Splendid Data Product Development B.V. All rights reserved. The methods - * and techniques described herein are considered trade secrets and/or confidential. Reproduction or distribution, in - * whole or in part, is forbidden except by express written permission of Splendid Data Product Development B.V. + * 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 + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, Client should + * obtain one via www.gnu.org/licenses/. */ package com.splendiddata.sqlparser.structure; diff --git a/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonArrayAgg.java b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonArrayAgg.java index c08f777..057c9c0 100644 --- a/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonArrayAgg.java +++ b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonArrayAgg.java @@ -1,9 +1,15 @@ /* - * Copyright (c) Splendid Data Product Development B.V. 2023 - 2024 + * Copyright (c) Splendid Data Product Development B.V. 2020 - 2026 * - * This unpublished material is proprietary to Splendid Data Product Development B.V. All rights reserved. The methods - * and techniques described herein are considered trade secrets and/or confidential. Reproduction or distribution, in - * whole or in part, is forbidden except by express written permission of Splendid Data Product Development B.V. + * 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 + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, Client should + * obtain one via www.gnu.org/licenses/. */ package com.splendiddata.sqlparser.structure; diff --git a/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonArrayConstructor.java b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonArrayConstructor.java index 49cc1bd..8482f4e 100644 --- a/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonArrayConstructor.java +++ b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonArrayConstructor.java @@ -1,9 +1,15 @@ /* - * Copyright (c) Splendid Data Product Development B.V. 2023 - 2024 + * Copyright (c) Splendid Data Product Development B.V. 2020 - 2026 * - * This unpublished material is proprietary to Splendid Data Product Development B.V. All rights reserved. The methods - * and techniques described herein are considered trade secrets and/or confidential. Reproduction or distribution, in - * whole or in part, is forbidden except by express written permission of Splendid Data Product Development B.V. + * 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 + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, Client should + * obtain one via www.gnu.org/licenses/. */ package com.splendiddata.sqlparser.structure; diff --git a/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonArrayQueryConstructor.java b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonArrayQueryConstructor.java index 37f8544..c6d6088 100644 --- a/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonArrayQueryConstructor.java +++ b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonArrayQueryConstructor.java @@ -1,9 +1,15 @@ /* - * Copyright (c) Splendid Data Product Development B.V. 2023 - 2024 + * Copyright (c) Splendid Data Product Development B.V. 2020 - 2026 * - * This unpublished material is proprietary to Splendid Data Product Development B.V. All rights reserved. The methods - * and techniques described herein are considered trade secrets and/or confidential. Reproduction or distribution, in - * whole or in part, is forbidden except by express written permission of Splendid Data Product Development B.V. + * 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 + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, Client should + * obtain one via www.gnu.org/licenses/. */ package com.splendiddata.sqlparser.structure; diff --git a/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonFormat.java b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonFormat.java index 2d09fcb..b6d5a94 100644 --- a/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonFormat.java +++ b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonFormat.java @@ -1,9 +1,15 @@ /* - * Copyright (c) Splendid Data Product Development B.V. 2020 - 2023 + * Copyright (c) Splendid Data Product Development B.V. 2020 - 2026 * - * This unpublished material is proprietary to Splendid Data Product Development B.V. All rights reserved. The methods - * and techniques described herein are considered trade secrets and/or confidential. Reproduction or distribution, in - * whole or in part, is forbidden except by express written permission of Splendid Data Product Development B.V. + * 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 + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, Client should + * obtain one via www.gnu.org/licenses/. */ package com.splendiddata.sqlparser.structure; diff --git a/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonIsPredicate.java b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonIsPredicate.java index 432d7ab..9d7e554 100644 --- a/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonIsPredicate.java +++ b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonIsPredicate.java @@ -1,9 +1,15 @@ /* - * Copyright (c) Splendid Data Product Development B.V. 2023 - 2024 + * Copyright (c) Splendid Data Product Development B.V. 2020 - 2026 * - * This unpublished material is proprietary to Splendid Data Product Development B.V. All rights reserved. The methods - * and techniques described herein are considered trade secrets and/or confidential. Reproduction or distribution, in - * whole or in part, is forbidden except by express written permission of Splendid Data Product Development B.V. + * 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 + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, Client should + * obtain one via www.gnu.org/licenses/. */ package com.splendiddata.sqlparser.structure; diff --git a/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonKeyValue.java b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonKeyValue.java index 8ef8b5b..9171c4a 100644 --- a/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonKeyValue.java +++ b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonKeyValue.java @@ -1,9 +1,15 @@ /* - * Copyright (c) Splendid Data Product Development B.V. 2023 + * Copyright (c) Splendid Data Product Development B.V. 2020 - 2026 * - * This unpublished material is proprietary to Splendid Data Product Development B.V. All rights reserved. The methods - * and techniques described herein are considered trade secrets and/or confidential. Reproduction or distribution, in - * whole or in part, is forbidden except by express written permission of Splendid Data Product Development B.V. + * 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 + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, Client should + * obtain one via www.gnu.org/licenses/. */ package com.splendiddata.sqlparser.structure; diff --git a/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonObjectAgg.java b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonObjectAgg.java index c4c8464..b041560 100644 --- a/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonObjectAgg.java +++ b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonObjectAgg.java @@ -1,9 +1,15 @@ /* - * Copyright (c) Splendid Data Product Development B.V. 2023 - 2024 + * Copyright (c) Splendid Data Product Development B.V. 2020 - 2026 * - * This unpublished material is proprietary to Splendid Data Product Development B.V. All rights reserved. The methods - * and techniques described herein are considered trade secrets and/or confidential. Reproduction or distribution, in - * whole or in part, is forbidden except by express written permission of Splendid Data Product Development B.V. + * 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 + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, Client should + * obtain one via www.gnu.org/licenses/. */ package com.splendiddata.sqlparser.structure; diff --git a/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonObjectConstructor.java b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonObjectConstructor.java index f2bfa02..f31a683 100644 --- a/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonObjectConstructor.java +++ b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonObjectConstructor.java @@ -1,9 +1,15 @@ /* - * Copyright (c) Splendid Data Product Development B.V. 2023 - 2024 + * Copyright (c) Splendid Data Product Development B.V. 2020 - 2026 * - * This unpublished material is proprietary to Splendid Data Product Development B.V. All rights reserved. The methods - * and techniques described herein are considered trade secrets and/or confidential. Reproduction or distribution, in - * whole or in part, is forbidden except by express written permission of Splendid Data Product Development B.V. + * 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 + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, Client should + * obtain one via www.gnu.org/licenses/. */ package com.splendiddata.sqlparser.structure; diff --git a/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonOutput.java b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonOutput.java index db58fcc..c95d252 100644 --- a/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonOutput.java +++ b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonOutput.java @@ -1,9 +1,15 @@ /* - * Copyright (c) Splendid Data Product Development B.V. 2023 + * Copyright (c) Splendid Data Product Development B.V. 2020 - 2026 * - * This unpublished material is proprietary to Splendid Data Product Development B.V. All rights reserved. The methods - * and techniques described herein are considered trade secrets and/or confidential. Reproduction or distribution, in - * whole or in part, is forbidden except by express written permission of Splendid Data Product Development B.V. + * 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 + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, Client should + * obtain one via www.gnu.org/licenses/. */ package com.splendiddata.sqlparser.structure; diff --git a/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonParseExpr.java b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonParseExpr.java index 595aa3f..82ef5ad 100644 --- a/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonParseExpr.java +++ b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonParseExpr.java @@ -1,9 +1,15 @@ /* - * Copyright (c) Splendid Data Product Development B.V. 2020 - 2024 + * Copyright (c) Splendid Data Product Development B.V. 2020 - 2026 * - * This unpublished material is proprietary to Splendid Data Product Development B.V. All rights reserved. The methods - * and techniques described herein are considered trade secrets and/or confidential. Reproduction or distribution, in - * whole or in part, is forbidden except by express written permission of Splendid Data Product Development B.V. + * 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 + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, Client should + * obtain one via www.gnu.org/licenses/. */ package com.splendiddata.sqlparser.structure; diff --git a/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonReturning.java b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonReturning.java index c086c32..617f72b 100644 --- a/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonReturning.java +++ b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonReturning.java @@ -1,9 +1,15 @@ /* - * Copyright (c) Splendid Data Product Development B.V. 2023 - 2024 + * Copyright (c) Splendid Data Product Development B.V. 2020 - 2026 * - * This unpublished material is proprietary to Splendid Data Product Development B.V. All rights reserved. The methods - * and techniques described herein are considered trade secrets and/or confidential. Reproduction or distribution, in - * whole or in part, is forbidden except by express written permission of Splendid Data Product Development B.V. + * 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 + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, Client should + * obtain one via www.gnu.org/licenses/. */ package com.splendiddata.sqlparser.structure; diff --git a/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonScalarExpr.java b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonScalarExpr.java index cb5097e..36557cb 100644 --- a/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonScalarExpr.java +++ b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonScalarExpr.java @@ -1,9 +1,15 @@ /* - * Copyright (c) Splendid Data Product Development B.V. 2020 - 2024 + * Copyright (c) Splendid Data Product Development B.V. 2020 - 2026 * - * This unpublished material is proprietary to Splendid Data Product Development B.V. All rights reserved. The methods - * and techniques described herein are considered trade secrets and/or confidential. Reproduction or distribution, in - * whole or in part, is forbidden except by express written permission of Splendid Data Product Development B.V. + * 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 + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, Client should + * obtain one via www.gnu.org/licenses/. */ package com.splendiddata.sqlparser.structure; diff --git a/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonTablePathSpec.java b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonTablePathSpec.java index b081c31..b72d3e1 100644 --- a/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonTablePathSpec.java +++ b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonTablePathSpec.java @@ -1,9 +1,15 @@ /* - * Copyright (c) Splendid Data Product Development B.V. 2023 - 2024 + * Copyright (c) Splendid Data Product Development B.V. 2020 - 2026 * - * This unpublished material is proprietary to Splendid Data Product Development B.V. All rights reserved. The methods - * and techniques described herein are considered trade secrets and/or confidential. Reproduction or distribution, in - * whole or in part, is forbidden except by express written permission of Splendid Data Product Development B.V. + * 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 + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, Client should + * obtain one via www.gnu.org/licenses/. */ package com.splendiddata.sqlparser.structure; diff --git a/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonValueExpr.java b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonValueExpr.java index 3551f44..e8d2e6b 100644 --- a/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonValueExpr.java +++ b/parser/src/main/java/com/splendiddata/sqlparser/structure/JsonValueExpr.java @@ -1,9 +1,15 @@ /* - * Copyright (c) Splendid Data Product Development B.V. 2023 - 2024 + * Copyright (c) Splendid Data Product Development B.V. 2020 - 2026 * - * This unpublished material is proprietary to Splendid Data Product Development B.V. All rights reserved. The methods - * and techniques described herein are considered trade secrets and/or confidential. Reproduction or distribution, in - * whole or in part, is forbidden except by express written permission of Splendid Data Product Development B.V. + * 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 + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, Client should + * obtain one via www.gnu.org/licenses/. */ package com.splendiddata.sqlparser.structure; 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 97ade14..8decdda 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/aggregates.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/aggregates.sql @@ -548,6 +548,23 @@ alter table t2 alter column z drop not null; create unique index t2_z_uidx on t2(z) nulls not distinct; explain (costs off) select y,z from t2 group by y,z; +-- A unique index proves uniqueness only under its own opfamily. When the +-- GROUP BY's eqop comes from a different opfamily with looser equality, +-- rows the index regards as distinct can collapse into one GROUP BY group, +-- so the index is not usable for removing redundant columns. +create type t_rec as (x numeric); +create temp table t_opf (a t_rec not null, b text); +create unique index on t_opf (a record_image_ops); +-- (1.0) and (1.00) are bytewise distinct but logically equal as records; +-- the index admits both, but GROUP BY a (default record_ops) would merge +-- them, so b must be retained as a grouping key. +insert into t_opf values (row(1.0)::t_rec, 'X'), (row(1.00)::t_rec, 'Y'); +explain (costs off) +select a, b from t_opf group by a, b order by b; +select a, b from t_opf group by a, b order by b; +drop table t_opf; +drop type t_rec; + drop table t1 cascade; drop table t2; drop table t3; @@ -576,6 +593,12 @@ select f2, count(*) from t1 x(x0,x1) left join (t1 left join t2 using(f2)) on (x0 = 0) group by f2; +-- check that we preserve join alias in GROUP BY expressions +create temp view v1 as +select f1::int from t1 left join t2 using (f1) group by f1; +select pg_get_viewdef('v1'::regclass); + +drop view v1; drop table t1, t2; -- 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 05a5635..de8fc22 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 @@ -2338,6 +2338,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* @@ -2841,6 +2849,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 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 47d62c1..ddd496e 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/arrays.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/arrays.sql @@ -528,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[]); 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 6f06bcf..80b36db 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 @@ -576,6 +576,9 @@ SELECT 'abc' <= 'ABC' COLLATE case_insensitive, 'abc' >= 'ABC' COLLATE case_inse SELECT array_sort('{a,B}'::text[] COLLATE case_insensitive); SELECT array_sort('{a,B}'::text[] COLLATE "C"); +-- test replace() at the end of the string (bug #19341) +SELECT replace('testX' COLLATE case_insensitive, 'x' COLLATE case_insensitive, 'er'); + -- test language tags CREATE COLLATION lt_insensitive (provider = icu, locale = 'en-u-ks-level1', deterministic = false); SELECT 'aBcD' COLLATE lt_insensitive = 'AbCd' COLLATE lt_insensitive; @@ -613,6 +616,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); @@ -643,6 +749,87 @@ CREATE UNIQUE INDEX ON test3ci (x); -- error SELECT string_to_array('ABC,DEF,GHI' COLLATE case_insensitive, ',', 'abc'); SELECT string_to_array('ABCDEFGHI' COLLATE case_insensitive, NULL, 'b'); +-- Test HAVING-to-WHERE pushdown with nondeterministic collations. +-- When a HAVING clause uses a different collation than the GROUP BY's +-- nondeterministic collation, it must not be pushed to WHERE, otherwise +-- aggregate results can change because the stricter filter eliminates rows +-- before grouping. + +-- Negative: collation conflict, HAVING must not be pushed to WHERE +EXPLAIN (COSTS OFF) +SELECT x, count(*) FROM test3ci GROUP BY x HAVING x = 'abc' COLLATE case_sensitive; +SELECT x, count(*) FROM test3ci GROUP BY x HAVING x = 'abc' COLLATE case_sensitive; + +-- Positive: same collation, safe to push HAVING to WHERE +EXPLAIN (COSTS OFF) +SELECT x, count(*) FROM test3ci GROUP BY x HAVING x = 'abc' COLLATE case_insensitive; +SELECT x, count(*) FROM test3ci GROUP BY x HAVING x = 'abc' COLLATE case_insensitive; + +-- Negative: function applied to grouped column with conflicting collation +EXPLAIN (COSTS OFF) +SELECT x, count(*) FROM test3ci GROUP BY x HAVING upper(x) = 'ABC' COLLATE case_sensitive; +SELECT x, count(*) FROM test3ci GROUP BY x HAVING upper(x) = 'ABC' COLLATE case_sensitive; + +-- Positive: function with same collation as GROUP BY +EXPLAIN (COSTS OFF) +SELECT x, count(*) FROM test3ci GROUP BY x HAVING upper(x) = 'ABC' COLLATE case_insensitive; +SELECT x, count(*) FROM test3ci GROUP BY x HAVING upper(x) = 'ABC' COLLATE case_insensitive; + +-- Negative: inner function has conflicting collation, even though outer +-- operator's collation matches GROUP BY due to a COLLATE override +EXPLAIN (COSTS OFF) +SELECT x, count(*) FROM test3ci GROUP BY x HAVING upper(x COLLATE case_sensitive) COLLATE case_insensitive = 'ABC'; +SELECT x, count(*) FROM test3ci GROUP BY x HAVING upper(x COLLATE case_sensitive) COLLATE case_insensitive = 'ABC'; + +-- Mixed AND: conflicting clause stays in HAVING, safe clause pushed to WHERE +EXPLAIN (COSTS OFF) +SELECT x, count(*) FROM test3ci GROUP BY x HAVING x = 'abc' COLLATE case_sensitive AND length(x) > 1; +SELECT x, count(*) FROM test3ci GROUP BY x HAVING x = 'abc' COLLATE case_sensitive AND length(x) > 1; + +-- Positive: AND of two safe clauses, both can be pushed +EXPLAIN (COSTS OFF) +SELECT x, count(*) FROM test3ci GROUP BY x HAVING x = 'abc' COLLATE case_insensitive AND length(x) > 1; +SELECT x, count(*) FROM test3ci GROUP BY x HAVING x = 'abc' COLLATE case_insensitive AND length(x) > 1; + +-- Negative: OR with a conflicting clause: must stay in HAVING +EXPLAIN (COSTS OFF) +SELECT x, count(*) FROM test3ci GROUP BY x HAVING x = 'abc' COLLATE case_sensitive OR x = 'def' COLLATE case_sensitive ORDER BY 1; +SELECT x, count(*) FROM test3ci GROUP BY x HAVING x = 'abc' COLLATE case_sensitive OR x = 'def' COLLATE case_sensitive ORDER BY 1; + +-- Negative: collation conflict inside a RowCompareExpr +EXPLAIN (COSTS OFF) +SELECT x, count(*) FROM test3ci GROUP BY x HAVING ROW(x, 1) < ROW('ABC' COLLATE case_sensitive, 1) ORDER BY 1; +SELECT x, count(*) FROM test3ci GROUP BY x HAVING ROW(x, 1) < ROW('ABC' COLLATE case_sensitive, 1) ORDER BY 1; + +-- Negative: simple-CASE form with conflicting WHEN comparison collation +EXPLAIN (COSTS OFF) +SELECT x, count(*) FROM test3ci GROUP BY x HAVING (CASE x WHEN 'abc' COLLATE case_sensitive THEN true ELSE false END); +SELECT x, count(*) FROM test3ci GROUP BY x HAVING (CASE x WHEN 'abc' COLLATE case_sensitive THEN true ELSE false END); + +-- Positive: simple-CASE form with matching collation, safe to push +EXPLAIN (COSTS OFF) +SELECT x, count(*) FROM test3ci GROUP BY x HAVING (CASE x WHEN 'abc' COLLATE case_insensitive THEN true ELSE false END); +SELECT x, count(*) FROM test3ci GROUP BY x HAVING (CASE x WHEN 'abc' COLLATE case_insensitive THEN true ELSE false END); + +-- Negative: nested CASE with collation conflict +EXPLAIN (COSTS OFF) +SELECT x, count(*) FROM test3ci GROUP BY x HAVING (CASE WHEN (CASE x WHEN 'abc' COLLATE case_sensitive THEN 1 ELSE 0 END) = 1 THEN true ELSE false END); +SELECT x, count(*) FROM test3ci GROUP BY x HAVING (CASE WHEN (CASE x WHEN 'abc' COLLATE case_sensitive THEN 1 ELSE 0 END) = 1 THEN true ELSE false END); + +-- Positive: conflicting collation but no grouping expression reference +EXPLAIN (COSTS OFF) +SELECT x, count(*) FROM test3ci GROUP BY x HAVING current_setting('server_version') = 'abc' COLLATE case_sensitive; + +-- Positive: deterministic collation in GROUP BY: always safe to push, even if +-- HAVING uses a nondeterministic collation +EXPLAIN (COSTS OFF) +SELECT x, count(*) FROM test3cs GROUP BY x HAVING x = 'abc' COLLATE case_sensitive; +SELECT x, count(*) FROM test3cs GROUP BY x HAVING x = 'abc' COLLATE case_sensitive; + +EXPLAIN (COSTS OFF) +SELECT x, count(*) FROM test3cs GROUP BY x HAVING x = 'abc' COLLATE case_insensitive ORDER BY 1; +SELECT x, count(*) FROM test3cs GROUP BY x HAVING x = 'abc' COLLATE case_insensitive ORDER BY 1; + -- bpchar CREATE TABLE test1bpci (x char(3) COLLATE case_insensitive); CREATE TABLE test2bpci (x char(3) COLLATE case_insensitive); @@ -991,6 +1178,26 @@ RESET enable_partitionwise_aggregate; RESET max_parallel_workers_per_gather; RESET enable_incremental_sort; +-- +-- A unique index can prove functional dependency for GROUP BY column +-- removal only if its per-column collation agrees on equality with +-- the GROUP BY column's collation. An index built under a different +-- (deterministic) collation would otherwise let remove_useless_groupby_columns +-- drop other columns whose values still differ within a nondeterministic +-- group. +-- +CREATE TABLE groupby_collation_t (a text COLLATE case_insensitive NOT NULL, b text); +INSERT INTO groupby_collation_t VALUES ('foo', 'X'), ('FOO', 'Y'); +CREATE UNIQUE INDEX ON groupby_collation_t (a COLLATE "C"); + +-- Column b must NOT be dropped: under case_insensitive on a, 'foo' and +-- 'FOO' would merge, but they have distinct b values. +EXPLAIN (COSTS OFF) +SELECT a, b FROM groupby_collation_t GROUP BY a, b ORDER BY a, b; +SELECT a, b FROM groupby_collation_t GROUP BY a, b ORDER BY a, b; + +DROP TABLE groupby_collation_t; + -- virtual generated columns CREATE TABLE t5 ( a int, @@ -1005,6 +1212,19 @@ INSERT INTO t5 (a, b) values (1, 'D1'), (2, 'D2'), (3, 'd1'); -- rewriting.) SELECT * FROM t5 ORDER BY c ASC, a ASC; +-- 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 68e48b6..b684f80 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/constraints.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/constraints.sql @@ -631,7 +631,9 @@ DROP TABLE deferred_excl; -- verify constraints created for NOT NULL clauses CREATE TABLE notnull_tbl1 (a INTEGER NOT NULL NOT NULL); \d+ notnull_tbl1 --- no-op +-- specifying an existing constraint is a no-op +ALTER TABLE notnull_tbl1 ADD CONSTRAINT notnull_tbl1_a_not_null NOT NULL a; +-- but using a different constraint name is not allowed ALTER TABLE notnull_tbl1 ADD CONSTRAINT nn NOT NULL a; \d+ notnull_tbl1 -- duplicate name @@ -708,6 +710,9 @@ DROP TABLE ATACC1, ATACC2, ATACC3; -- NOT NULL NO INHERIT is not possible on partitioned tables CREATE TABLE ATACC1 (a int NOT NULL NO INHERIT) PARTITION BY LIST (a); CREATE TABLE ATACC1 (a int, NOT NULL a NO INHERIT) PARTITION BY LIST (a); +CREATE TABLE ATACC1 (a int, CONSTRAINT a_is_not_null NOT NULL a) PARTITION BY LIST (a); +ALTER TABLE ATACC1 ALTER CONSTRAINT a_is_not_null NO INHERIT; +DROP TABLE ATACC1; -- it's not possible to override a no-inherit constraint with an inheritable one CREATE TABLE ATACC2 (a int, CONSTRAINT a_is_not_null NOT NULL a NO INHERIT); @@ -840,6 +845,9 @@ ALTER TABLE notnull_tbl1 ADD CONSTRAINT nn NOT NULL a; -- cannot add primary key on a column with an invalid not-null ALTER TABLE notnull_tbl1 ADD PRIMARY KEY (a); +-- cannot set column as generated-as-identity if it has an invalid not-null +ALTER TABLE notnull_tbl1 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; + -- ALTER column SET NOT NULL validates an invalid constraint (but this fails -- because of rows with null values) ALTER TABLE notnull_tbl1 ALTER a SET NOT NULL; @@ -1057,3 +1065,8 @@ 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/create_index.sql b/parser/src/test/resources/postgres/test/regress/sql/create_index.sql index 0f04298..13f4a39 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 @@ -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 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 f35f03f..801f9d6 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 @@ -113,6 +113,13 @@ SAVEPOINT q; DROP TABLE remember_node_subid; ROLLBACK TO q; COMMIT; DROP TABLE remember_node_subid; +-- generated NOT NULL constraint names must not collide with explicitly named constraints +CREATE TABLE two_not_null_constraints ( + col integer NOT NULL, + CONSTRAINT two_not_null_constraints_col_not_null CHECK (col IS NOT NULL) +); +DROP TABLE two_not_null_constraints; + -- -- Partitioned tables -- 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 93389b5..d52a93e 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 @@ -276,6 +276,32 @@ CREATE FOREIGN TABLE ctl_foreign_table2(LIKE ctl_table INCLUDING ALL) SERVER ctl SELECT attname, attcompression FROM pg_attribute WHERE attrelid = 'ctl_foreign_table2'::regclass and attnum > 0 ORDER BY attnum; +-- 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; + DROP TABLE ctl_table; DROP FOREIGN TABLE ctl_foreign_table1; DROP FOREIGN TABLE ctl_foreign_table2; 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/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/foreign_data.sql b/parser/src/test/resources/postgres/test/regress/sql/foreign_data.sql index 92de65c..8e65c81 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 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 09f8df8..d2058df 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 @@ -792,6 +792,43 @@ INSERT INTO fktable VALUES (500, 1000); COMMIT; +-- Check that the existing FK trigger is both deferrable and initially deferred +SELECT conname, tgrelid::regclass as tgrel, + regexp_replace(tgname, '[0-9]+', 'N') as tgname, tgtype, + tgdeferrable, tginitdeferred +FROM pg_trigger t JOIN pg_constraint c ON (t.tgconstraint = c.oid) +WHERE conrelid = 'fktable'::regclass AND conname = 'fktable_fk_fkey' +ORDER BY tgrelid, tgtype; + +-- Changing the constraint to NOT ENFORCED drops the associated FK triggers +ALTER TABLE FKTABLE ALTER CONSTRAINT fktable_fk_fkey NOT ENFORCED; +SELECT conname, tgrelid::regclass as tgrel, + regexp_replace(tgname, '[0-9]+', 'N') as tgname, tgtype, + tgdeferrable, tginitdeferred +FROM pg_trigger t JOIN pg_constraint c ON (t.tgconstraint = c.oid) +WHERE conrelid = 'fktable'::regclass AND conname = 'fktable_fk_fkey' +ORDER BY tgrelid, tgtype; + +-- Changing it back to ENFORCED will recreate the necessary FK triggers +-- that are deferrable and initially deferred +ALTER TABLE FKTABLE ALTER CONSTRAINT fktable_fk_fkey ENFORCED; +SELECT conname, tgrelid::regclass as tgrel, + regexp_replace(tgname, '[0-9]+', 'N') as tgname, tgtype, + tgdeferrable, tginitdeferred +FROM pg_trigger t JOIN pg_constraint c ON (t.tgconstraint = c.oid) +WHERE conrelid = 'fktable'::regclass AND conname = 'fktable_fk_fkey' +ORDER BY tgrelid, tgtype; + +-- Verify that a deferrable, initially deferred foreign key still works +-- as expected after being set to NOT ENFORCED and then re-enabled +BEGIN; + +-- doesn't match PK, but no error yet +INSERT INTO fktable VALUES (2, 20); + +-- should catch error from INSERT at commit +COMMIT; + DROP TABLE fktable, pktable; -- tricky behavior: according to SQL99, if a deferred constraint is set @@ -2394,3 +2431,51 @@ 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_stored.sql b/parser/src/test/resources/postgres/test/regress/sql/generated_stored.sql index 2a5fcdf..4ccf55d 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/generated_stored.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/generated_stored.sql @@ -225,6 +225,10 @@ COPY gtest1 FROM stdin; 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; @@ -508,7 +512,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); @@ -752,6 +759,29 @@ 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; -- sanity check of system catalog SELECT attrelid, attname, attgenerated FROM pg_attribute WHERE attgenerated NOT IN ('', 's', 'v'); diff --git a/parser/src/test/resources/postgres/test/regress/sql/generated_virtual.sql b/parser/src/test/resources/postgres/test/regress/sql/generated_virtual.sql index a5e0b57..7579212 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/generated_virtual.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/generated_virtual.sql @@ -225,6 +225,10 @@ COPY gtest1 FROM stdin; 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; @@ -370,6 +374,27 @@ INSERT INTO gtest21b (a) VALUES (NULL); -- error ALTER TABLE gtest21b ALTER COLUMN b DROP NOT NULL; INSERT INTO gtest21b (a) VALUES (0); -- ok now +-- virtual generated columns are not physically stored, even when not null +CREATE TABLE gtest21c (a int NOT NULL, b int GENERATED ALWAYS AS (a * 2) VIRTUAL NOT NULL, c int NOT NULL); +INSERT INTO gtest21c (a, c) VALUES (10, 42); +SELECT a, b, c FROM gtest21c; +DROP TABLE gtest21c; + +-- try adding a virtual generated column to an existing table with tuples, +-- then try adding an atthasmissing column before adding a normal nullable +-- column. +CREATE TABLE gtest21d (a int NOT NULL); +INSERT INTO gtest21d (a) VALUES(10); +ALTER TABLE gtest21d ADD COLUMN b INT GENERATED ALWAYS AS (a * 10) VIRTUAL NOT NULL; +SELECT * FROM gtest21d ORDER BY a; +INSERT INTO gtest21d (a) VALUES(20); +ALTER TABLE gtest21d ADD COLUMN c INT NOT NULL DEFAULT 1234; +SELECT * FROM gtest21d ORDER BY a; +ALTER TABLE gtest21d ADD COLUMN d INT; +INSERT INTO gtest21d (a, c, d) VALUES(30, 12345, 100); +SELECT * FROM gtest21d ORDER BY a; +DROP TABLE gtest21d; + -- not-null constraint with partitioned table CREATE TABLE gtestnn_parent ( f1 int, @@ -551,7 +576,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) VIRTUAL) PARTITION BY RANGE (f3); +CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) VIRTUAL) PARTITION BY RANGE ((f3)); CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) VIRTUAL) PARTITION BY RANGE ((f3 * 3)); +CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) VIRTUAL) PARTITION BY RANGE ((gtest_part_key)); +CREATE TABLE gtest_part_key (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 2) VIRTUAL) PARTITION BY RANGE ((gtest_part_key is not null)); -- ALTER TABLE ... ADD COLUMN CREATE TABLE gtest25 (a int PRIMARY KEY); @@ -803,6 +831,29 @@ 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) VIRTUAL); +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; -- sanity check of system catalog SELECT attrelid, attname, attgenerated FROM pg_attribute WHERE attgenerated NOT IN ('', 's', 'v'); @@ -882,3 +933,43 @@ select * from gtest33 where b is null; reset constraint_exclusion; drop table gtest33; + +-- Ensure that EXCLUDED. in INSERT ... ON CONFLICT +-- DO UPDATE is expanded to the generation expression, both for plain and +-- partitioned target relations. +create table gtest34 (id int primary key, a int, + c int generated always as (a * 10) virtual); +insert into gtest34 values (1, 5); +insert into gtest34 values (1, 7) + on conflict (id) do update set a = excluded.c returning *; +insert into gtest34 values (1, 2) + on conflict (id) do update set a = gtest34.c + excluded.c returning *; +insert into gtest34 values (1, 3) + on conflict (id) do update set a = 999 where excluded.c > 20 returning *; +drop table gtest34; + +create table gtest34p (id int primary key, a int, + c int generated always as (a * 10) virtual) + partition by range (id); +create table gtest34p_1 partition of gtest34p for values from (1) to (100); +insert into gtest34p values (1, 5); +insert into gtest34p values (1, 7) + on conflict (id) do update set a = excluded.c returning *; +insert into gtest34p values (1, 2) + on conflict (id) do update set a = gtest34p.c + excluded.c returning *; +drop table gtest34p; + +-- Ensure that virtual generated columns work with WHERE CURRENT OF +create table gtest_cursor (id int primary key, a int, b int generated always as (a * 2) virtual); +insert into gtest_cursor values (1, 10), (2, 20), (3, 30); + +begin; +declare curs cursor for select * from gtest_cursor order by id for update; +fetch 1 from curs; +update gtest_cursor set a = 99 where current of curs; +select * from gtest_cursor order by id; +delete from gtest_cursor where current of curs; +select * from gtest_cursor order by id; +commit; + +drop table gtest_cursor; diff --git a/parser/src/test/resources/postgres/test/regress/sql/groupingsets.sql b/parser/src/test/resources/postgres/test/regress/sql/groupingsets.sql index 4fbc158..92cc5a1 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/groupingsets.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/groupingsets.sql @@ -191,6 +191,52 @@ select x, y || 'y' group by grouping sets (x, y) order by 1, 2; +-- check that operands wrapped in PlaceHolderVars are capable of index matching +begin; + +set local enable_bitmapscan = off; + +explain (costs off) +select x, y + from (select unique1 as x, unique2 as y from tenk1) as t + where x = 1 + group by grouping sets (x, y) + order by 1, 2; + +select x, y + from (select unique1 as x, unique2 as y from tenk1) as t + where x = 1 + group by grouping sets (x, y) + order by 1, 2; + +explain (costs off) +select x, y + from (select unique1::oid as x, unique2 as y from tenk1) as t + where x::integer = 1 + group by grouping sets (x, y) + order by 1, 2; + +select x, y + from (select unique1::oid as x, unique2 as y from tenk1) as t + where x::integer = 1 + group by grouping sets (x, y) + order by 1, 2; + +explain (costs off) +select x, y + from (select t1.unique1 as x, t1.unique2 as y from tenk1 t1, tenk1 t2) as t + where x = 1 + group by grouping sets (x, y) + order by 1, 2; + +select x, y + from (select t1.unique1 as x, t1.unique2 as y from tenk1 t1, tenk1 t2) as t + where x = 1 + group by grouping sets (x, y) + order by 1, 2; + +rollback; + -- check qual push-down rules for a subquery with grouping sets explain (verbose, costs off) select * from ( @@ -298,11 +344,29 @@ explain (costs off) select v.c, (select count(*) from gstest2 group by () having v.c) from (values (false),(true)) v(c) order by v.c; --- test pushdown of HAVING clause that does not reference any columns that are nullable by grouping sets +-- test pushdown of non-degenerate HAVING clause that does not reference any +-- columns that are nullable by grouping sets explain (costs off) select a, b, count(*) from gstest2 group by grouping sets ((a, b), (a)) having a > 1 and b > 1; select a, b, count(*) from gstest2 group by grouping sets ((a, b), (a)) having a > 1 and b > 1; +explain (costs off) +select a, b, count(*) from gstest2 group by rollup(a), b having b > 1; +select a, b, count(*) from gstest2 group by rollup(a), b having b > 1; + +-- test pushdown of degenerate HAVING clause +explain (costs off) +select count(*) from gstest2 group by grouping sets (()) having false; +select count(*) from gstest2 group by grouping sets (()) having false; + +explain (costs off) +select a, count(*) from gstest2 group by grouping sets ((a), ()) having false; +select a, count(*) from gstest2 group by grouping sets ((a), ()) having false; + +explain (costs off) +select a, b, count(*) from gstest2 group by grouping sets ((a), (b)) having false; +select a, b, count(*) from gstest2 group by grouping sets ((a), (b)) having false; + -- HAVING with GROUPING queries select ten, grouping(ten) from onek group by grouping sets(ten) having grouping(ten) >= 0 @@ -711,4 +775,27 @@ select a, b, row_number() over (order by a, b nulls first) from (values (1, 1), (2, 2)) as t (a, b) where a = b group by grouping sets((a, b), (a)); +-- test handling of SRFs with grouping sets +explain (verbose, costs off) +select generate_series(1, a) as g +from (values (1, 1), (2, 2)) as t (a, b) +group by rollup(g) +order by 1; + +select generate_series(1, a) as g +from (values (1, 1), (2, 2)) as t (a, b) +group by rollup(g) +order by 1; + +explain (verbose, costs off) +select generate_series(1, a) as g, a+b as ab +from (values (1, 1), (2, 2)) as t (a, b) +group by rollup(a, ab) +order by 1, 2; + +select generate_series(1, a) as g, a+b as ab +from (values (1, 1), (2, 2)) as t (a, b) +group by rollup(a, ab) +order by 1, 2; + -- end 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/indexing.sql b/parser/src/test/resources/postgres/test/regress/sql/indexing.sql index b5cb01c..706a6ec 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/join.sql b/parser/src/test/resources/postgres/test/regress/sql/join.sql index 3533fd3..d0f36be 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/join.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/join.sql @@ -759,6 +759,26 @@ select * from tbl_rs t1 join (select t1.a+t3.a from tbl_rs t3) and t2.a < 5) on true; +-- +-- regression test for bug with parallel-hash-right-semi join +-- + +begin; + +-- encourage use of parallel plans +set local parallel_setup_cost=0; +set local parallel_tuple_cost=0; +set local min_parallel_table_scan_size=0; +set local max_parallel_workers_per_gather=4; + +-- ensure we don't get parallel hash right semi join +explain (costs off) +select * from tenk1 t1 +where exists (select 1 from tenk1 t2 where fivethous = t1.fivethous) +and t1.fivethous < 5; + +rollback; + -- -- regression test for bug #13908 (hash join with skew tuples & nbatch increase) -- @@ -1461,6 +1481,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 -- @@ -2181,6 +2225,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 @@ -2275,6 +2342,18 @@ CREATE TEMP TABLE parted_b1 partition of parted_b for values from (0) to (10); explain (costs off) select a.* from a left join parted_b pb on a.b_id = pb.id; +-- test that clauses that still embed PHVs are not referencing the removed +-- relation when rebuilt for a partition of the kept relation +explain (costs off) +select 1 from (select t1.id from parted_b t1 left join parted_b t2 on t1.id = t2.id) s +where s.id = 1 group by (); + +explain (costs off) +select 1 from parted_b t1 + join (select t2.id from parted_b t2 left join parted_b t3 on t2.id = t3.id) s + on t1.id = s.id +group by (); + rollback; create temp table parent (k int primary key, pd int); @@ -3034,6 +3113,12 @@ SELECT * FROM (SELECT n2.a FROM sj n1, sj n2 WHERE n1.a <> n2.a) q0, sl WHERE q0.a = 1; +-- Do not forget to replace relid in bare Var join clause (bug #19435) +ALTER TABLE sl ADD COLUMN bool_col boolean; +EXPLAIN (COSTS OFF) +SELECT 1 AS c1 FROM sl sl1 LEFT JOIN (sl AS sl2 NATURAL JOIN sl AS sl3) + ON sl2.bool_col LEFT JOIN sl AS sl4 ON sl2.bool_col; + -- Check optimization disabling if it will violate special join conditions. -- Two identical joined relations satisfies self join removal conditions but -- stay in different special join infos. @@ -3712,6 +3797,16 @@ GROUP BY s.c1, s.c2; DROP TABLE group_tbl; +-- Test that we ignore PlaceHolderVars when looking up statistics +EXPLAIN (COSTS OFF) +SELECT t1.unique1 FROM tenk1 t1 LEFT JOIN + (SELECT *, 42 AS phv FROM tenk1 t2) ss ON t1.unique2 = ss.unique2 +WHERE ss.unique1 = ss.phv AND t1.unique1 < 100; + +SELECT t1.unique1 FROM tenk1 t1 LEFT JOIN + (SELECT *, 42 AS phv FROM tenk1 t2) ss ON t1.unique2 = ss.unique2 +WHERE ss.unique1 = ss.phv AND t1.unique1 < 100; + -- -- Test for a nested loop join involving index scan, transforming OR-clauses -- to SAOP. diff --git a/parser/src/test/resources/postgres/test/regress/sql/jsonb.sql b/parser/src/test/resources/postgres/test/regress/sql/jsonb.sql index 57c11ac..07295b4 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/jsonb.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/jsonb.sql @@ -851,6 +851,7 @@ SELECT count(*) FROM testjsonb WHERE j @? '$'; SELECT count(*) FROM testjsonb WHERE j @? '$.public'; SELECT count(*) FROM testjsonb WHERE j @? '$.bar'; +ALTER TABLE testjsonb SET (parallel_workers = 2); CREATE INDEX jidx ON testjsonb USING gin (j); SET enable_seqscan = off; @@ -939,7 +940,7 @@ SELECT count(*) FROM testjsonb WHERE j = '{"pos":98, "line":371, "node":"CBA", " --gin path opclass DROP INDEX jidx; -CREATE INDEX jidx ON testjsonb USING gin (j jsonb_path_ops); +CREATE INDEX CONCURRENTLY jidx ON testjsonb USING gin (j jsonb_path_ops); SET enable_seqscan = off; SELECT count(*) FROM testjsonb WHERE j @> '{"wait":null}'; 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_aggregate.sql b/parser/src/test/resources/postgres/test/regress/sql/partition_aggregate.sql index ab070fe..f2f23d5 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/partition_aggregate.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/partition_aggregate.sql @@ -74,6 +74,11 @@ EXPLAIN (COSTS OFF) SELECT a FROM pagg_tab WHERE a < 3 GROUP BY a ORDER BY 1; SELECT a FROM pagg_tab WHERE a < 3 GROUP BY a ORDER BY 1; +-- Test partitionwise aggregation with ordered append path built from fractional paths +EXPLAIN (COSTS OFF) +SELECT count(*) FROM pagg_tab GROUP BY c ORDER BY c LIMIT 1; +SELECT count(*) FROM pagg_tab GROUP BY c ORDER BY c LIMIT 1; + RESET enable_hashagg; -- ROLLUP, partitionwise aggregation does not apply 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 d93c0c0..359a920 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 @@ -1447,3 +1447,74 @@ select min(a) over (partition by a order by a) from part_abc where a >= stable_o drop view part_abc_view; drop table part_abc; + +-- +-- Check that operands wrapped in PlaceHolderVars are matched to partition +-- keys, allowing partition pruning to occur. PlaceHolderVars can be +-- introduced when a subquery's output is used with grouping sets. +-- +create table phv_part (a int, b text) partition by list (a); +create table phv_part_1 partition of phv_part for values in (1); +create table phv_part_2 partition of phv_part for values in (2); +create table phv_part_null partition of phv_part for values in (null); +insert into phv_part values (1, 'one'), (2, 'two'), (null, 'null'); + +-- OpExpr: PHV-wrapped operand matched via equal() +explain (costs off) +select * from (select a, b from phv_part) t + where a = 1 + group by grouping sets (a, b); + +select * from (select a, b from phv_part) t + where a = 1 + group by grouping sets (a, b); + +-- OpExpr with RelabelType: PHV wrapped around a casted column +explain (costs off) +select * from (select a::oid as x, b from phv_part) t + where x::int = 1 + group by grouping sets (x, b); + +select * from (select a::oid as x, b from phv_part) t + where x::int = 1 + group by grouping sets (x, b); + +-- ScalarArrayOpExpr: IN clause with PHV-wrapped operand +explain (costs off) +select * from (select a, b from phv_part) t + where a in (1, null) + group by grouping sets (a, b); + +select * from (select a, b from phv_part) t + where a in (1, null) + group by grouping sets (a, b); + +-- NullTest: IS NULL with PHV-wrapped operand +explain (costs off) +select * from (select a, b from phv_part) t + where a is null + group by grouping sets (a, b); + +select * from (select a, b from phv_part) t + where a is null + group by grouping sets (a, b); + +drop table phv_part; + +-- BooleanTest: IS TRUE with PHV-wrapped boolean partition key +create table phv_boolpart (a bool, b text) partition by list (a); +create table phv_boolpart_t partition of phv_boolpart for values in (true); +create table phv_boolpart_f partition of phv_boolpart for values in (false); +create table phv_boolpart_null partition of phv_boolpart default; +insert into phv_boolpart values (true, 'yes'), (false, 'no'), (null, 'unknown'); + +explain (costs off) +select * from (select a, b from phv_boolpart) t + where a is true + group by grouping sets (a, b); + +select * from (select a, b from phv_boolpart) t + where a is true + group by grouping sets (a, b); + +drop table phv_boolpart; diff --git a/parser/src/test/resources/postgres/test/regress/sql/portals.sql b/parser/src/test/resources/postgres/test/regress/sql/portals.sql index fc4cccb..196b862 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/portals.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/portals.sql @@ -508,6 +508,11 @@ DECLARE c1 CURSOR FOR SELECT * FROM ucview; FETCH FROM c1; DELETE FROM ucview WHERE CURRENT OF c1; -- fail, views not supported ROLLBACK; +BEGIN; +DECLARE c1 CURSOR FOR SELECT * FROM ucview; +FETCH FROM c1; +UPDATE ucview SET f1 = f1 + 10 WHERE CURRENT OF c1; -- fail, views not supported +ROLLBACK; -- Check WHERE CURRENT OF with an index-only scan BEGIN; 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/psql.sql b/parser/src/test/resources/postgres/test/regress/sql/psql.sql index b6428c0..f247627 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/psql.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/psql.sql @@ -1,12 +1,9 @@ /* - * 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. + * + * - - - -- -- Tests for psql features that aren't closely connected to any -- specific server features @@ -32,48 +29,78 @@ -- \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 + +-- \parse (extended query protocol) +\parse +SELECT 1 \parse '' +SELECT 2 \parse stmt1 +SELECT $1 \parse stmt2 +SELECT $1, $2 \parse stmt3 + +-- \bind_named (extended query protocol) +\bind_named +\bind_named '' \g +\bind_named stmt1 \g +\bind_named stmt2 'foo' \g +\bind_named stmt3 'foo' 'bar' \g +-- Repeated calls. The second call generates an error, cleaning up the +-- statement name set by the first call. +\bind_named stmt4 +\bind_named +\g +-- Last \bind_named wins +\bind_named stmt2 'foo' \bind_named stmt3 'foo2' 'bar2' \g +-- Multiple \g calls mean multiple executions +\bind_named stmt2 'foo3' \g \bind_named stmt3 'foo4' 'bar4' \g + +-- \close_prepared (extended query protocol) +\close_prepared +\close_prepared '' +\close_prepared stmt2 +\close_prepared stmt2 +SELECT name, statement FROM pg_prepared_statements ORDER BY name; -- \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 -- last \bind wins --- Deactivated for SplendidDataTest: select $1::int as col \bind 'foo' \bind 2 \g +select $1::int as col \bind 'foo' \bind 2 \g -- Multiple \g calls mean multiple executions --- Deactivated for SplendidDataTest: select $1::int as col \bind 1 \g \bind 2 \g +select $1::int as col \bind 1 \g \bind 2 \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 -- bind_named error --- Deactivated for SplendidDataTest: \bind_named stmt2 'baz' \g --- Deactivated for SplendidDataTest: \bind_named stmt3 'baz' \g +\bind_named stmt2 'baz' \g +\bind_named stmt3 'baz' \g -- \gset @@ -92,7 +119,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 @@ -104,7 +131,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 @@ -119,44 +146,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; @@ -164,22 +191,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 @@ -200,10 +227,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 @@ -212,54 +239,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 @@ -268,54 +295,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; @@ -329,81 +356,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 @@ -412,56 +439,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 @@ -582,23 +620,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; @@ -624,10 +662,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; @@ -673,24 +711,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; @@ -718,29 +756,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; @@ -767,36 +805,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; @@ -824,25 +862,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 @@ -879,8 +917,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 @@ -888,80 +926,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 @@ -984,17 +1022,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 @@ -1003,85 +1041,98 @@ 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 + \bind_named stmt1 1 2 \g + \C arg1 + \c arg1 arg2 arg3 arg4 + \cd arg1 + \close_prepared stmt1 + \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 + \endpipeline + \errverbose + \f arg1 + \flush + \flushrequest + \g arg1 + \gx arg1 + \gexec + \getresults + SELECT 1 AS one \gset + \h + \? + \html + \i arg1 + \ir arg1 + \l arg1 + \lo arg1 arg2 + \lo_list + \o arg1 + \p + SELECT 1 \parse + \password arg1 + \prompt arg1 arg2 + \pset arg1 arg2 + \q + \reset + \restrict test + \s arg1 + \sendpipeline + \set arg1 arg2 arg3 arg4 arg5 arg6 arg7 + \setenv arg1 arg2 + \sf whole_line + \sv whole_line + \startpipeline + \syncpipeline + \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 @@ -1109,12 +1160,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 @@ -1127,7 +1178,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 @@ -1153,7 +1204,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 @@ -1167,20 +1218,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 @@ -1419,7 +1470,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 @@ -1437,9 +1488,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 ; @@ -1458,11 +1509,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; @@ -1499,7 +1550,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. @@ -1910,3 +1961,7 @@ CREATE TABLE defprivs (a int); \zx defprivs \pset null '' DROP TABLE defprivs; + +* +*/ +select 1; \ No newline at end of file diff --git a/parser/src/test/resources/postgres/test/regress/sql/psql_pipeline.sql b/parser/src/test/resources/postgres/test/regress/sql/psql_pipeline.sql index 854dbcd..c9e2563 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/psql_pipeline.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/psql_pipeline.sql @@ -1,10 +1,9 @@ /* - * 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 pipelines. + * It hardly has added value for testing sql. + * + * + -- -- Tests using psql pipelining -- @@ -13,7 +12,7 @@ CREATE TABLE psql_pipeline(a INTEGER PRIMARY KEY, s TEXT); -- Single query \startpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 'val1' \sendpipeline +SELECT $1 \bind 'val1' \sendpipeline \endpipeline \startpipeline SELECT 'val1'; @@ -21,9 +20,9 @@ SELECT 'val1'; -- Multiple queries \startpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 'val1' \sendpipeline --- Deactivated for SplendidDataTest: SELECT $1, $2 \bind 'val2' 'val3' \sendpipeline --- Deactivated for SplendidDataTest: SELECT $1, $2 \bind 'val2' 'val3' \sendpipeline +SELECT $1 \bind 'val1' \sendpipeline +SELECT $1, $2 \bind 'val2' 'val3' \sendpipeline +SELECT $1, $2 \bind 'val2' 'val3' \sendpipeline SELECT 'val4'; SELECT 'val5', 'val6'; \endpipeline @@ -38,10 +37,10 @@ SELECT 1; SELECT 2; SELECT 3 -- Test \flush \startpipeline \flush --- Deactivated for SplendidDataTest: SELECT $1 \bind 'val1' \sendpipeline +SELECT $1 \bind 'val1' \sendpipeline \flush --- Deactivated for SplendidDataTest: SELECT $1, $2 \bind 'val2' 'val3' \sendpipeline --- Deactivated for SplendidDataTest: SELECT $1, $2 \bind 'val2' 'val3' \sendpipeline +SELECT $1, $2 \bind 'val2' 'val3' \sendpipeline +SELECT $1, $2 \bind 'val2' 'val3' \sendpipeline \flush SELECT 'val4'; SELECT 'val5', 'val6'; @@ -52,12 +51,12 @@ SELECT 'val5', 'val6'; \echo :PIPELINE_COMMAND_COUNT \echo :PIPELINE_SYNC_COUNT \echo :PIPELINE_RESULT_COUNT --- Deactivated for SplendidDataTest: SELECT $1 \bind 'val1' \sendpipeline +SELECT $1 \bind 'val1' \sendpipeline \syncpipeline \syncpipeline --- Deactivated for SplendidDataTest: SELECT $1, $2 \bind 'val2' 'val3' \sendpipeline +SELECT $1, $2 \bind 'val2' 'val3' \sendpipeline \syncpipeline --- Deactivated for SplendidDataTest: SELECT $1, $2 \bind 'val4' 'val5' \sendpipeline +SELECT $1, $2 \bind 'val4' 'val5' \sendpipeline \echo :PIPELINE_COMMAND_COUNT \echo :PIPELINE_SYNC_COUNT \echo :PIPELINE_RESULT_COUNT @@ -75,7 +74,7 @@ SELECT 'val9'; -- Query terminated with a semicolon replaces an unnamed prepared -- statement. \startpipeline --- Deactivated for SplendidDataTest: SELECT $1 \parse '' +SELECT $1 \parse '' SELECT 1; \bind_named '' \endpipeline @@ -83,7 +82,7 @@ SELECT 1; -- Extended query is appended to pipeline by a semicolon after a -- newline. \startpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 1 +SELECT $1 \bind 1 ; SELECT 2; \endpipeline @@ -91,48 +90,48 @@ SELECT 2; -- \startpipeline should not have any effect if already in a pipeline. \startpipeline \startpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 'val1' \sendpipeline +SELECT $1 \bind 'val1' \sendpipeline \endpipeline -- Convert an implicit transaction block to an explicit transaction block. \startpipeline --- Deactivated for SplendidDataTest: INSERT INTO psql_pipeline VALUES ($1) \bind 1 \sendpipeline --- Deactivated for SplendidDataTest: BEGIN \bind \sendpipeline --- Deactivated for SplendidDataTest: INSERT INTO psql_pipeline VALUES ($1) \bind 2 \sendpipeline --- Deactivated for SplendidDataTest: ROLLBACK \bind \sendpipeline +INSERT INTO psql_pipeline VALUES ($1) \bind 1 \sendpipeline +BEGIN \bind \sendpipeline +INSERT INTO psql_pipeline VALUES ($1) \bind 2 \sendpipeline +ROLLBACK \bind \sendpipeline \endpipeline -- Multiple explicit transactions \startpipeline --- Deactivated for SplendidDataTest: BEGIN \bind \sendpipeline --- Deactivated for SplendidDataTest: INSERT INTO psql_pipeline VALUES ($1) \bind 1 \sendpipeline --- Deactivated for SplendidDataTest: ROLLBACK \bind \sendpipeline --- Deactivated for SplendidDataTest: BEGIN \bind \sendpipeline --- Deactivated for SplendidDataTest: INSERT INTO psql_pipeline VALUES ($1) \bind 1 \sendpipeline --- Deactivated for SplendidDataTest: COMMIT \bind \sendpipeline +BEGIN \bind \sendpipeline +INSERT INTO psql_pipeline VALUES ($1) \bind 1 \sendpipeline +ROLLBACK \bind \sendpipeline +BEGIN \bind \sendpipeline +INSERT INTO psql_pipeline VALUES ($1) \bind 1 \sendpipeline +COMMIT \bind \sendpipeline \endpipeline -- Use \parse and \bind_named \startpipeline --- Deactivated for SplendidDataTest: SELECT $1 \parse '' --- Deactivated for SplendidDataTest: SELECT $1, $2 \parse '' --- Deactivated for SplendidDataTest: SELECT $2 \parse pipeline_1 --- Deactivated for SplendidDataTest: \bind_named '' 1 2 \sendpipeline --- Deactivated for SplendidDataTest: \bind_named pipeline_1 2 \sendpipeline +SELECT $1 \parse '' +SELECT $1, $2 \parse '' +SELECT $2 \parse pipeline_1 +\bind_named '' 1 2 \sendpipeline +\bind_named pipeline_1 2 \sendpipeline \endpipeline -- \getresults displays all results preceding a \flushrequest. \startpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 1 \sendpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 2 \sendpipeline +SELECT $1 \bind 1 \sendpipeline +SELECT $1 \bind 2 \sendpipeline \flushrequest \getresults \endpipeline -- \getresults displays all results preceding a \syncpipeline. \startpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 1 \sendpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 2 \sendpipeline +SELECT $1 \bind 1 \sendpipeline +SELECT $1 \bind 2 \sendpipeline \syncpipeline \getresults \endpipeline @@ -140,7 +139,7 @@ SELECT 2; -- \getresults immediately returns if there is no result to fetch. \startpipeline \getresults --- Deactivated for SplendidDataTest: SELECT $1 \bind 2 \sendpipeline +SELECT $1 \bind 2 \sendpipeline \getresults \flushrequest \endpipeline @@ -148,42 +147,42 @@ SELECT 2; -- \getresults only fetches results preceding a \flushrequest. \startpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 2 \sendpipeline +SELECT $1 \bind 2 \sendpipeline \flushrequest --- Deactivated for SplendidDataTest: SELECT $1 \bind 2 \sendpipeline +SELECT $1 \bind 2 \sendpipeline \getresults \endpipeline -- \getresults only fetches results preceding a \syncpipeline. \startpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 2 \sendpipeline +SELECT $1 \bind 2 \sendpipeline \syncpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 2 \sendpipeline +SELECT $1 \bind 2 \sendpipeline \getresults \endpipeline -- Use pipeline with chunked results for both \getresults and \endpipeline. \startpipeline \set FETCH_COUNT 10 --- Deactivated for SplendidDataTest: SELECT $1 \bind 2 \sendpipeline +SELECT $1 \bind 2 \sendpipeline \flushrequest \getresults --- Deactivated for SplendidDataTest: SELECT $1 \bind 2 \sendpipeline +SELECT $1 \bind 2 \sendpipeline \endpipeline \unset FETCH_COUNT -- \getresults with specific number of requested results. \startpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 1 \sendpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 2 \sendpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 3 \sendpipeline +SELECT $1 \bind 1 \sendpipeline +SELECT $1 \bind 2 \sendpipeline +SELECT $1 \bind 3 \sendpipeline \echo :PIPELINE_SYNC_COUNT \syncpipeline \echo :PIPELINE_SYNC_COUNT \echo :PIPELINE_RESULT_COUNT \getresults 1 \echo :PIPELINE_RESULT_COUNT --- Deactivated for SplendidDataTest: SELECT $1 \bind 4 \sendpipeline +SELECT $1 \bind 4 \sendpipeline \getresults 3 \echo :PIPELINE_RESULT_COUNT \endpipeline @@ -192,7 +191,7 @@ SELECT 2; \startpipeline \syncpipeline \syncpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 1 \sendpipeline +SELECT $1 \bind 1 \sendpipeline \flushrequest \getresults 2 \getresults 1 @@ -200,9 +199,9 @@ SELECT 2; -- \getresults 0 should get all the results. \startpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 1 \sendpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 2 \sendpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 3 \sendpipeline +SELECT $1 \bind 1 \sendpipeline +SELECT $1 \bind 2 \sendpipeline +SELECT $1 \bind 3 \sendpipeline \syncpipeline \getresults 0 \endpipeline @@ -217,16 +216,16 @@ SELECT 2; -- After an aborted pipeline, commands after a \syncpipeline should be -- displayed. \startpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind \sendpipeline +SELECT $1 \bind \sendpipeline \syncpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 1 \sendpipeline +SELECT $1 \bind 1 \sendpipeline \endpipeline -- For an incorrect number of parameters, the pipeline is aborted and -- the following queries will not be executed. \startpipeline --- Deactivated for SplendidDataTest: SELECT \bind 'val1' \sendpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 'val1' \sendpipeline +SELECT \bind 'val1' \sendpipeline +SELECT $1 \bind 'val1' \sendpipeline \endpipeline -- Using a semicolon with a parameter triggers an error and aborts @@ -239,29 +238,29 @@ SELECT 1; -- An explicit transaction with an error needs to be rollbacked after -- the pipeline. \startpipeline --- Deactivated for SplendidDataTest: BEGIN \bind \sendpipeline --- Deactivated for SplendidDataTest: INSERT INTO psql_pipeline VALUES ($1) \bind 1 \sendpipeline --- Deactivated for SplendidDataTest: ROLLBACK \bind \sendpipeline +BEGIN \bind \sendpipeline +INSERT INTO psql_pipeline VALUES ($1) \bind 1 \sendpipeline +ROLLBACK \bind \sendpipeline \endpipeline ROLLBACK; -- \watch is not allowed in a pipeline. \startpipeline --- Deactivated for SplendidDataTest: SELECT \bind \sendpipeline +SELECT \bind \sendpipeline \watch 1 \endpipeline -- \gdesc should fail as synchronous commands are not allowed in a pipeline, -- and the pipeline should still be usable. \startpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 1 \gdesc --- Deactivated for SplendidDataTest: SELECT $1 \bind 1 \sendpipeline +SELECT $1 \bind 1 \gdesc +SELECT $1 \bind 1 \sendpipeline \endpipeline -- \gset is not allowed in a pipeline, pipeline should still be usable. \startpipeline --- Deactivated for SplendidDataTest: SELECT $1 as i, $2 as j \parse '' --- Deactivated for SplendidDataTest: SELECT $1 as k, $2 as l \parse 'second' +SELECT $1 as i, $2 as j \parse '' +SELECT $1 as k, $2 as l \parse 'second' \bind_named '' 1 2 \gset \bind_named second 1 2 \gset pref02_ \echo :pref02_i :pref02_j \bind_named '' 1 2 \sendpipeline @@ -269,63 +268,63 @@ ROLLBACK; -- \g and \gx are not allowed, pipeline should still be usable. \startpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 1 \g --- Deactivated for SplendidDataTest: SELECT $1 \bind 1 \g (format=unaligned tuples_only=on) --- Deactivated for SplendidDataTest: SELECT $1 \bind 1 \gx --- Deactivated for SplendidDataTest: SELECT $1 \bind 1 \gx (format=unaligned tuples_only=on) +SELECT $1 \bind 1 \g +SELECT $1 \bind 1 \g (format=unaligned tuples_only=on) +SELECT $1 \bind 1 \gx +SELECT $1 \bind 1 \gx (format=unaligned tuples_only=on) \reset --- Deactivated for SplendidDataTest: SELECT $1 \bind 1 \sendpipeline +SELECT $1 \bind 1 \sendpipeline \endpipeline -- \g and \gx warnings should be emitted in an aborted pipeline, with -- pipeline still usable. \startpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind \sendpipeline +SELECT $1 \bind \sendpipeline \flushrequest \getresults --- Deactivated for SplendidDataTest: SELECT $1 \bind 1 \g --- Deactivated for SplendidDataTest: SELECT $1 \bind 1 \gx +SELECT $1 \bind 1 \g +SELECT $1 \bind 1 \gx \endpipeline -- \sendpipeline is not allowed outside of a pipeline \sendpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 1 \sendpipeline +SELECT $1 \bind 1 \sendpipeline \reset -- \sendpipeline is not allowed if not preceded by \bind or \bind_named \startpipeline \sendpipeline --- Deactivated for SplendidDataTest: SELECT 1 \sendpipeline +SELECT 1 \sendpipeline \endpipeline -- \gexec is not allowed, pipeline should still be usable. \startpipeline --- Deactivated for SplendidDataTest: SELECT 'INSERT INTO psql_pipeline(a) SELECT generate_series(1, 10)' \parse 'insert_stmt' +SELECT 'INSERT INTO psql_pipeline(a) SELECT generate_series(1, 10)' \parse 'insert_stmt' \bind_named insert_stmt \gexec \bind_named insert_stmt \sendpipeline --- Deactivated for SplendidDataTest: SELECT COUNT(*) FROM psql_pipeline \bind \sendpipeline +SELECT COUNT(*) FROM psql_pipeline \bind \sendpipeline \endpipeline -- After an error, pipeline is aborted and requires \syncpipeline to be -- reusable. \startpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind \sendpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 1 \sendpipeline --- Deactivated for SplendidDataTest: SELECT $1 \parse a +SELECT $1 \bind \sendpipeline +SELECT $1 \bind 1 \sendpipeline +SELECT $1 \parse a \bind_named a 1 \sendpipeline \close_prepared a \flushrequest \getresults -- Pipeline is aborted. --- Deactivated for SplendidDataTest: SELECT $1 \bind 1 \sendpipeline --- Deactivated for SplendidDataTest: SELECT $1 \parse a +SELECT $1 \bind 1 \sendpipeline +SELECT $1 \parse a \bind_named a 1 \sendpipeline \close_prepared a -- Sync allows pipeline to recover. \syncpipeline \getresults --- Deactivated for SplendidDataTest: SELECT $1 \bind 1 \sendpipeline --- Deactivated for SplendidDataTest: SELECT $1 \parse a +SELECT $1 \bind 1 \sendpipeline +SELECT $1 \parse a \bind_named a 1 \sendpipeline \close_prepared a \flushrequest @@ -334,9 +333,9 @@ ROLLBACK; -- In an aborted pipeline, \getresults 1 aborts commands one at a time. \startpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind \sendpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 1 \sendpipeline --- Deactivated for SplendidDataTest: SELECT $1 \parse a +SELECT $1 \bind \sendpipeline +SELECT $1 \bind 1 \sendpipeline +SELECT $1 \parse a \bind_named a 1 \sendpipeline \syncpipeline \getresults 1 @@ -349,10 +348,10 @@ ROLLBACK; -- Test chunked results with an aborted pipeline. \startpipeline \set FETCH_COUNT 10 --- Deactivated for SplendidDataTest: SELECT $1 \bind \sendpipeline +SELECT $1 \bind \sendpipeline \flushrequest \getresults --- Deactivated for SplendidDataTest: SELECT $1 \bind \sendpipeline +SELECT $1 \bind \sendpipeline \endpipeline \unset FETCH_COUNT @@ -368,7 +367,7 @@ select 1; -- Error messages accumulate and are repeated. \startpipeline --- Deactivated for SplendidDataTest: SELECT 1 \bind \sendpipeline +SELECT 1 \bind \sendpipeline \gdesc \gdesc \endpipeline @@ -383,67 +382,134 @@ select 1; -- commit the implicit transaction block. The first command after a -- sync will not be seen as belonging to a pipeline. \startpipeline --- Deactivated for SplendidDataTest: SET LOCAL statement_timeout='1h' \bind \sendpipeline --- Deactivated for SplendidDataTest: SHOW statement_timeout \bind \sendpipeline +SET LOCAL statement_timeout='1h' \bind \sendpipeline +SHOW statement_timeout \bind \sendpipeline \syncpipeline --- Deactivated for SplendidDataTest: SHOW statement_timeout \bind \sendpipeline --- Deactivated for SplendidDataTest: SET LOCAL statement_timeout='2h' \bind \sendpipeline --- Deactivated for SplendidDataTest: SHOW statement_timeout \bind \sendpipeline +SHOW statement_timeout \bind \sendpipeline +SET LOCAL statement_timeout='2h' \bind \sendpipeline +SHOW statement_timeout \bind \sendpipeline \endpipeline -- REINDEX CONCURRENTLY fails if not the first command in a pipeline. \startpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 1 \sendpipeline --- Deactivated for SplendidDataTest: REINDEX TABLE CONCURRENTLY psql_pipeline \bind \sendpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 2 \sendpipeline +SELECT $1 \bind 1 \sendpipeline +REINDEX TABLE CONCURRENTLY psql_pipeline \bind \sendpipeline +SELECT $1 \bind 2 \sendpipeline \endpipeline -- REINDEX CONCURRENTLY works if it is the first command in a pipeline. \startpipeline --- Deactivated for SplendidDataTest: REINDEX TABLE CONCURRENTLY psql_pipeline \bind \sendpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 2 \sendpipeline +REINDEX TABLE CONCURRENTLY psql_pipeline \bind \sendpipeline +SELECT $1 \bind 2 \sendpipeline \endpipeline -- Subtransactions are not allowed in a pipeline. \startpipeline --- Deactivated for SplendidDataTest: SAVEPOINT a \bind \sendpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 1 \sendpipeline --- Deactivated for SplendidDataTest: ROLLBACK TO SAVEPOINT a \bind \sendpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 2 \sendpipeline +SAVEPOINT a \bind \sendpipeline +SELECT $1 \bind 1 \sendpipeline +ROLLBACK TO SAVEPOINT a \bind \sendpipeline +SELECT $1 \bind 2 \sendpipeline \endpipeline -- LOCK fails as the first command in a pipeline, as not seen in an -- implicit transaction block. \startpipeline --- Deactivated for SplendidDataTest: LOCK psql_pipeline \bind \sendpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 2 \sendpipeline +LOCK psql_pipeline \bind \sendpipeline +SELECT $1 \bind 2 \sendpipeline \endpipeline -- LOCK succeeds as it is not the first command in a pipeline, -- seen in an implicit transaction block. \startpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 1 \sendpipeline --- Deactivated for SplendidDataTest: LOCK psql_pipeline \bind \sendpipeline --- Deactivated for SplendidDataTest: SELECT $1 \bind 2 \sendpipeline +SELECT $1 \bind 1 \sendpipeline +LOCK psql_pipeline \bind \sendpipeline +SELECT $1 \bind 2 \sendpipeline \endpipeline -- VACUUM works as the first command in a pipeline. \startpipeline --- Deactivated for SplendidDataTest: VACUUM psql_pipeline \bind \sendpipeline +VACUUM psql_pipeline \bind \sendpipeline \endpipeline -- VACUUM fails when not the first command in a pipeline. \startpipeline --- Deactivated for SplendidDataTest: SELECT 1 \bind \sendpipeline --- Deactivated for SplendidDataTest: VACUUM psql_pipeline \bind \sendpipeline +SELECT 1 \bind \sendpipeline +VACUUM psql_pipeline \bind \sendpipeline \endpipeline -- VACUUM works after a \syncpipeline. \startpipeline --- Deactivated for SplendidDataTest: SELECT 1 \bind \sendpipeline +SELECT 1 \bind \sendpipeline +\syncpipeline +VACUUM psql_pipeline \bind \sendpipeline +\endpipeline + +-- Deferred constraint violation at commit time in a pipeline. +CREATE TABLE psql_pipeline_defer (a INTEGER PRIMARY KEY DEFERRABLE INITIALLY DEFERRED); +\startpipeline +INSERT INTO psql_pipeline_defer VALUES ($1), ($1) RETURNING * \bind 1 \sendpipeline +\endpipeline + +-- Same with \syncpipeline and commands after the failing sync. +\startpipeline +INSERT INTO psql_pipeline_defer VALUES ($1), ($1) \bind 1 \sendpipeline \syncpipeline --- Deactivated for SplendidDataTest: VACUUM psql_pipeline \bind \sendpipeline +SELECT $1 \bind 'after_sync_1' \sendpipeline \endpipeline +-- More patterns with more \syncpipeline, more commands and \getresults +\startpipeline +INSERT INTO psql_pipeline_defer VALUES ($1), ($1) \bind 1 \sendpipeline +INSERT INTO psql_pipeline_defer VALUES ($1), ($1) \bind 1 \sendpipeline +\syncpipeline +SELECT $1 \bind 'after_sync_1' \sendpipeline +\getresults +SELECT $1 \bind 'after_sync_2' \sendpipeline +\endpipeline +\startpipeline +INSERT INTO psql_pipeline_defer VALUES ($1), ($1) \bind 1 \sendpipeline +INSERT INTO psql_pipeline_defer VALUES ($1), ($1) \bind 1 \sendpipeline +\syncpipeline +\getresults +SELECT $1 \bind 'after_sync_1' \sendpipeline +\getresults +SELECT $1 \bind 'after_sync_2' \sendpipeline +INSERT INTO psql_pipeline_defer VALUES ($1), ($1) \bind 1 \sendpipeline +INSERT INTO psql_pipeline_defer VALUES ($1), ($1) \bind 1 \sendpipeline +SELECT $1 \bind 'after_sync_3' \sendpipeline +SELECT $1 \bind 'after_sync_4' \sendpipeline +SELECT $1 \bind 'after_sync_5' \sendpipeline +\endpipeline + +-- Deferred error combined with a regular command error after the sync. +\startpipeline +INSERT INTO psql_pipeline_defer VALUES ($1), ($1) \bind 1 \sendpipeline +\syncpipeline +SELECT $1 \bind \sendpipeline +SELECT $1 \bind 'after_error' \sendpipeline +\endpipeline + +-- Empty sync segment followed by a deferred error. +\startpipeline +\syncpipeline +INSERT INTO psql_pipeline_defer VALUES ($1), ($1) \bind 1 \sendpipeline +\endpipeline + +-- Deferred error with \getresults reading results one at a time. +\startpipeline +INSERT INTO psql_pipeline_defer VALUES ($1), ($1) \bind 1 \sendpipeline +SELECT $1 \bind 'partial' \sendpipeline +\syncpipeline +\getresults 1 +\getresults 1 +\getresults +\endpipeline + +DROP TABLE psql_pipeline_defer; + -- Clean up DROP TABLE psql_pipeline; + +* +*/ +select 1; \ No newline at end of file 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 b60c5b2..9ddaad6 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/returning.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/returning.sql @@ -250,6 +250,17 @@ DELETE FROM foo WHERE f1 = 5 RETURNING old.tableoid::regclass, old.ctid, old.*, new.tableoid::regclass, new.ctid, new.*, *; +-- Parenthesized OLD and NEW +INSERT INTO foo VALUES (6, 'paren-test', 60, 600) + RETURNING old, (old).f4, (old).*, + new, (new).f4, (new).*; +UPDATE foo SET f4 = 700 WHERE f1 = 6 + RETURNING old, (old).f4, (old).*, + new, (new).f4, (new).*; +DELETE FROM foo WHERE f1 = 6 + RETURNING old, (old).f4, (old).*, + new, (new).f4, (new).*; + -- RETURNING OLD and NEW from subquery EXPLAIN (verbose, costs off) INSERT INTO foo VALUES (5, 'subquery test') 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 2cd327e..0ed94ae 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/sqljson.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/sqljson.sql @@ -403,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; 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 8d7b225..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 @@ -450,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'); @@ -460,6 +461,7 @@ 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); 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 b42d89c..3ee14db 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/stats.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/stats.sql @@ -410,9 +410,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} 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 6b98b75..9dc1903 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 @@ -91,6 +91,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; @@ -1759,6 +1767,39 @@ 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); @@ -1767,6 +1808,7 @@ 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; CREATE TABLE grouping_unique (x integer); diff --git a/parser/src/test/resources/postgres/test/regress/sql/stats_import.sql b/parser/src/test/resources/postgres/test/regress/sql/stats_import.sql index d140733..ee97fa6 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/stats_import.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/stats_import.sql @@ -457,6 +457,60 @@ AND tablename = 'test' AND inherited = false AND attname = 'id'; +-- warn: mcv / mcf array length mismatch (more vals), mcv-pair fails, rest get set +SELECT pg_catalog.pg_restore_attribute_stats( + 'schemaname', 'stats_import', + 'relname', 'test', + 'attname', 'id', + 'inherited', false::boolean, + 'null_frac', 0.24::real, + 'most_common_vals', '{2,1,3}'::text, + 'most_common_freqs', '{0.3,0.25}'::real[] + ); + +SELECT * +FROM pg_stats +WHERE schemaname = 'stats_import' +AND tablename = 'test' +AND inherited = false +AND attname = 'id'; + +-- warn: mcv / mcf array length mismatch (more freqs), mcv-pair fails, rest get set +SELECT pg_catalog.pg_restore_attribute_stats( + 'schemaname', 'stats_import', + 'relname', 'test', + 'attname', 'id', + 'inherited', false::boolean, + 'null_frac', 0.25::real, + 'most_common_vals', '{2,1}'::text, + 'most_common_freqs', '{0.3,0.25,0.05}'::real[] + ); + +SELECT * +FROM pg_stats +WHERE schemaname = 'stats_import' +AND tablename = 'test' +AND inherited = false +AND attname = 'id'; + +-- warn: most_common_vals is multi-dimensional, mcv-pair fails, rest get set +SELECT pg_catalog.pg_restore_attribute_stats( + 'schemaname', 'stats_import', + 'relname', 'test', + 'attname', 'id', + 'inherited', false::boolean, + 'null_frac', 0.26::real, + 'most_common_vals', '{{2,1},{3,4}}'::text, + 'most_common_freqs', '{0.3,0.25,0.05,0.04}'::real[] + ); + +SELECT * +FROM pg_stats +WHERE schemaname = 'stats_import' +AND tablename = 'test' +AND inherited = false +AND attname = 'id'; + -- ok: mcv+mcf SELECT pg_catalog.pg_restore_attribute_stats( 'schemaname', 'stats_import', 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 cbb3485..42e4221 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/subselect.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/subselect.sql @@ -347,6 +347,17 @@ select * from ( where not exists (select 1 from tenk1 as b where b.unique2 = 10000) ) ss; + +-- +-- Test cases for interactions between PARAM_EXEC, subplans and array +-- subscripts +-- + +-- check that array subscription doesn't conflict with PARAM_EXEC (see #19370) +SELECT (array[1,2])[(SELECT g.i)] FROM generate_series(1, 1) g(i); +SELECT (array[1,2])[(SELECT g.i):(SELECT g.i + 1)] FROM generate_series(1, 1) g(i); + + -- -- Test that an IN implemented using a UniquePath does unique-ification -- with the right semantics, as per bug #4113. (Unfortunately we have @@ -879,6 +890,46 @@ select * from (select 9 as x, unnest(array[1,2,3,11,12,13]) as u) ss where tattle(x, u); +-- +-- check that an upper-level qual is not pushed down if it references a grouped +-- Var whose underlying expression contains SRFs +-- +explain (verbose, costs off) +select * from + (select generate_series(1, ten) as g, count(*) from tenk1 group by 1) ss + where ss.g = 1; + +select * from + (select generate_series(1, ten) as g, count(*) from tenk1 group by 1) ss + where ss.g = 1; + +-- +-- check that an upper-level qual is not pushed down if it references a grouped +-- Var whose underlying expression contains volatile functions +-- +alter function tattle(x int, y int) volatile; + +explain (verbose, costs off) +select * from + (select tattle(3, ten) as v, count(*) from tenk1 where unique1 < 3 group by 1) ss + where ss.v; + +select * from + (select tattle(3, ten) as v, count(*) from tenk1 where unique1 < 3 group by 1) ss + where ss.v; + +-- if we pretend it's stable, we get different results: +alter function tattle(x int, y int) stable; + +explain (verbose, costs off) +select * from + (select tattle(3, ten) as v, count(*) from tenk1 where unique1 < 3 group by 1) ss + where ss.v; + +select * from + (select tattle(3, ten) as v, count(*) from tenk1 where unique1 < 3 group by 1) ss + where ss.v; + drop function tattle(x int, y int); -- 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 1e58bc3..c8f7ba8 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/triggers.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/triggers.sql @@ -2223,6 +2223,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..d570557 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/tsearch.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/tsearch.sql @@ -230,6 +230,7 @@ RESET enable_bitmapscan; DROP INDEX wowidx; +ALTER TABLE test_tsvector SET (parallel_workers = 2); CREATE INDEX wowidx ON test_tsvector USING gin (a); SET enable_seqscan=OFF; @@ -654,6 +655,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 13700a6..bc50bc4 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/union.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/union.sql @@ -592,3 +592,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/window.sql b/parser/src/test/resources/postgres/test/regress/sql/window.sql index 62df09d..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), 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 26079e7..b1521f6 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/with.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/with.sql @@ -1108,16 +1108,26 @@ from int4_tbl; -- -- test for bug #19055: interaction of WITH with aggregates -- --- The reference to cte1 must determine the aggregate's level, --- even though it contains no Vars referencing cte1 -explain (verbose, costs off) +-- 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; -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 @@ -1358,6 +1368,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/without_overlaps.sql b/parser/src/test/resources/postgres/test/regress/sql/without_overlaps.sql index 4aaca24..430f5bf 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/without_overlaps.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/without_overlaps.sql @@ -180,6 +180,37 @@ ALTER TABLE temporal_rng3 DROP CONSTRAINT temporal_rng3_uq; DROP TABLE temporal_rng3; DROP TYPE textrange2; +-- +-- test PRIMARY KEY and UNIQUE constraints' interaction with domains +-- + +-- range over domain: +CREATE DOMAIN int4_d as integer check (value <> 10); +CREATE TYPE int4_d_range as range (subtype = int4_d); +CREATE TABLE temporal_rng4 ( + id int4range, + valid_at int4_d_range, + CONSTRAINT temporal_rng4_pk PRIMARY KEY(id, valid_at WITHOUT OVERLAPS) +); +INSERT INTO temporal_rng4 VALUES ('[1,11)', '[9,10)'); -- start bound violates domain +INSERT INTO temporal_rng4 VALUES ('[1,2)', '[10,11)'); -- end bound violates domain +INSERT INTO temporal_rng4 VALUES ('[1,2)', '[1,13)'), ('[1,2)', '[2,5)'); -- overlaps +INSERT INTO temporal_rng4 VALUES ('[1,2)', '[1,13)'), ('[1,2)', '[20,23)'); -- okay +INSERT INTO temporal_rng4 VALUES ('[1,2)', '[30,)'); -- null bound is okay +DROP TABLE temporal_rng4; + +-- domain over range: +CREATE DOMAIN int4range_d AS int4range CHECK (VALUE <> '[10,11)'); +CREATE TABLE temporal_rng4 ( + id int4range, + valid_at int4range_d, + CONSTRAINT temporal_rng4_pk UNIQUE (id, valid_at WITHOUT OVERLAPS) +); +INSERT INTO temporal_rng4 VALUES ('[1,2)', '[10,11)'); -- violates domain +INSERT INTO temporal_rng4 VALUES ('[1,2)', '[1,13)'), ('[1,2)', '[2,13)'); -- overlaps +INSERT INTO temporal_rng4 VALUES ('[1,2)', '[1,13)'), ('[1,2)', '[20,23)'); -- okay +DROP TABLE temporal_rng4; + -- -- test ALTER TABLE ADD CONSTRAINT -- 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 931ec78..f8cdaf3 100644 --- a/parser/src/test/resources/postgres/test/regress/sql/xml.sql +++ b/parser/src/test/resources/postgres/test/regress/sql/xml.sql @@ -13,7 +13,14 @@ CREATE TABLE xmltest ( INSERT INTO xmltest VALUES (1, 'one'); INSERT INTO xmltest VALUES (2, 'two'); -INSERT INTO xmltest VALUES (3, 'three '); + +-- If no XML data could be inserted, skip the tests as the server has been +-- compiled without libxml support. +SELECT count(*) = 0 AS skip_test FROM xmltest \gset +\if :skip_test +\quit +\endif SELECT * FROM xmltest; @@ -38,7 +45,7 @@ SELECT xmlconcat(xmlcomment('hello'), SELECT xmlconcat('hello', 'you'); SELECT xmlconcat(1, 2); -SELECT xmlconcat('bad', ' '); SELECT xmlconcat('', NULL, ''); SELECT xmlconcat('', NULL, ''); SELECT xmlconcat(NULL); @@ -83,17 +90,17 @@ SELECT xmlparse(content '&'); SELECT xmlparse(content '&idontexist;'); SELECT xmlparse(content ''); SELECT xmlparse(content ''); -SELECT xmlparse(content '&idontexist;'); +SELECT xmlparse(content '&idontexist; '); SELECT xmlparse(content ''); -SELECT xmlparse(document ' '); +SELECT xmlparse(document '!'); SELECT xmlparse(document 'abc'); SELECT xmlparse(document 'x'); -SELECT xmlparse(document '&'); -SELECT xmlparse(document '&idontexist;'); +SELECT xmlparse(document '& '); +SELECT xmlparse(document '&idontexist; '); SELECT xmlparse(document ''); SELECT xmlparse(document ''); -SELECT xmlparse(document '&idontexist;'); +SELECT xmlparse(document '&idontexist; '); SELECT xmlparse(document ''); @@ -252,6 +259,7 @@ SELECT xpath('count(//*)=3', ''); SELECT xpath('name(/*)', ''); SELECT xpath('/nosuchtag', ''); SELECT xpath('root', ''); +SELECT xpath('//namespace::foo', ''); -- Round-trip non-ASCII data through xpath(). DO $$ diff --git a/pom.xml b/pom.xml index 28e874a..5f8c556 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.splendiddata pg_sqlparser - 18.1 + 18.2 pom pg_sqlparser Java implementation of the Postgres sql parser. This version of @@ -42,9 +42,9 @@ ${java.version} UTF-8 file:**/generated-sources/** - 2.25.2 - 21.0.8 - 5.13.4 + 2.26.0 + 21.0.11 + 6.1.0 3.9.1 2.8 @@ -95,40 +95,40 @@ org.apache.maven maven-plugin-api - 3.9.11 + 3.9.16 org.apache.maven.plugin-tools maven-plugin-annotations - 3.15.1 + 3.15.2 provided org.apache.maven.plugin-tools maven-plugin-tools-api - 3.15.1 + 3.15.2 org.apache.maven maven-core - 3.9.11 + 3.9.16 com.github.javaparser javaparser-core - 3.27.0 + 3.28.2 jakarta.xml.bind jakarta.xml.bind-api - 4.0.4 + 4.0.5 org.glassfish.jaxb jaxb-runtime - 4.0.6 + 4.0.9 @@ -159,18 +159,18 @@ org.checkerframework checker-qual - 3.51.0 + 4.2.0 org.apache.maven maven-model - 3.9.11 + 3.9.16 provided org.apache.maven maven-artifact - 3.9.11 + 3.9.16 provided @@ -187,7 +187,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.14.1 + 3.15.0 ${java.version} @@ -198,12 +198,12 @@ org.apache.maven.plugins maven-antrun-plugin - 3.1.0 + 3.2.0 org.codehaus.mojo exec-maven-plugin - 3.5.1 + 3.6.3 org.codehaus.mojo @@ -218,12 +218,12 @@ org.apache.maven.plugins maven-dependency-plugin - 3.8.1 + 3.11.0 org.apache.maven.plugins maven-jar-plugin - 3.4.2 + 3.5.0 org.openjfx @@ -233,17 +233,17 @@ org.jacoco jacoco-maven-plugin - 0.8.13 + 0.8.15 org.apache.maven.plugins maven-plugin-plugin - 3.15.1 + 3.15.2 org.apache.maven.plugins maven-site-plugin - 3.21.0 + 3.22.0 org.apache.maven.plugins @@ -253,7 +253,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.5.4 + 3.5.6 org.apache.maven.plugins @@ -276,12 +276,12 @@ org.apache.maven.plugins maven-resources-plugin - 3.3.1 + 3.5.0 org.apache.maven.plugins maven-enforcer-plugin - 3.6.1 + 3.6.3 @@ -339,7 +339,7 @@ org.apache.maven.plugins maven-source-plugin - 3.3.1 + 3.4.0 attach-sources @@ -393,7 +393,7 @@ org.apache.maven.plugins maven-surefire-report-plugin - 3.5.4 + 3.5.6