Skip to content

Commit b0fb2c6

Browse files
committed
Refactor to avoid code duplication in transformPLAssignStmt.
transformPLAssignStmt contained many lines cribbed directly from transformSelectStmt. I had supposed that we could manage to keep the two copies in sync, but the bug just fixed in 7504d2b shows that that hope was foolish. Let's refactor so there's just one copy. The main stumbling block to doing this is that transformPLAssignStmt has a chunk of custom code that has to run after transformTargetList but before we potentially modify the tlist further during analysis of ORDER BY and GROUP BY. Rather than make transformSelectStmt fully aware of PLAssignStmt processing, I put that code into a callback function. It still feels a little bit ugly, but it's not too awful, and surely it's better than a hundred lines of duplicated code. The steps involved in processing a PLAssignStmt remain exactly the same as before, just in different places. Author: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected]
1 parent 7504d2b commit b0fb2c6

File tree

2 files changed

+84
-124
lines changed

2 files changed

+84
-124
lines changed

src/backend/parser/analyze.c

Lines changed: 83 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,14 @@
5555
#include "utils/syscache.h"
5656

5757

58+
/* Passthrough data for transformPLAssignStmtTarget */
59+
typedef struct SelectStmtPassthrough
60+
{
61+
PLAssignStmt *stmt; /* the assignment statement */
62+
Node *target; /* node representing the target variable */
63+
List *indirection; /* indirection yet to be applied to target */
64+
} SelectStmtPassthrough;
65+
5866
/* Hook for plugins to get control at end of parse analysis */
5967
post_parse_analyze_hook_type post_parse_analyze_hook = NULL;
6068

