Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit f9ecb6c

Browse files
committed
Fix misidentification of SQL statement type in plpgsql's exec_stmt_execsql.
To distinguish SQL statements that are INSERT/UPDATE/DELETE from other ones, exec_stmt_execsql looked at the post-rewrite form of the statement rather than the original. This is problematic because it did that only during first execution of the statement (in a session), but the correct answer could change later due to addition or removal of DO INSTEAD rules during the session. That could lead to an Assert failure, as reported by Tushar Ahuja and Robert Haas. In non-assert builds, there's a hazard that we would fail to enforce STRICT behavior when we'd be expected to. That would happen if an initially present DO INSTEAD, that replaced the original statement with one of a different type, were removed; after that the statement should act "normally", including strictness enforcement, but it didn't. (The converse case of enforcing strictness when we shouldn't doesn't seem to be a hazard, as addition of a DO INSTEAD that changes the statement type would always lead to acting as though the statement returned zero rows, so that the strictness error could not fire.) To fix, inspect the original form of the statement not the post-rewrite form, making it valid to assume the answer can't change intra-session. This should lead to the same answer in every case except when there is a DO INSTEAD that changes the statement type; we will now set mod_stmt=true anyway, while we would not have done so before. That breaks the Assert in the SPI_OK_REWRITTEN code path, which expected the latter behavior. It might be all right to assert mod_stmt rather than !mod_stmt there, but I'm not entirely convinced that that'd always hold, so just remove the assertion altogether. This has been broken for a long time, so back-patch to all supported branches. Discussion: https://postgr.es/m/CA+TgmoZUrRN4xvZe_BbBn_Xp0BDwuMEue-0OyF0fJpfvU2Yc7Q@mail.gmail.com
1 parent de1593d commit f9ecb6c

File tree

1 file changed

+14
-14
lines changed

1 file changed

+14
-14
lines changed

src/pl/plpgsql/src/pl_exec.c

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3465,20 +3465,20 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
34653465
foreach(l, SPI_plan_get_plan_sources(expr->plan))
34663466
{
34673467
CachedPlanSource *plansource = (CachedPlanSource *) lfirst(l);
3468-
ListCell *l2;
34693468

3470-
foreach(l2, plansource->query_list)
3469+
/*
3470+
* We could look at the raw_parse_tree, but it seems simpler to
3471+
* check the command tag. Note we should *not* look at the Query
3472+
* tree(s), since those are the result of rewriting and could have
3473+
* been transmogrified into something else entirely.
3474+
*/
3475+
if (plansource->commandTag &&
3476+
(strcmp(plansource->commandTag, "INSERT") == 0 ||
3477+
strcmp(plansource->commandTag, "UPDATE") == 0 ||
3478+
strcmp(plansource->commandTag, "DELETE") == 0))
34713479
{
3472-
Query *q = (Query *) lfirst(l2);
3473-
3474-
Assert(IsA(q, Query));
3475-
if (q->canSetTag)
3476-
{
3477-
if (q->commandType == CMD_INSERT ||
3478-
q->commandType == CMD_UPDATE ||
3479-
q->commandType == CMD_DELETE)
3480-
stmt->mod_stmt = true;
3481-
}
3480+
stmt->mod_stmt = true;
3481+
break;
34823482
}
34833483
}
34843484
}
@@ -3543,12 +3543,12 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
35433543
break;
35443544

35453545
case SPI_OK_REWRITTEN:
3546-
Assert(!stmt->mod_stmt);
35473546

35483547
/*
35493548
* The command was rewritten into another kind of command. It's
35503549
* not clear what FOUND would mean in that case (and SPI doesn't
3551-
* return the row count either), so just set it to false.
3550+
* return the row count either), so just set it to false. Note
3551+
* that we can't assert anything about mod_stmt here.
35523552
*/
35533553
exec_set_found(estate, false);
35543554
break;

0 commit comments

Comments
 (0)