-
Notifications
You must be signed in to change notification settings - Fork 3.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
opt,plpgsql: subquery hoisting rules should not reorder PL/pgSQL subr…
…outines Due to #97432, it is possible for subquery-hoisting decorrelation rules to hoist a volatile subquery from a CASE expression. This can cause a query to display side effects which were meant to be gated behind a conditional expression, or else were meant to occur in a different order. This is a problem for PL/pgSQL, which relies on expressions being executed in a certain order. While #115826 added a `Barrier` expression to prevent rules from changing execution order, this doesn't work for hoisting rules that traverse an entire operator subtree, instead of relying on match-and-replace patterns. This commit makes a targeted fix for PL/pgSQL routines by preventing subquery-hoisting rules from matching if a scalar expression contains a `BarrierExpr` or a `UDFCall` with `TailCall = true`. Either of these conditions indicates that changing execution order would cause incorrect results. Fixes #120327 Release note (bug fix): Fixed a bug introduced in v23.2 that could cause a PL/pgSQL routine to return incorrect results when there was at least one parameter, and an `IF` statement with one leak-proof branch, and one branch with side effects.
- Loading branch information
1 parent
1a66d6d
commit 619bb47
Showing
3 changed files
with
180 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
# Regression test for #120439 - don't allow subquery hoisting that violates | ||
# PL/pgSQL subroutine ordering. | ||
exec-ddl | ||
CREATE OR REPLACE PROCEDURE p(x INT) AS $$ | ||
BEGIN | ||
IF pg_sleep(0.1) IS NOT NULL THEN | ||
RAISE NOTICE 'foo %', x; | ||
ELSE | ||
SELECT x; | ||
END IF; | ||
END | ||
$$ LANGUAGE PLpgSQL; | ||
---- | ||
|
||
# The subquery hoisting rules shouldn't pull subroutine tail calls out of a | ||
# CASE statement. | ||
norm expect-not=HoistValuesSubquery format=show-scalars | ||
CALL p(1); | ||
---- | ||
call | ||
├── volatile | ||
└── procedure: p | ||
├── args | ||
│ └── const: 1 | ||
├── params: x:1 | ||
└── body | ||
└── values | ||
├── columns: stmt_if_5:12 | ||
├── outer: (1) | ||
├── cardinality: [1 - 1] | ||
├── volatile | ||
├── key: () | ||
├── fd: ()-->(12) | ||
└── tuple | ||
└── case | ||
├── true | ||
├── when | ||
│ ├── is-not | ||
│ │ ├── function: pg_sleep | ||
│ │ │ └── const: 0.1 | ||
│ │ └── null | ||
│ └── subquery | ||
│ └── values | ||
│ ├── columns: "_stmt_raise_2":7 | ||
│ ├── outer: (1) | ||
│ ├── cardinality: [1 - 1] | ||
│ ├── volatile | ||
│ ├── key: () | ||
│ ├── fd: ()-->(7) | ||
│ └── tuple | ||
│ └── udf: _stmt_raise_2 | ||
│ ├── args | ||
│ │ └── variable: x:1 | ||
│ ├── params: x:4 | ||
│ └── body | ||
│ ├── values | ||
│ │ ├── columns: stmt_raise_3:5 | ||
│ │ ├── outer: (4) | ||
│ │ ├── cardinality: [1 - 1] | ||
│ │ ├── volatile | ||
│ │ ├── key: () | ||
│ │ ├── fd: ()-->(5) | ||
│ │ └── tuple | ||
│ │ └── function: crdb_internal.plpgsql_raise | ||
│ │ ├── const: 'NOTICE' | ||
│ │ ├── concat | ||
│ │ │ ├── concat | ||
│ │ │ │ ├── const: 'foo ' | ||
│ │ │ │ └── coalesce | ||
│ │ │ │ ├── cast: STRING | ||
│ │ │ │ │ └── variable: x:4 | ||
│ │ │ │ └── const: '<NULL>' | ||
│ │ │ └── const: '' | ||
│ │ ├── const: '' | ||
│ │ ├── const: '' | ||
│ │ └── const: '00000' | ||
│ └── values | ||
│ ├── columns: stmt_if_1:6 | ||
│ ├── cardinality: [1 - 1] | ||
│ ├── key: () | ||
│ ├── fd: ()-->(6) | ||
│ └── tuple | ||
│ └── subquery | ||
│ └── values | ||
│ ├── columns: "_implicit_return":3 | ||
│ ├── cardinality: [1 - 1] | ||
│ ├── key: () | ||
│ ├── fd: ()-->(3) | ||
│ └── tuple | ||
│ └── null | ||
└── subquery | ||
└── values | ||
├── columns: "_stmt_exec_4":11 | ||
├── outer: (1) | ||
├── cardinality: [1 - 1] | ||
├── key: () | ||
├── fd: ()-->(11) | ||
└── tuple | ||
└── udf: _stmt_exec_4 | ||
├── args | ||
│ └── variable: x:1 | ||
├── params: x:8 | ||
└── body | ||
├── values | ||
│ ├── columns: x:9 | ||
│ ├── outer: (8) | ||
│ ├── cardinality: [1 - 1] | ||
│ ├── key: () | ||
│ ├── fd: ()-->(9) | ||
│ └── tuple | ||
│ └── variable: x:8 | ||
└── values | ||
├── columns: stmt_if_1:10 | ||
├── cardinality: [1 - 1] | ||
├── key: () | ||
├── fd: ()-->(10) | ||
└── tuple | ||
└── subquery | ||
└── values | ||
├── columns: "_implicit_return":3 | ||
├── cardinality: [1 - 1] | ||
├── key: () | ||
├── fd: ()-->(3) | ||
└── tuple | ||
└── null |