@@ -64,7 +72,8 @@ static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt);
6472
static OnConflictExpr *transformOnConflictClause(ParseState *pstate,
6573
OnConflictClause *onConflictClause);
6674
static int count_rowexpr_columns(ParseState *pstate, Node *expr);
67-
static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
75+
static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt,
76+
SelectStmtPassthrough *passthru);
6877
static Query *transformValuesClause(ParseState *pstate, SelectStmt *stmt);
6978
static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt);
7079
static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
@@ -75,6 +84,8 @@ static Query *transformReturnStmt(ParseState *pstate, ReturnStmt *stmt);
7584
static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
7685
static Query *transformPLAssignStmt(ParseState *pstate,
7786
PLAssignStmt *stmt);
87+
static List *transformPLAssignStmtTarget(ParseState *pstate, List *tlist,
88+
SelectStmtPassthrough *passthru);
7889
static Query *transformDeclareCursorStmt(ParseState *pstate,
7990
DeclareCursorStmt *stmt);
8091
static Query *transformExplainStmt(ParseState *pstate,
@@ -371,7 +382,7 @@ transformStmt(ParseState *pstate, Node *parseTree)
371382
if (n->valuesLists)
372383
result = transformValuesClause(pstate, n);
373384
else if (n->op == SETOP_NONE)
374-
result = transformSelectStmt(pstate, n);
385+
result = transformSelectStmt(pstate, n, NULL);
375386
else
376387
result = transformSetOperationStmt(pstate, n);
377388
}
@@ -1374,11 +1385,19 @@ count_rowexpr_columns(ParseState *pstate, Node *expr)
13741385
* transformSelectStmt -
13751386
* transforms a Select Statement
13761387
*
1388+
* This function is also used to transform the source expression of a
1389+
* PLAssignStmt. In that usage, passthru is non-NULL and we need to
1390+
* call transformPLAssignStmtTarget after the initial transformation of the
1391+
* SELECT's targetlist. (We could generalize this into an arbitrary callback
1392+
* function, but for now that would just be more notation with no benefit.)
1393+
* All the rest is the same as a regular SelectStmt.
1394+
*
13771395
* Note: this covers only cases with no set operations and no VALUES lists;
13781396
* see below for the other cases.
13791397
*/
13801398
static Query *
1381-
transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
1399+
transformSelectStmt(ParseState *pstate, SelectStmt *stmt,
1400+
SelectStmtPassthrough *passthru)
13821401
{
13831402
Query *qry = makeNode(Query);
13841403
Node *qual;
@@ -1415,8 +1434,16 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
14151434
qry->targetList = transformTargetList(pstate, stmt->targetList,
14161435
EXPR_KIND_SELECT_TARGET);
14171436

1418-
/* mark column origins */
1419-
markTargetListOrigins(pstate, qry->targetList);
1437+
/*
1438+
* If we're within a PLAssignStmt, do further transformation of the
1439+
* targetlist; that has to happen before we consider sorting or grouping.
1440+
* Otherwise, mark column origins (which are useless in a PLAssignStmt).
1441+
*/
1442+
if (passthru)
1443+
qry->targetList = transformPLAssignStmtTarget(pstate, qry->targetList,
1444+
passthru);
1445+
else
1446+
markTargetListOrigins(pstate, qry->targetList);
14201447

14211448
/* transform WHERE */
14221449
qual = transformWhereClause(pstate, stmt->whereClause,
@@ -2763,20 +2790,13 @@ transformReturningClause(ParseState *pstate, Query *qry,
27632790
static Query *
27642791
transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt)
27652792
{
2766-
Query *qry = makeNode(Query);
2793+
Query *qry;
27672794
ColumnRef *cref = makeNode(ColumnRef);
27682795
List *indirection = stmt->indirection;
27692796
int nnames = stmt->nnames;
2770-
SelectStmt *sstmt = stmt->val;
27712797
Node *target;
2772-
Oid targettype;
2773-
int32 targettypmod;
2774-
Oid targetcollation;
2775-
List *tlist;
2776-
TargetEntry *tle;
2777-
Oid type_id;
2778-
Node *qual;
2779-
ListCell *l;
2798+
SelectStmtPassthrough passthru;
2799+
bool save_resolve_unknowns;
27802800

27812801
/*
27822802
* First, construct a ColumnRef for the target variable. If the target
@@ -2802,33 +2822,62 @@ transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt)
28022822

28032823
/*
28042824
* Transform the target reference. Typically we will get back a Param
2805-
* node, but there's no reason to be too picky about its type.
2825+
* node, but there's no reason to be too picky about its type. (Note that
2826+
* we must do this before calling transformSelectStmt. It's tempting to
2827+
* do it inside transformPLAssignStmtTarget, but we need to do it before
2828+
* adding any FROM tables to the pstate's namespace, else we might wrongly
2829+
* resolve the target as a table column.)
28062830
*/
28072831
target = transformExpr(pstate, (Node *) cref,
28082832
EXPR_KIND_UPDATE_TARGET);
2809-
targettype = exprType(target);
2810-
targettypmod = exprTypmod(target);
2811-
targetcollation = exprCollation(target);
2833+
2834+
/* Set up passthrough data for transformPLAssignStmtTarget */
2835+
passthru.stmt = stmt;
2836+
passthru.target = target;
2837+
passthru.indirection = indirection;
28122838

28132839
/*
2814-
* The rest mostly matches transformSelectStmt, except that we needn't
2815-
* consider WITH or INTO, and we build a targetlist our own way.
2840+
* To avoid duplicating a lot of code, we use transformSelectStmt to do
2841+
* almost all of the work. However, we need to do additional processing
2842+
* on the SELECT's targetlist after it's been transformed, but before
2843+
* possible addition of targetlist items for ORDER BY or GROUP BY.
2844+
* transformSelectStmt knows it should call transformPLAssignStmtTarget if
2845+
* it's passed a passthru argument.
2846+
*
2847+
* Also, disable resolution of unknown-type tlist items; PL/pgSQL wants to
2848+
* deal with that itself.
28162849
*/
2817-
qry->commandType = CMD_SELECT;
2818-
pstate->p_is_insert = false;
2850+
save_resolve_unknowns = pstate->p_resolve_unknowns;
2851+
pstate->p_resolve_unknowns = false;
2852+
qry = transformSelectStmt(pstate, stmt->val, &passthru);
2853+
pstate->p_resolve_unknowns = save_resolve_unknowns;
28192854

2820-
/* make FOR UPDATE/FOR SHARE info available to addRangeTableEntry */
2821-
pstate->p_locking_clause = sstmt->lockingClause;
2822-
2823-
/* make WINDOW info available for window functions, too */
2824-
pstate->p_windowdefs = sstmt->windowClause;
2855+
return qry;
2856+
}
28252857

2826-
/* process the FROM clause */
2827-
transformFromClause(pstate, sstmt->fromClause);
2858+
/*
2859+
* Callback function to adjust a SELECT's tlist to make the output suitable
2860+
* for assignment to a PLAssignStmt's target variable.
2861+
*
2862+
* Note: we actually modify the tle->expr in-place, but the function's API
2863+
* is set up to not presume that.
2864+
*/
2865+
static List *
2866+
transformPLAssignStmtTarget(ParseState *pstate, List *tlist,
2867+
SelectStmtPassthrough *passthru)
2868+
{
2869+
PLAssignStmt *stmt = passthru->stmt;
2870+
Node *target = passthru->target;
2871+
List *indirection = passthru->indirection;
2872+
Oid targettype;
2873+
int32 targettypmod;
2874+
Oid targetcollation;
2875+
TargetEntry *tle;
2876+
Oid type_id;
28282877

2829-
/* initially transform the targetlist as if in SELECT */
2830-
tlist = transformTargetList(pstate, sstmt->targetList,
2831-
EXPR_KIND_SELECT_TARGET);
2878+
targettype = exprType(target);
2879+
targettypmod = exprTypmod(target);
2880+
targetcollation = exprCollation(target);
28322881

28332882
/* we should have exactly one targetlist item */
28342883
if (list_length(tlist) != 1)
@@ -2906,97 +2955,7 @@ transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt)
29062955

29072956
pstate->p_expr_kind = EXPR_KIND_NONE;
29082957

2909-
qry->targetList = list_make1(tle);
2910-
2911-
/* transform WHERE */
2912-
qual = transformWhereClause(pstate, sstmt->whereClause,
2913-
EXPR_KIND_WHERE, "WHERE");
2914-
2915-
/* initial processing of HAVING clause is much like WHERE clause */
2916-
qry->havingQual = transformWhereClause(pstate, sstmt->havingClause,
2917-
EXPR_KIND_HAVING, "HAVING");
2918-
2919-
/*
2920-
* Transform sorting/grouping stuff. Do ORDER BY first because both
2921-
* transformGroupClause and transformDistinctClause need the results. Note
2922-
* that these functions can also change the targetList, so it's passed to
2923-
* them by reference.
2924-
*/
2925-
qry->sortClause = transformSortClause(pstate,
2926-
sstmt->sortClause,
2927-
&qry->targetList,
2928-
EXPR_KIND_ORDER_BY,
2929-
false /* allow SQL92 rules */ );
2930-
2931-
qry->groupClause = transformGroupClause(pstate,
2932-
sstmt->groupClause,
2933-
&qry->groupingSets,
2934-
&qry->targetList,
2935-
qry->sortClause,
2936-
EXPR_KIND_GROUP_BY,
2937-
false /* allow SQL92 rules */ );
2938-
qry->groupDistinct = sstmt->groupDistinct;
2939-
2940-
if (sstmt->distinctClause == NIL)
2941-
{
2942-
qry->distinctClause = NIL;
2943-
qry->hasDistinctOn = false;
2944-
}
2945-
else if (linitial(sstmt->distinctClause) == NULL)
2946-
{
2947-
/* We had SELECT DISTINCT */
2948-
qry->distinctClause = transformDistinctClause(pstate,
2949-
&qry->targetList,
2950-
qry->sortClause,
2951-
false);
2952-
qry->hasDistinctOn = false;
2953-
}
2954-
else
2955-
{
2956-
/* We had SELECT DISTINCT ON */
2957-
qry->distinctClause = transformDistinctOnClause(pstate,
2958-
sstmt->distinctClause,
2959-
&qry->targetList,
2960-
qry->sortClause);
2961-
qry->hasDistinctOn = true;
2962-
}
2963-
2964-
/* transform LIMIT */
2965-
qry->limitOffset = transformLimitClause(pstate, sstmt->limitOffset,
2966-
EXPR_KIND_OFFSET, "OFFSET",
2967-
sstmt->limitOption);
2968-
qry->limitCount = transformLimitClause(pstate, sstmt->limitCount,
2969-
EXPR_KIND_LIMIT, "LIMIT",
2970-
sstmt->limitOption);
2971-
qry->limitOption = sstmt->limitOption;
2972-
2973-
/* transform window clauses after we have seen all window functions */
2974-
qry->windowClause = transformWindowDefinitions(pstate,
2975-
pstate->p_windowdefs,
2976-
&qry->targetList);
2977-
2978-
qry->rtable = pstate->p_rtable;
2979-
qry->rteperminfos = pstate->p_rteperminfos;
2980-
qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
2981-
2982-
qry->hasSubLinks = pstate->p_hasSubLinks;
2983-
qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
2984-
qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
2985-
qry->hasAggs = pstate->p_hasAggs;
2986-
2987-
foreach(l, sstmt->lockingClause)
2988-
{
2989-
transformLockingClause(pstate, qry,
2990-
(LockingClause *) lfirst(l), false);
2991-
}
2992-
2993-
assign_query_collations(pstate, qry);
2994-
2995-
/* this must be done after collations, for reliable comparison of exprs */
2996-
if (pstate->p_hasAggs || qry->groupClause || qry->groupingSets || qry->havingQual)
2997-
parseCheckAggregates(pstate, qry);
2998-
2999-
return qry;
2958+
return list_make1(tle);
30002959
}
30012960

30022961

src/tools/pgindent/typedefs.list

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2685,6 +2685,7 @@ SecLabelStmt
26852685
SeenRelsEntry
26862686
SelectLimit
26872687
SelectStmt
2688+
SelectStmtPassthrough
26882689
Selectivity
26892690
SelfJoinCandidate
26902691
SemTPadded

0 commit comments

Comments
 (0)