Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * analyze.c
4 : : * transform the raw parse tree into a query tree
5 : : *
6 : : * For optimizable statements, we are careful to obtain a suitable lock on
7 : : * each referenced table, and other modules of the backend preserve or
8 : : * re-obtain these locks before depending on the results. It is therefore
9 : : * okay to do significant semantic analysis of these statements. For
10 : : * utility commands, no locks are obtained here (and if they were, we could
11 : : * not be sure we'd still have them at execution). Hence the general rule
12 : : * for utility commands is to just dump them into a Query node untransformed.
13 : : * DECLARE CURSOR, EXPLAIN, and CREATE TABLE AS are exceptions because they
14 : : * contain optimizable statements, which we should transform.
15 : : *
16 : : *
17 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
18 : : * Portions Copyright (c) 1994, Regents of the University of California
19 : : *
20 : : * src/backend/parser/analyze.c
21 : : *
22 : : *-------------------------------------------------------------------------
23 : : */
24 : :
25 : : #include "postgres.h"
26 : :
27 : : #include "access/stratnum.h"
28 : : #include "access/sysattr.h"
29 : : #include "catalog/dependency.h"
30 : : #include "catalog/pg_am.h"
31 : : #include "catalog/pg_operator.h"
32 : : #include "catalog/pg_proc.h"
33 : : #include "catalog/pg_type.h"
34 : : #include "commands/defrem.h"
35 : : #include "miscadmin.h"
36 : : #include "nodes/makefuncs.h"
37 : : #include "nodes/nodeFuncs.h"
38 : : #include "nodes/queryjumble.h"
39 : : #include "optimizer/optimizer.h"
40 : : #include "parser/analyze.h"
41 : : #include "parser/parse_agg.h"
42 : : #include "parser/parse_clause.h"
43 : : #include "parser/parse_coerce.h"
44 : : #include "parser/parse_collate.h"
45 : : #include "parser/parse_cte.h"
46 : : #include "parser/parse_expr.h"
47 : : #include "parser/parse_func.h"
48 : : #include "parser/parse_merge.h"
49 : : #include "parser/parse_oper.h"
50 : : #include "parser/parse_param.h"
51 : : #include "parser/parse_relation.h"
52 : : #include "parser/parse_target.h"
53 : : #include "parser/parse_type.h"
54 : : #include "parser/parsetree.h"
55 : : #include "utils/backend_status.h"
56 : : #include "utils/builtins.h"
57 : : #include "utils/fmgroids.h"
58 : : #include "utils/guc.h"
59 : : #include "utils/lsyscache.h"
60 : : #include "utils/rangetypes.h"
61 : : #include "utils/rel.h"
62 : : #include "utils/syscache.h"
63 : :
64 : :
65 : : /* Passthrough data for transformPLAssignStmtTarget */
66 : : typedef struct SelectStmtPassthrough
67 : : {
68 : : PLAssignStmt *stmt; /* the assignment statement */
69 : : Node *target; /* node representing the target variable */
70 : : List *indirection; /* indirection yet to be applied to target */
71 : : } SelectStmtPassthrough;
72 : :
73 : : /* Hook for plugins to get control at end of parse analysis */
74 : : post_parse_analyze_hook_type post_parse_analyze_hook = NULL;
75 : :
76 : : static Query *transformOptionalSelectInto(ParseState *pstate, Node *parseTree);
77 : : static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
78 : : static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt);
79 : : static OnConflictExpr *transformOnConflictClause(ParseState *pstate,
80 : : OnConflictClause *onConflictClause);
81 : : static ForPortionOfExpr *transformForPortionOfClause(ParseState *pstate,
82 : : int rtindex,
83 : : const ForPortionOfClause *forPortionOf,
84 : : bool isUpdate);
85 : : static int count_rowexpr_columns(ParseState *pstate, Node *expr);
86 : : static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt,
87 : : SelectStmtPassthrough *passthru);
88 : : static Query *transformValuesClause(ParseState *pstate, SelectStmt *stmt);
89 : : static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt);
90 : : static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
91 : : bool isTopLevel, List **targetlist);
92 : : static void determineRecursiveColTypes(ParseState *pstate,
93 : : Node *larg, List *nrtargetlist);
94 : : static Query *transformReturnStmt(ParseState *pstate, ReturnStmt *stmt);
95 : : static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
96 : : static Query *transformPLAssignStmt(ParseState *pstate,
97 : : PLAssignStmt *stmt);
98 : : static List *transformPLAssignStmtTarget(ParseState *pstate, List *tlist,
99 : : SelectStmtPassthrough *passthru);
100 : : static Query *transformDeclareCursorStmt(ParseState *pstate,
101 : : DeclareCursorStmt *stmt);
102 : : static Query *transformExplainStmt(ParseState *pstate,
103 : : ExplainStmt *stmt);
104 : : static Query *transformCreateTableAsStmt(ParseState *pstate,
105 : : CreateTableAsStmt *stmt);
106 : : static Query *transformCallStmt(ParseState *pstate,
107 : : CallStmt *stmt);
108 : : static void transformLockingClause(ParseState *pstate, Query *qry,
109 : : LockingClause *lc, bool pushedDown);
110 : : #ifdef DEBUG_NODE_TESTS_ENABLED
111 : : static bool test_raw_expression_coverage(Node *node, void *context);
112 : : #endif
113 : :
114 : :
115 : : /*
116 : : * parse_analyze_fixedparams
117 : : * Analyze a raw parse tree and transform it to Query form.
118 : : *
119 : : * Optionally, information about $n parameter types can be supplied.
120 : : * References to $n indexes not defined by paramTypes[] are disallowed.
121 : : *
122 : : * The result is a Query node. Optimizable statements require considerable
123 : : * transformation, while utility-type statements are simply hung off
124 : : * a dummy CMD_UTILITY Query node.
125 : : */
126 : : Query *
1523 peter@eisentraut.org 127 :CBC 489360 : parse_analyze_fixedparams(RawStmt *parseTree, const char *sourceText,
128 : : const Oid *paramTypes, int numParams,
129 : : QueryEnvironment *queryEnv)
130 : : {
8407 tgl@sss.pgh.pa.us 131 : 489360 : ParseState *pstate = make_parsestate(NULL);
132 : : Query *query;
1854 bruce@momjian.us 133 : 489360 : JumbleState *jstate = NULL;
134 : :
6172 135 [ - + ]: 489360 : Assert(sourceText != NULL); /* required as of 8.4 */
136 : :
7357 tgl@sss.pgh.pa.us 137 : 489360 : pstate->p_sourcetext = sourceText;
138 : :
6030 139 [ + + ]: 489360 : if (numParams > 0)
1523 peter@eisentraut.org 140 : 1602 : setup_parse_fixed_parameters(pstate, paramTypes, numParams);
141 : :
3322 kgrittn@postgresql.o 142 : 489360 : pstate->p_queryEnv = queryEnv;
143 : :
5160 tgl@sss.pgh.pa.us 144 : 489360 : query = transformTopLevelStmt(pstate, parseTree);
145 : :
1816 alvherre@alvh.no-ip. 146 [ + + ]: 483577 : if (IsQueryIdEnabled())
1042 michael@paquier.xyz 147 : 76790 : jstate = JumbleQuery(query);
148 : :
5152 tgl@sss.pgh.pa.us 149 [ + + ]: 483577 : if (post_parse_analyze_hook)
1854 bruce@momjian.us 150 : 76617 : (*post_parse_analyze_hook) (pstate, query, jstate);
151 : :
6891 tgl@sss.pgh.pa.us 152 : 483577 : free_parsestate(pstate);
153 : :
1841 bruce@momjian.us 154 : 483577 : pgstat_report_query_id(query->queryId, false);
155 : :
6891 tgl@sss.pgh.pa.us 156 : 483577 : return query;
157 : : }
158 : :
159 : : /*
160 : : * parse_analyze_varparams
161 : : *
162 : : * This variant is used when it's okay to deduce information about $n
163 : : * symbol datatypes from context. The passed-in paramTypes[] array can
164 : : * be modified or enlarged (via repalloc).
165 : : */
166 : : Query *
3398 167 : 5604 : parse_analyze_varparams(RawStmt *parseTree, const char *sourceText,
168 : : Oid **paramTypes, int *numParams,
169 : : QueryEnvironment *queryEnv)
170 : : {
8407 171 : 5604 : ParseState *pstate = make_parsestate(NULL);
172 : : Query *query;
1854 bruce@momjian.us 173 : 5604 : JumbleState *jstate = NULL;
174 : :
6172 175 [ - + ]: 5604 : Assert(sourceText != NULL); /* required as of 8.4 */
176 : :
7357 tgl@sss.pgh.pa.us 177 : 5604 : pstate->p_sourcetext = sourceText;
178 : :
1523 peter@eisentraut.org 179 : 5604 : setup_parse_variable_parameters(pstate, paramTypes, numParams);
180 : :
181 : 5604 : pstate->p_queryEnv = queryEnv;
182 : :
5160 tgl@sss.pgh.pa.us 183 : 5604 : query = transformTopLevelStmt(pstate, parseTree);
184 : :
185 : : /* make sure all is well with parameter types */
6030 186 : 5595 : check_variable_parameters(pstate, query);
187 : :
1816 alvherre@alvh.no-ip. 188 [ + + ]: 5595 : if (IsQueryIdEnabled())
1042 michael@paquier.xyz 189 : 266 : jstate = JumbleQuery(query);
190 : :
5152 tgl@sss.pgh.pa.us 191 [ + + ]: 5595 : if (post_parse_analyze_hook)
1854 bruce@momjian.us 192 : 266 : (*post_parse_analyze_hook) (pstate, query, jstate);
193 : :
6891 tgl@sss.pgh.pa.us 194 : 5595 : free_parsestate(pstate);
195 : :
1841 bruce@momjian.us 196 : 5595 : pgstat_report_query_id(query->queryId, false);
197 : :
6891 tgl@sss.pgh.pa.us 198 : 5595 : return query;
199 : : }
200 : :
201 : : /*
202 : : * parse_analyze_withcb
203 : : *
204 : : * This variant is used when the caller supplies their own parser callback to
205 : : * resolve parameters and possibly other things.
206 : : */
207 : : Query *
1518 peter@eisentraut.org 208 : 25414 : parse_analyze_withcb(RawStmt *parseTree, const char *sourceText,
209 : : ParserSetupHook parserSetup,
210 : : void *parserSetupArg,
211 : : QueryEnvironment *queryEnv)
212 : : {
213 : 25414 : ParseState *pstate = make_parsestate(NULL);
214 : : Query *query;
215 : 25414 : JumbleState *jstate = NULL;
216 : :
217 [ - + ]: 25414 : Assert(sourceText != NULL); /* required as of 8.4 */
218 : :
219 : 25414 : pstate->p_sourcetext = sourceText;
220 : 25414 : pstate->p_queryEnv = queryEnv;
221 : 25414 : (*parserSetup) (pstate, parserSetupArg);
222 : :
223 : 25414 : query = transformTopLevelStmt(pstate, parseTree);
224 : :
225 [ + + ]: 25339 : if (IsQueryIdEnabled())
1042 michael@paquier.xyz 226 : 4156 : jstate = JumbleQuery(query);
227 : :
1518 peter@eisentraut.org 228 [ + + ]: 25339 : if (post_parse_analyze_hook)
229 : 4153 : (*post_parse_analyze_hook) (pstate, query, jstate);
230 : :
231 : 25339 : free_parsestate(pstate);
232 : :
233 : 25339 : pgstat_report_query_id(query->queryId, false);
234 : :
235 : 25339 : return query;
236 : : }
237 : :
238 : :
239 : : /*
240 : : * parse_sub_analyze
241 : : * Entry point for recursively analyzing a sub-statement.
242 : : */
243 : : Query *
6082 tgl@sss.pgh.pa.us 244 : 72233 : parse_sub_analyze(Node *parseTree, ParseState *parentParseState,
245 : : CommonTableExpr *parentCTE,
246 : : bool locked_from_parent,
247 : : bool resolve_unknowns)
248 : : {
9341 249 : 72233 : ParseState *pstate = make_parsestate(parentParseState);
250 : : Query *query;
251 : :
6082 252 : 72233 : pstate->p_parent_cte = parentCTE;
6034 253 : 72233 : pstate->p_locked_from_parent = locked_from_parent;
3387 254 : 72233 : pstate->p_resolve_unknowns = resolve_unknowns;
255 : :
6891 256 : 72233 : query = transformStmt(pstate, parseTree);
257 : :
258 : 72092 : free_parsestate(pstate);
259 : :
260 : 72092 : return query;
261 : : }
262 : :
263 : : /*
264 : : * transformTopLevelStmt -
265 : : * transform a Parse tree into a Query tree.
266 : : *
267 : : * This function is just responsible for transferring statement location data
268 : : * from the RawStmt into the finished Query.
269 : : */
270 : : Query *
3398 271 : 522769 : transformTopLevelStmt(ParseState *pstate, RawStmt *parseTree)
272 : : {
273 : : Query *result;
274 : :
275 : : /* We're at top level, so allow SELECT INTO */
276 : 522769 : result = transformOptionalSelectInto(pstate, parseTree->stmt);
277 : :
327 michael@paquier.xyz 278 : 516898 : result->stmt_location = parseTree->stmt_location;
279 : 516898 : result->stmt_len = parseTree->stmt_len;
280 : :
3398 tgl@sss.pgh.pa.us 281 : 516898 : return result;
282 : : }
283 : :
284 : : /*
285 : : * transformOptionalSelectInto -
286 : : * If SELECT has INTO, convert it to CREATE TABLE AS.
287 : : *
288 : : * The only thing we do here that we don't do in transformStmt() is to
289 : : * convert SELECT ... INTO into CREATE TABLE AS. Since utility statements
290 : : * aren't allowed within larger statements, this is only allowed at the top
291 : : * of the parse tree, and so we only try it before entering the recursive
292 : : * transformStmt() processing.
293 : : */
294 : : static Query *
295 : 539260 : transformOptionalSelectInto(ParseState *pstate, Node *parseTree)
296 : : {
5160 297 [ + + ]: 539260 : if (IsA(parseTree, SelectStmt))
298 : : {
299 : 240285 : SelectStmt *stmt = (SelectStmt *) parseTree;
300 : :
301 : : /* If it's a set-operation tree, drill down to leftmost SelectStmt */
302 [ + - + + ]: 247561 : while (stmt && stmt->op != SETOP_NONE)
303 : 7276 : stmt = stmt->larg;
2180 304 [ + - + - : 240285 : Assert(stmt && IsA(stmt, SelectStmt) && stmt->larg == NULL);
- + ]
305 : :
5160 306 [ + + ]: 240285 : if (stmt->intoClause)
307 : : {
308 : 70 : CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
309 : :
310 : 70 : ctas->query = parseTree;
311 : 70 : ctas->into = stmt->intoClause;
2124 michael@paquier.xyz 312 : 70 : ctas->objtype = OBJECT_TABLE;
5160 tgl@sss.pgh.pa.us 313 : 70 : ctas->is_select_into = true;
314 : :
315 : : /*
316 : : * Remove the intoClause from the SelectStmt. This makes it safe
317 : : * for transformSelectStmt to complain if it finds intoClause set
318 : : * (implying that the INTO appeared in a disallowed place).
319 : : */
320 : 70 : stmt->intoClause = NULL;
321 : :
322 : 70 : parseTree = (Node *) ctas;
323 : : }
324 : : }
325 : :
326 : 539260 : return transformStmt(pstate, parseTree);
327 : : }
328 : :
329 : : /*
330 : : * transformStmt -
331 : : * recursively transform a Parse tree into a Query tree.
332 : : */
333 : : Query *
6891 334 : 623938 : transformStmt(ParseState *pstate, Node *parseTree)
335 : : {
336 : : Query *result;
337 : :
338 : : #ifdef DEBUG_NODE_TESTS_ENABLED
339 : :
340 : : /*
341 : : * We apply debug_raw_expression_coverage_test testing to basic DML
342 : : * statements; we can't just run it on everything because
343 : : * raw_expression_tree_walker() doesn't claim to handle utility
344 : : * statements.
345 : : */
642 peter@eisentraut.org 346 [ - + ]: 623938 : if (Debug_raw_expression_coverage_test)
347 : : {
642 peter@eisentraut.org 348 [ # # ]:UBC 0 : switch (nodeTag(parseTree))
349 : : {
350 : 0 : case T_SelectStmt:
351 : : case T_InsertStmt:
352 : : case T_UpdateStmt:
353 : : case T_DeleteStmt:
354 : : case T_MergeStmt:
355 : 0 : (void) test_raw_expression_coverage(parseTree, NULL);
356 : 0 : break;
357 : 0 : default:
358 : 0 : break;
359 : : }
360 : : }
361 : : #endif /* DEBUG_NODE_TESTS_ENABLED */
362 : :
363 : : /*
364 : : * Caution: when changing the set of statement types that have non-default
365 : : * processing here, see also stmt_requires_parse_analysis() and
366 : : * analyze_requires_snapshot().
367 : : */
10467 bruce@momjian.us 368 [ + + + + :CBC 623938 : switch (nodeTag(parseTree))
+ + + + +
+ + + ]
369 : : {
370 : : /*
371 : : * Optimizable statements
372 : : */
10343 373 : 40673 : case T_InsertStmt:
6891 tgl@sss.pgh.pa.us 374 : 40673 : result = transformInsertStmt(pstate, (InsertStmt *) parseTree);
10466 bruce@momjian.us 375 : 39678 : break;
376 : :
377 : 3336 : case T_DeleteStmt:
378 : 3336 : result = transformDeleteStmt(pstate, (DeleteStmt *) parseTree);
379 : 3239 : break;
380 : :
10343 381 : 9248 : case T_UpdateStmt:
382 : 9248 : result = transformUpdateStmt(pstate, (UpdateStmt *) parseTree);
10466 383 : 9106 : break;
384 : :
1499 alvherre@alvh.no-ip. 385 : 1382 : case T_MergeStmt:
386 : 1382 : result = transformMergeStmt(pstate, (MergeStmt *) parseTree);
387 : 1338 : break;
388 : :
10343 bruce@momjian.us 389 : 321196 : case T_SelectStmt:
390 : : {
7216 mail@joeconway.com 391 : 321196 : SelectStmt *n = (SelectStmt *) parseTree;
392 : :
393 [ + + ]: 321196 : if (n->valuesLists)
394 : 5787 : result = transformValuesClause(pstate, n);
395 [ + + ]: 315409 : else if (n->op == SETOP_NONE)
220 tgl@sss.pgh.pa.us 396 :GNC 306812 : result = transformSelectStmt(pstate, n, NULL);
397 : : else
7216 mail@joeconway.com 398 :CBC 8597 : result = transformSetOperationStmt(pstate, n);
399 : : }
10466 bruce@momjian.us 400 : 316492 : break;
401 : :
1854 peter@eisentraut.org 402 : 2738 : case T_ReturnStmt:
403 : 2738 : result = transformReturnStmt(pstate, (ReturnStmt *) parseTree);
404 : 2734 : break;
405 : :
1947 tgl@sss.pgh.pa.us 406 : 3547 : case T_PLAssignStmt:
407 : 3547 : result = transformPLAssignStmt(pstate,
408 : : (PLAssignStmt *) parseTree);
409 : 3534 : break;
410 : :
411 : : /*
412 : : * Special cases
413 : : */
6948 414 : 2699 : case T_DeclareCursorStmt:
415 : 2699 : result = transformDeclareCursorStmt(pstate,
416 : : (DeclareCursorStmt *) parseTree);
417 : 2686 : break;
418 : :
419 : 16491 : case T_ExplainStmt:
420 : 16491 : result = transformExplainStmt(pstate,
421 : : (ExplainStmt *) parseTree);
422 : 16482 : break;
423 : :
5160 424 : 1306 : case T_CreateTableAsStmt:
425 : 1306 : result = transformCreateTableAsStmt(pstate,
426 : : (CreateTableAsStmt *) parseTree);
427 : 1297 : break;
428 : :
2996 peter_e@gmx.net 429 : 312 : case T_CallStmt:
430 : 312 : result = transformCallStmt(pstate,
431 : : (CallStmt *) parseTree);
2969 432 : 291 : break;
433 : :
10466 bruce@momjian.us 434 : 221010 : default:
435 : :
436 : : /*
437 : : * other statements don't require any transformation; just return
438 : : * the original parsetree with a Query node plastered on top.
439 : : */
440 : 221010 : result = makeNode(Query);
441 : 221010 : result->commandType = CMD_UTILITY;
154 peter@eisentraut.org 442 :GNC 221010 : result->utilityStmt = parseTree;
10466 bruce@momjian.us 443 :CBC 221010 : break;
444 : : }
445 : :
446 : : /* Mark as original query until we learn differently */
8404 tgl@sss.pgh.pa.us 447 : 617887 : result->querySource = QSRC_ORIGINAL;
448 : 617887 : result->canSetTag = true;
449 : :
10467 bruce@momjian.us 450 : 617887 : return result;
451 : : }
452 : :
453 : : /*
454 : : * stmt_requires_parse_analysis
455 : : * Returns true if parse analysis will do anything non-trivial
456 : : * with the given raw parse tree.
457 : : *
458 : : * Generally, this should return true for any statement type for which
459 : : * transformStmt() does more than wrap a CMD_UTILITY Query around it.
460 : : * When it returns false, the caller can assume that there is no situation
461 : : * in which parse analysis of the raw statement could need to be re-done.
462 : : *
463 : : * Currently, since the rewriter and planner do nothing for CMD_UTILITY
464 : : * Queries, a false result means that the entire parse analysis/rewrite/plan
465 : : * pipeline will never need to be re-done. If that ever changes, callers
466 : : * will likely need adjustment.
467 : : */
468 : : bool
985 tgl@sss.pgh.pa.us 469 : 20346340 : stmt_requires_parse_analysis(RawStmt *parseTree)
470 : : {
471 : : bool result;
472 : :
3398 473 [ + + + ]: 20346340 : switch (nodeTag(parseTree->stmt))
474 : : {
475 : : /*
476 : : * Optimizable statements
477 : : */
6352 478 : 19796154 : case T_InsertStmt:
479 : : case T_DeleteStmt:
480 : : case T_UpdateStmt:
481 : : case T_MergeStmt:
482 : : case T_SelectStmt:
483 : : case T_ReturnStmt:
484 : : case T_PLAssignStmt:
485 : 19796154 : result = true;
486 : 19796154 : break;
487 : :
488 : : /*
489 : : * Special cases
490 : : */
491 : 33904 : case T_DeclareCursorStmt:
492 : : case T_ExplainStmt:
493 : : case T_CreateTableAsStmt:
494 : : case T_CallStmt:
495 : 33904 : result = true;
496 : 33904 : break;
497 : :
498 : 516282 : default:
499 : : /* all other statements just get wrapped in a CMD_UTILITY Query */
500 : 516282 : result = false;
501 : 516282 : break;
502 : : }
503 : :
504 : 20346340 : return result;
505 : : }
506 : :
507 : : /*
508 : : * analyze_requires_snapshot
509 : : * Returns true if a snapshot must be set before doing parse analysis
510 : : * on the given raw parse tree.
511 : : */
512 : : bool
985 513 : 457336 : analyze_requires_snapshot(RawStmt *parseTree)
514 : : {
515 : : /*
516 : : * Currently, this should return true in exactly the same cases that
517 : : * stmt_requires_parse_analysis() does, so we just invoke that function
518 : : * rather than duplicating it. We keep the two entry points separate for
519 : : * clarity of callers, since from the callers' standpoint these are
520 : : * different conditions.
521 : : *
522 : : * While there may someday be a statement type for which transformStmt()
523 : : * does something nontrivial and yet no snapshot is needed for that
524 : : * processing, it seems likely that making such a choice would be fragile.
525 : : * If you want to install an exception, document the reasoning for it in a
526 : : * comment.
527 : : */
528 : 457336 : return stmt_requires_parse_analysis(parseTree);
529 : : }
530 : :
531 : : /*
532 : : * query_requires_rewrite_plan()
533 : : * Returns true if rewriting or planning is non-trivial for this Query.
534 : : *
535 : : * This is much like stmt_requires_parse_analysis(), but applies one step
536 : : * further down the pipeline.
537 : : *
538 : : * We do not provide an equivalent of analyze_requires_snapshot(): callers
539 : : * can assume that any rewriting or planning activity needs a snapshot.
540 : : */
541 : : bool
398 542 : 362322 : query_requires_rewrite_plan(Query *query)
543 : : {
544 : : bool result;
545 : :
546 [ + - ]: 362322 : if (query->commandType != CMD_UTILITY)
547 : : {
548 : : /* All optimizable statements require rewriting/planning */
549 : 362322 : result = true;
550 : : }
551 : : else
552 : : {
553 : : /* This list should match stmt_requires_parse_analysis() */
398 tgl@sss.pgh.pa.us 554 [ # # ]:UBC 0 : switch (nodeTag(query->utilityStmt))
555 : : {
556 : 0 : case T_DeclareCursorStmt:
557 : : case T_ExplainStmt:
558 : : case T_CreateTableAsStmt:
559 : : case T_CallStmt:
560 : 0 : result = true;
561 : 0 : break;
562 : 0 : default:
563 : 0 : result = false;
564 : 0 : break;
565 : : }
566 : : }
398 tgl@sss.pgh.pa.us 567 :CBC 362322 : return result;
568 : : }
569 : :
570 : : /*
571 : : * transformDeleteStmt -
572 : : * transforms a Delete Statement
573 : : */
574 : : static Query *
10466 bruce@momjian.us 575 : 3336 : transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
576 : : {
577 : 3336 : Query *qry = makeNode(Query);
578 : : ParseNamespaceItem *nsitem;
579 : : Node *qual;
580 : :
10467 581 : 3336 : qry->commandType = CMD_DELETE;
582 : :
583 : : /* process the WITH clause independently of all else */
5681 tgl@sss.pgh.pa.us 584 [ + + ]: 3336 : if (stmt->withClause)
585 : : {
586 : 20 : qry->hasRecursive = stmt->withClause->recursive;
587 : 20 : qry->cteList = transformWithClause(pstate, stmt->withClause);
5548 588 : 20 : qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
589 : : }
590 : :
591 : : /* set up range table with just the result rel */
8810 592 : 6668 : qry->resultRelation = setTargetTable(pstate, stmt->relation,
3420 593 : 3336 : stmt->relation->inh,
594 : : true,
595 : : ACL_DELETE);
2315 596 : 3332 : nsitem = pstate->p_target_nsitem;
597 : :
598 : : /* disallow DELETE ... WHERE CURRENT OF on a view */
13 dean.a.rasheed@gmail 599 [ + + ]: 3332 : if (stmt->whereClause &&
600 [ + + ]: 2259 : IsA(stmt->whereClause, CurrentOfExpr) &&
601 [ + + ]: 76 : pstate->p_target_relation->rd_rel->relkind == RELKIND_VIEW)
602 [ + - ]: 4 : ereport(ERROR,
603 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
604 : : errmsg("WHERE CURRENT OF on a view is not implemented"));
605 : :
606 : : /* there's no DISTINCT in DELETE */
9595 tgl@sss.pgh.pa.us 607 : 3328 : qry->distinctClause = NIL;
608 : :
609 : : /* subqueries in USING cannot access the result relation */
4501 610 : 3328 : nsitem->p_lateral_only = true;
4497 611 : 3328 : nsitem->p_lateral_ok = false;
612 : :
613 : : /*
614 : : * The USING clause is non-standard SQL syntax, and is equivalent in
615 : : * functionality to the FROM list that can be specified for UPDATE. The
616 : : * USING keyword is used rather than FROM because FROM is already a
617 : : * keyword in the DELETE syntax.
618 : : */
7698 neilc@samurai.com 619 : 3328 : transformFromClause(pstate, stmt->usingClause);
620 : :
621 : : /* remaining clauses can reference the result relation normally */
4501 tgl@sss.pgh.pa.us 622 : 3316 : nsitem->p_lateral_only = false;
4497 623 : 3316 : nsitem->p_lateral_ok = true;
624 : :
34 peter@eisentraut.org 625 [ + + ]:GNC 3316 : if (stmt->forPortionOf)
626 : 376 : qry->forPortionOf = transformForPortionOfClause(pstate,
627 : : qry->resultRelation,
628 : 433 : stmt->forPortionOf,
629 : : false);
630 : :
5016 tgl@sss.pgh.pa.us 631 :CBC 3259 : qual = transformWhereClause(pstate, stmt->whereClause,
632 : : EXPR_KIND_WHERE, "WHERE");
633 : :
474 dean.a.rasheed@gmail 634 : 3243 : transformReturningClause(pstate, qry, stmt->returningClause,
635 : : EXPR_KIND_RETURNING);
636 : :
637 : : /* done building the range table and jointree */
10467 bruce@momjian.us 638 : 3239 : qry->rtable = pstate->p_rtable;
1246 alvherre@alvh.no-ip. 639 : 3239 : qry->rteperminfos = pstate->p_rteperminfos;
9349 tgl@sss.pgh.pa.us 640 : 3239 : qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
641 : :
9668 642 : 3239 : qry->hasSubLinks = pstate->p_hasSubLinks;
6337 643 : 3239 : qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
3521 644 : 3239 : qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
5200 645 : 3239 : qry->hasAggs = pstate->p_hasAggs;
646 : :
5526 647 : 3239 : assign_query_collations(pstate, qry);
648 : :
649 : : /* this must be done after collations, for reliable comparison of exprs */
2665 rhodiumtoad@postgres 650 [ - + ]: 3239 : if (pstate->p_hasAggs)
2665 rhodiumtoad@postgres 651 :UBC 0 : parseCheckAggregates(pstate, qry);
652 : :
9343 tgl@sss.pgh.pa.us 653 :CBC 3239 : return qry;
654 : : }
655 : :
656 : : /*
657 : : * transformInsertStmt -
658 : : * transform an Insert Statement
659 : : */
660 : : static Query *
6891 661 : 40673 : transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
662 : : {
9787 663 : 40673 : Query *qry = makeNode(Query);
7216 mail@joeconway.com 664 : 40673 : SelectStmt *selectStmt = (SelectStmt *) stmt->selectStmt;
665 : 40673 : List *exprList = NIL;
666 : : bool isGeneralSelect;
667 : : List *sub_rtable;
668 : : List *sub_rteperminfos;
669 : : List *sub_namespace;
670 : : List *icolumns;
671 : : List *attrnos;
672 : : ParseNamespaceItem *nsitem;
673 : : RTEPermissionInfo *perminfo;
674 : : ListCell *icols;
675 : : ListCell *attnos;
676 : : ListCell *lc;
677 : : bool requiresUpdatePerm;
678 : : AclMode targetPerms;
679 : :
680 : : /* There can't be any outer WITH to worry about */
5681 tgl@sss.pgh.pa.us 681 [ - + ]: 40673 : Assert(pstate->p_ctenamespace == NIL);
682 : :
10467 bruce@momjian.us 683 : 40673 : qry->commandType = CMD_INSERT;
684 : :
685 : : /* process the WITH clause independently of all else */
5681 tgl@sss.pgh.pa.us 686 [ + + ]: 40673 : if (stmt->withClause)
687 : : {
688 : 189 : qry->hasRecursive = stmt->withClause->recursive;
689 : 189 : qry->cteList = transformWithClause(pstate, stmt->withClause);
5548 690 : 189 : qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
691 : : }
692 : :
3316 peter_e@gmx.net 693 : 40673 : qry->override = stmt->override;
694 : :
695 : : /*
696 : : * ON CONFLICT DO UPDATE and ON CONFLICT DO SELECT FOR UPDATE/SHARE
697 : : * require UPDATE permission on the target relation.
698 : : */
82 dean.a.rasheed@gmail 699 [ + + ]:GNC 42232 : requiresUpdatePerm = (stmt->onConflictClause &&
700 [ + + ]: 1559 : (stmt->onConflictClause->action == ONCONFLICT_UPDATE ||
701 [ + + ]: 626 : (stmt->onConflictClause->action == ONCONFLICT_SELECT &&
702 [ + + ]: 240 : stmt->onConflictClause->lockStrength != LCS_NONE)));
703 : :
704 : : /*
705 : : * We have three cases to deal with: DEFAULT VALUES (selectStmt == NULL),
706 : : * VALUES list, or general SELECT input. We special-case VALUES, both for
707 : : * efficiency and so we can handle DEFAULT specifications.
708 : : *
709 : : * The grammar allows attaching ORDER BY, LIMIT, FOR UPDATE, or WITH to a
710 : : * VALUES clause. If we have any of those, treat it as a general SELECT;
711 : : * so it will work, but you can't use DEFAULT items together with those.
712 : : */
5694 tgl@sss.pgh.pa.us 713 [ + + + + ]:CBC 71375 : isGeneralSelect = (selectStmt && (selectStmt->valuesLists == NIL ||
714 [ + - ]: 30702 : selectStmt->sortClause != NIL ||
715 [ + - ]: 30702 : selectStmt->limitOffset != NULL ||
716 [ + - ]: 30702 : selectStmt->limitCount != NULL ||
717 [ + - ]: 30702 : selectStmt->lockingClause != NIL ||
718 [ - + ]: 30702 : selectStmt->withClause != NULL));
719 : :
720 : : /*
721 : : * If a non-nil rangetable/namespace was passed in, and we are doing
722 : : * INSERT/SELECT, arrange to pass the rangetable/rteperminfos/namespace
723 : : * down to the SELECT. This can only happen if we are inside a CREATE
724 : : * RULE, and in that case we want the rule's OLD and NEW rtable entries to
725 : : * appear as part of the SELECT's rtable, not as outer references for it.
726 : : * (Kluge!) The SELECT's joinlist is not affected however. We must do
727 : : * this before adding the target table to the INSERT's rtable.
728 : : */
7216 mail@joeconway.com 729 [ + + ]: 40673 : if (isGeneralSelect)
730 : : {
9211 tgl@sss.pgh.pa.us 731 : 4441 : sub_rtable = pstate->p_rtable;
732 : 4441 : pstate->p_rtable = NIL;
1246 alvherre@alvh.no-ip. 733 : 4441 : sub_rteperminfos = pstate->p_rteperminfos;
734 : 4441 : pstate->p_rteperminfos = NIL;
5018 tgl@sss.pgh.pa.us 735 : 4441 : sub_namespace = pstate->p_namespace;
736 : 4441 : pstate->p_namespace = NIL;
737 : : }
738 : : else
739 : : {
9211 740 : 36232 : sub_rtable = NIL; /* not used, but keep compiler quiet */
1225 741 : 36232 : sub_rteperminfos = NIL;
5018 742 : 36232 : sub_namespace = NIL;
743 : : }
744 : :
745 : : /*
746 : : * Must get write lock on INSERT target table before scanning SELECT, else
747 : : * we will grab the wrong kind of initial lock if the target table is also
748 : : * mentioned in the SELECT part. Note that the target table is not added
749 : : * to the joinlist or namespace.
750 : : */
4015 andres@anarazel.de 751 : 40673 : targetPerms = ACL_INSERT;
82 dean.a.rasheed@gmail 752 [ + + ]:GNC 40673 : if (requiresUpdatePerm)
4015 andres@anarazel.de 753 :CBC 1011 : targetPerms |= ACL_UPDATE;
8810 tgl@sss.pgh.pa.us 754 : 40673 : qry->resultRelation = setTargetTable(pstate, stmt->relation,
755 : : false, false, targetPerms);
756 : :
757 : : /* Validate stmt->cols list, or build default list if no list given */
7216 mail@joeconway.com 758 : 40657 : icolumns = checkInsertTargets(pstate, stmt->cols, &attrnos);
759 [ - + ]: 40625 : Assert(list_length(icolumns) == list_length(attrnos));
760 : :
761 : : /*
762 : : * Determine which variant of INSERT we have.
763 : : */
764 [ + + ]: 40625 : if (selectStmt == NULL)
765 : : {
766 : : /*
767 : : * We have INSERT ... DEFAULT VALUES. We can handle this case by
768 : : * emitting an empty targetlist --- all columns will be defaulted when
769 : : * the planner expands the targetlist.
770 : : */
771 : 5530 : exprList = NIL;
772 : : }
773 [ + + ]: 35095 : else if (isGeneralSelect)
774 : : {
775 : : /*
776 : : * We make the sub-pstate a child of the outer pstate so that it can
777 : : * see any Param definitions supplied from above. Since the outer
778 : : * pstate's rtable and namespace are presently empty, there are no
779 : : * side-effects of exposing names the sub-SELECT shouldn't be able to
780 : : * see.
781 : : */
8407 tgl@sss.pgh.pa.us 782 : 4441 : ParseState *sub_pstate = make_parsestate(pstate);
783 : : Query *selectQuery;
784 : :
785 : : /*
786 : : * Process the source SELECT.
787 : : *
788 : : * It is important that this be handled just like a standalone SELECT;
789 : : * otherwise the behavior of SELECT within INSERT might be different
790 : : * from a stand-alone SELECT. (Indeed, Postgres up through 6.5 had
791 : : * bugs of just that nature...)
792 : : *
793 : : * The sole exception is that we prevent resolving unknown-type
794 : : * outputs as TEXT. This does not change the semantics since if the
795 : : * column type matters semantically, it would have been resolved to
796 : : * something else anyway. Doing this lets us resolve such outputs as
797 : : * the target column's type, which we handle below.
798 : : */
9211 799 : 4441 : sub_pstate->p_rtable = sub_rtable;
1246 alvherre@alvh.no-ip. 800 : 4441 : sub_pstate->p_rteperminfos = sub_rteperminfos;
6172 bruce@momjian.us 801 : 4441 : sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */
1191 tgl@sss.pgh.pa.us 802 : 4441 : sub_pstate->p_nullingrels = NIL;
5018 803 : 4441 : sub_pstate->p_namespace = sub_namespace;
3387 804 : 4441 : sub_pstate->p_resolve_unknowns = false;
805 : :
6891 806 : 4441 : selectQuery = transformStmt(sub_pstate, stmt->selectStmt);
807 : :
808 : 4437 : free_parsestate(sub_pstate);
809 : :
810 : : /* The grammar should have produced a SELECT */
6455 811 [ + - ]: 4437 : if (!IsA(selectQuery, Query) ||
3398 812 [ - + ]: 4437 : selectQuery->commandType != CMD_SELECT)
6455 tgl@sss.pgh.pa.us 813 [ # # ]:UBC 0 : elog(ERROR, "unexpected non-SELECT command in INSERT ... SELECT");
814 : :
815 : : /*
816 : : * Make the source be a subquery in the INSERT's rangetable, and add
817 : : * it to the INSERT's joinlist (but not the namespace).
818 : : */
2315 tgl@sss.pgh.pa.us 819 :CBC 4437 : nsitem = addRangeTableEntryForSubquery(pstate,
820 : : selectQuery,
821 : : NULL,
822 : : false,
823 : : false);
824 : 4437 : addNSItemToQuery(pstate, nsitem, true, false, false);
825 : :
826 : : /*----------
827 : : * Generate an expression list for the INSERT that selects all the
828 : : * non-resjunk columns from the subquery. (INSERT's tlist must be
829 : : * separate from the subquery's tlist because we may add columns,
830 : : * insert datatype coercions, etc.)
831 : : *
832 : : * HACK: unknown-type constants and params in the SELECT's targetlist
833 : : * are copied up as-is rather than being referenced as subquery
834 : : * outputs. This is to ensure that when we try to coerce them to
835 : : * the target column's datatype, the right things happen (see
836 : : * special cases in coerce_type). Otherwise, this fails:
837 : : * INSERT INTO foo SELECT 'bar', ... FROM baz
838 : : *----------
839 : : */
7216 mail@joeconway.com 840 : 4437 : exprList = NIL;
841 [ + + + + : 15626 : foreach(lc, selectQuery->targetList)
+ + ]
842 : : {
843 : 11189 : TargetEntry *tle = (TargetEntry *) lfirst(lc);
844 : : Expr *expr;
845 : :
7699 tgl@sss.pgh.pa.us 846 [ + + ]: 11189 : if (tle->resjunk)
9343 847 : 64 : continue;
8407 848 [ + - ]: 11125 : if (tle->expr &&
2180 849 [ + + + + : 13804 : (IsA(tle->expr, Const) || IsA(tle->expr, Param)) &&
+ + ]
8407 850 : 2679 : exprType((Node *) tle->expr) == UNKNOWNOID)
9343 851 : 837 : expr = tle->expr;
852 : : else
853 : : {
2315 854 : 10288 : Var *var = makeVarFromTargetEntry(nsitem->p_rtindex, tle);
855 : :
6419 856 : 10288 : var->location = exprLocation((Node *) tle->expr);
857 : 10288 : expr = (Expr *) var;
858 : : }
7216 mail@joeconway.com 859 : 11125 : exprList = lappend(exprList, expr);
860 : : }
861 : :
862 : : /* Prepare row for assignment to target table */
863 : 4437 : exprList = transformInsertRow(pstate, exprList,
864 : : stmt->cols,
865 : : icolumns, attrnos,
866 : : false);
867 : : }
868 [ + + ]: 30654 : else if (list_length(selectStmt->valuesLists) > 1)
869 : : {
870 : : /*
871 : : * Process INSERT ... VALUES with multiple VALUES sublists. We
872 : : * generate a VALUES RTE holding the transformed expression lists, and
873 : : * build up a targetlist containing Vars that reference the VALUES
874 : : * RTE.
875 : : */
876 : 3295 : List *exprsLists = NIL;
3435 tgl@sss.pgh.pa.us 877 : 3295 : List *coltypes = NIL;
878 : 3295 : List *coltypmods = NIL;
879 : 3295 : List *colcollations = NIL;
7216 mail@joeconway.com 880 : 3295 : int sublist_length = -1;
5007 tgl@sss.pgh.pa.us 881 : 3295 : bool lateral = false;
882 : :
5160 883 [ - + ]: 3295 : Assert(selectStmt->intoClause == NULL);
884 : :
7216 mail@joeconway.com 885 [ + - + + : 14590 : foreach(lc, selectStmt->valuesLists)
+ + ]
886 : : {
7153 bruce@momjian.us 887 : 11295 : List *sublist = (List *) lfirst(lc);
888 : :
889 : : /*
890 : : * Do basic expression transformation (same as a ROW() expr, but
891 : : * allow SetToDefault at top level)
892 : : */
3451 tgl@sss.pgh.pa.us 893 : 11295 : sublist = transformExpressionList(pstate, sublist,
894 : : EXPR_KIND_VALUES, true);
895 : :
896 : : /*
897 : : * All the sublists must be the same length, *after*
898 : : * transformation (which might expand '*' into multiple items).
899 : : * The VALUES RTE can't handle anything different.
900 : : */
7216 mail@joeconway.com 901 [ + + ]: 11295 : if (sublist_length < 0)
902 : : {
903 : : /* Remember post-transformation length of first sublist */
904 : 3295 : sublist_length = list_length(sublist);
905 : : }
906 [ - + ]: 8000 : else if (sublist_length != list_length(sublist))
907 : : {
7216 mail@joeconway.com 908 [ # # ]:UBC 0 : ereport(ERROR,
909 : : (errcode(ERRCODE_SYNTAX_ERROR),
910 : : errmsg("VALUES lists must all be the same length"),
911 : : parser_errposition(pstate,
912 : : exprLocation((Node *) sublist))));
913 : : }
914 : :
915 : : /*
916 : : * Prepare row for assignment to target table. We process any
917 : : * indirection on the target column specs normally but then strip
918 : : * off the resulting field/array assignment nodes, since we don't
919 : : * want the parsed statement to contain copies of those in each
920 : : * VALUES row. (It's annoying to have to transform the
921 : : * indirection specs over and over like this, but avoiding it
922 : : * would take some really messy refactoring of
923 : : * transformAssignmentIndirection.)
924 : : */
7216 mail@joeconway.com 925 :CBC 11295 : sublist = transformInsertRow(pstate, sublist,
926 : : stmt->cols,
927 : : icolumns, attrnos,
928 : : true);
929 : :
930 : : /*
931 : : * We must assign collations now because assign_query_collations
932 : : * doesn't process rangetable entries. We just assign all the
933 : : * collations independently in each row, and don't worry about
934 : : * whether they are consistent vertically. The outer INSERT query
935 : : * isn't going to care about the collations of the VALUES columns,
936 : : * so it's not worth the effort to identify a common collation for
937 : : * each one here. (But note this does have one user-visible
938 : : * consequence: INSERT ... VALUES won't complain about conflicting
939 : : * explicit COLLATEs in a column, whereas the same VALUES
940 : : * construct in another context would complain.)
941 : : */
5526 tgl@sss.pgh.pa.us 942 : 11295 : assign_list_collations(pstate, sublist);
943 : :
7216 mail@joeconway.com 944 : 11295 : exprsLists = lappend(exprsLists, sublist);
945 : : }
946 : :
947 : : /*
948 : : * Construct column type/typmod/collation lists for the VALUES RTE.
949 : : * Every expression in each column has been coerced to the type/typmod
950 : : * of the corresponding target column or subfield, so it's sufficient
951 : : * to look at the exprType/exprTypmod of the first row. We don't care
952 : : * about the collation labeling, so just fill in InvalidOid for that.
953 : : */
3435 tgl@sss.pgh.pa.us 954 [ + - + + : 9586 : foreach(lc, (List *) linitial(exprsLists))
+ + ]
955 : : {
956 : 6291 : Node *val = (Node *) lfirst(lc);
957 : :
958 : 6291 : coltypes = lappend_oid(coltypes, exprType(val));
959 : 6291 : coltypmods = lappend_int(coltypmods, exprTypmod(val));
960 : 6291 : colcollations = lappend_oid(colcollations, InvalidOid);
961 : : }
962 : :
963 : : /*
964 : : * Ordinarily there can't be any current-level Vars in the expression
965 : : * lists, because the namespace was empty ... but if we're inside
966 : : * CREATE RULE, then NEW/OLD references might appear. In that case we
967 : : * have to mark the VALUES RTE as LATERAL.
968 : : */
7216 969 [ + + + - ]: 3313 : if (list_length(pstate->p_rtable) != 1 &&
970 : 18 : contain_vars_of_level((Node *) exprsLists, 0))
5007 971 : 18 : lateral = true;
972 : :
973 : : /*
974 : : * Generate the VALUES RTE
975 : : */
2315 976 : 3295 : nsitem = addRangeTableEntryForValues(pstate, exprsLists,
977 : : coltypes, coltypmods, colcollations,
978 : : NULL, lateral, true);
979 : 3295 : addNSItemToQuery(pstate, nsitem, true, false, false);
980 : :
981 : : /*
982 : : * Generate list of Vars referencing the RTE
983 : : */
1191 984 : 3295 : exprList = expandNSItemVars(pstate, nsitem, 0, -1, NULL);
985 : :
986 : : /*
987 : : * Re-apply any indirection on the target column specs to the Vars
988 : : */
3562 989 : 3295 : exprList = transformInsertRow(pstate, exprList,
990 : : stmt->cols,
991 : : icolumns, attrnos,
992 : : false);
993 : : }
994 : : else
995 : : {
996 : : /*
997 : : * Process INSERT ... VALUES with a single VALUES sublist. We treat
998 : : * this case separately for efficiency. The sublist is just computed
999 : : * directly as the Query's targetlist, with no VALUES RTE. So it
1000 : : * works just like a SELECT without any FROM.
1001 : : */
7216 mail@joeconway.com 1002 : 27359 : List *valuesLists = selectStmt->valuesLists;
1003 : :
1004 [ - + ]: 27359 : Assert(list_length(valuesLists) == 1);
5160 tgl@sss.pgh.pa.us 1005 [ - + ]: 27359 : Assert(selectStmt->intoClause == NULL);
1006 : :
1007 : : /*
1008 : : * Do basic expression transformation (same as a ROW() expr, but allow
1009 : : * SetToDefault at top level)
1010 : : */
7216 mail@joeconway.com 1011 : 27359 : exprList = transformExpressionList(pstate,
5016 tgl@sss.pgh.pa.us 1012 : 27359 : (List *) linitial(valuesLists),
1013 : : EXPR_KIND_VALUES_SINGLE,
1014 : : true);
1015 : :
1016 : : /* Prepare row for assignment to target table */
7216 mail@joeconway.com 1017 : 27343 : exprList = transformInsertRow(pstate, exprList,
1018 : : stmt->cols,
1019 : : icolumns, attrnos,
1020 : : false);
1021 : : }
1022 : :
1023 : : /*
1024 : : * Generate query's target list using the computed list of expressions.
1025 : : * Also, mark all the target columns as needing insert permissions.
1026 : : */
1246 alvherre@alvh.no-ip. 1027 : 39754 : perminfo = pstate->p_target_nsitem->p_perminfo;
7216 mail@joeconway.com 1028 : 39754 : qry->targetList = NIL;
2623 tgl@sss.pgh.pa.us 1029 [ - + ]: 39754 : Assert(list_length(exprList) <= list_length(icolumns));
1030 [ + + + + : 117909 : forthree(lc, exprList, icols, icolumns, attnos, attrnos)
+ + + + +
+ + + + +
+ - + - +
+ ]
1031 : : {
7153 bruce@momjian.us 1032 : 78155 : Expr *expr = (Expr *) lfirst(lc);
2623 tgl@sss.pgh.pa.us 1033 : 78155 : ResTarget *col = lfirst_node(ResTarget, icols);
1034 : 78155 : AttrNumber attr_num = (AttrNumber) lfirst_int(attnos);
1035 : : TargetEntry *tle;
1036 : :
7216 mail@joeconway.com 1037 : 78155 : tle = makeTargetEntry(expr,
1038 : : attr_num,
1039 : : col->name,
1040 : : false);
1041 : 78155 : qry->targetList = lappend(qry->targetList, tle);
1042 : :
1246 alvherre@alvh.no-ip. 1043 : 78155 : perminfo->insertedCols = bms_add_member(perminfo->insertedCols,
1044 : : attr_num - FirstLowInvalidHeapAttributeNumber);
1045 : : }
1046 : :
1047 : : /*
1048 : : * If we have any clauses yet to process, set the query namespace to
1049 : : * contain only the target relation, removing any entries added in a
1050 : : * sub-SELECT or VALUES list.
1051 : : */
474 dean.a.rasheed@gmail 1052 [ + + + + ]: 39754 : if (stmt->onConflictClause || stmt->returningClause)
1053 : : {
5018 tgl@sss.pgh.pa.us 1054 : 2199 : pstate->p_namespace = NIL;
2315 1055 : 2199 : addNSItemToQuery(pstate, pstate->p_target_nsitem,
1056 : : false, true, true);
1057 : : }
1058 : :
1059 : : /* ON CONFLICT DO SELECT requires a RETURNING clause */
82 dean.a.rasheed@gmail 1060 [ + + ]:GNC 39754 : if (stmt->onConflictClause &&
1061 [ + + ]: 1559 : stmt->onConflictClause->action == ONCONFLICT_SELECT &&
1062 [ + + ]: 240 : !stmt->returningClause)
1063 [ + - ]: 4 : ereport(ERROR,
1064 : : errcode(ERRCODE_SYNTAX_ERROR),
1065 : : errmsg("ON CONFLICT DO SELECT requires a RETURNING clause"),
1066 : : parser_errposition(pstate, stmt->onConflictClause->location));
1067 : :
1068 : : /* Process ON CONFLICT, if any. */
1848 tgl@sss.pgh.pa.us 1069 [ + + ]:CBC 39750 : if (stmt->onConflictClause)
1070 : 1555 : qry->onConflict = transformOnConflictClause(pstate,
1071 : : stmt->onConflictClause);
1072 : :
1073 : : /* Process RETURNING, if any. */
474 dean.a.rasheed@gmail 1074 [ + + ]: 39710 : if (stmt->returningClause)
1075 : 1091 : transformReturningClause(pstate, qry, stmt->returningClause,
1076 : : EXPR_KIND_RETURNING);
1077 : :
1078 : : /* done building the range table and jointree */
9343 tgl@sss.pgh.pa.us 1079 : 39678 : qry->rtable = pstate->p_rtable;
1246 alvherre@alvh.no-ip. 1080 : 39678 : qry->rteperminfos = pstate->p_rteperminfos;
6891 tgl@sss.pgh.pa.us 1081 : 39678 : qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
1082 : :
3521 1083 : 39678 : qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
6891 1084 : 39678 : qry->hasSubLinks = pstate->p_hasSubLinks;
1085 : :
5526 1086 : 39678 : assign_query_collations(pstate, qry);
1087 : :
6891 1088 : 39678 : return qry;
1089 : : }
1090 : :
1091 : : /*
1092 : : * Prepare an INSERT row for assignment to the target table.
1093 : : *
1094 : : * exprlist: transformed expressions for source values; these might come from
1095 : : * a VALUES row, or be Vars referencing a sub-SELECT or VALUES RTE output.
1096 : : * stmtcols: original target-columns spec for INSERT (we just test for NIL)
1097 : : * icolumns: effective target-columns spec (list of ResTarget)
1098 : : * attrnos: integer column numbers (must be same length as icolumns)
1099 : : * strip_indirection: if true, remove any field/array assignment nodes
1100 : : */
1101 : : List *
1102 : 47026 : transformInsertRow(ParseState *pstate, List *exprlist,
1103 : : List *stmtcols, List *icolumns, List *attrnos,
1104 : : bool strip_indirection)
1105 : : {
1106 : : List *result;
1107 : : ListCell *lc;
1108 : : ListCell *icols;
1109 : : ListCell *attnos;
1110 : :
1111 : : /*
1112 : : * Check length of expr list. It must not have more expressions than
1113 : : * there are target columns. We allow fewer, but only if no explicit
1114 : : * columns list was given (the remaining columns are implicitly
1115 : : * defaulted). Note we must check this *after* transformation because
1116 : : * that could expand '*' into multiple items.
1117 : : */
1118 [ + + ]: 47026 : if (list_length(exprlist) > list_length(icolumns))
1119 [ + - ]: 17 : ereport(ERROR,
1120 : : (errcode(ERRCODE_SYNTAX_ERROR),
1121 : : errmsg("INSERT has more expressions than target columns"),
1122 : : parser_errposition(pstate,
1123 : : exprLocation(list_nth(exprlist,
1124 : : list_length(icolumns))))));
1125 [ + + + + ]: 57435 : if (stmtcols != NIL &&
1126 : 10426 : list_length(exprlist) < list_length(icolumns))
1127 : : {
1128 : : /*
1129 : : * We can get here for cases like INSERT ... SELECT (a,b,c) FROM ...
1130 : : * where the user accidentally created a RowExpr instead of separate
1131 : : * columns. Add a suitable hint if that seems to be the problem,
1132 : : * because the main error message is quite misleading for this case.
1133 : : * (If there's no stmtcols, you'll get something about data type
1134 : : * mismatch, which is less misleading so we don't worry about giving a
1135 : : * hint in that case.)
1136 : : */
1137 [ + - - + : 8 : ereport(ERROR,
- - ]
1138 : : (errcode(ERRCODE_SYNTAX_ERROR),
1139 : : errmsg("INSERT has more target columns than expressions"),
1140 : : ((list_length(exprlist) == 1 &&
1141 : : count_rowexpr_columns(pstate, linitial(exprlist)) ==
1142 : : list_length(icolumns)) ?
1143 : : errhint("The insertion source is a row expression containing the same number of columns expected by the INSERT. Did you accidentally use extra parentheses?") : 0),
1144 : : parser_errposition(pstate,
1145 : : exprLocation(list_nth(icolumns,
1146 : : list_length(exprlist))))));
1147 : : }
1148 : :
1149 : : /*
1150 : : * Prepare columns for assignment to target table.
1151 : : */
1152 : 47001 : result = NIL;
2623 1153 [ + + + + : 151416 : forthree(lc, exprlist, icols, icolumns, attnos, attrnos)
+ + + + +
+ + + + +
+ - + - +
+ ]
1154 : : {
6891 1155 : 105241 : Expr *expr = (Expr *) lfirst(lc);
2623 1156 : 105241 : ResTarget *col = lfirst_node(ResTarget, icols);
1157 : 105241 : int attno = lfirst_int(attnos);
1158 : :
6891 1159 : 105241 : expr = transformAssignedExpr(pstate, expr,
1160 : : EXPR_KIND_INSERT_TARGET,
1161 : 105241 : col->name,
1162 : : attno,
1163 : : col->indirection,
1164 : : col->location);
1165 : :
3562 1166 [ + + ]: 104415 : if (strip_indirection)
1167 : : {
1168 : : /*
1169 : : * We need to remove top-level FieldStores and SubscriptingRefs,
1170 : : * as well as any CoerceToDomain appearing above one of those ---
1171 : : * but not a CoerceToDomain that isn't above one of those.
1172 : : */
1173 [ + - ]: 25116 : while (expr)
1174 : : {
782 1175 : 25116 : Expr *subexpr = expr;
1176 : :
1177 [ + + ]: 25292 : while (IsA(subexpr, CoerceToDomain))
1178 : : {
1179 : 176 : subexpr = ((CoerceToDomain *) subexpr)->arg;
1180 : : }
1181 [ + + ]: 25116 : if (IsA(subexpr, FieldStore))
1182 : : {
1183 : 144 : FieldStore *fstore = (FieldStore *) subexpr;
1184 : :
3562 1185 : 144 : expr = (Expr *) linitial(fstore->newvals);
1186 : : }
782 1187 [ + + ]: 24972 : else if (IsA(subexpr, SubscriptingRef))
1188 : : {
1189 : 232 : SubscriptingRef *sbsref = (SubscriptingRef *) subexpr;
1190 : :
2650 alvherre@alvh.no-ip. 1191 [ - + ]: 232 : if (sbsref->refassgnexpr == NULL)
3562 tgl@sss.pgh.pa.us 1192 :UBC 0 : break;
1193 : :
2650 alvherre@alvh.no-ip. 1194 :CBC 232 : expr = sbsref->refassgnexpr;
1195 : : }
1196 : : else
3562 tgl@sss.pgh.pa.us 1197 : 24740 : break;
1198 : : }
1199 : : }
1200 : :
6891 1201 : 104415 : result = lappend(result, expr);
1202 : : }
1203 : :
1204 : 46175 : return result;
1205 : : }
1206 : :
1207 : : /*
1208 : : * transformOnConflictClause -
1209 : : * transforms an OnConflictClause in an INSERT
1210 : : */
1211 : : static OnConflictExpr *
4015 andres@anarazel.de 1212 : 1555 : transformOnConflictClause(ParseState *pstate,
1213 : : OnConflictClause *onConflictClause)
1214 : : {
1848 tgl@sss.pgh.pa.us 1215 : 1555 : ParseNamespaceItem *exclNSItem = NULL;
1216 : : List *arbiterElems;
1217 : : Node *arbiterWhere;
1218 : : Oid arbiterConstraint;
4015 andres@anarazel.de 1219 : 1555 : List *onConflictSet = NIL;
1220 : 1555 : Node *onConflictWhere = NULL;
1221 : 1555 : int exclRelIndex = 0;
1222 : 1555 : List *exclRelTlist = NIL;
1223 : : OnConflictExpr *result;
1224 : :
1225 : : /*
1226 : : * If this is ON CONFLICT DO SELECT/UPDATE, first create the range table
1227 : : * entry for the EXCLUDED pseudo relation, so that that will be present
1228 : : * while processing arbiter expressions. (You can't actually reference it
1229 : : * from there, but this provides a useful error message if you try.)
1230 : : */
82 dean.a.rasheed@gmail 1231 [ + + ]:GNC 1555 : if (onConflictClause->action == ONCONFLICT_UPDATE ||
1232 [ + + ]: 622 : onConflictClause->action == ONCONFLICT_SELECT)
1233 : : {
3867 andres@anarazel.de 1234 :CBC 1169 : Relation targetrel = pstate->p_target_relation;
1235 : : RangeTblEntry *exclRte;
1236 : :
2315 tgl@sss.pgh.pa.us 1237 : 1169 : exclNSItem = addRangeTableEntryForRelation(pstate,
1238 : : targetrel,
1239 : : RowExclusiveLock,
1240 : : makeAlias("excluded", NIL),
1241 : : false, false);
1242 : 1169 : exclRte = exclNSItem->p_rte;
1243 : 1169 : exclRelIndex = exclNSItem->p_rtindex;
1244 : :
1245 : : /*
1246 : : * relkind is set to composite to signal that we're not dealing with
1247 : : * an actual relation, and no permission checks are required on it.
1248 : : * (We'll check the actual target relation, instead.)
1249 : : */
3867 andres@anarazel.de 1250 : 1169 : exclRte->relkind = RELKIND_COMPOSITE_TYPE;
1251 : :
1252 : : /* Create EXCLUDED rel's targetlist for use by EXPLAIN */
2831 tgl@sss.pgh.pa.us 1253 : 1169 : exclRelTlist = BuildOnConflictExcludedTargetlist(targetrel,
1254 : : exclRelIndex);
1255 : : }
1256 : :
1257 : : /* Process the arbiter clause, ON CONFLICT ON (...) */
1848 1258 : 1555 : transformOnConflictArbiter(pstate, onConflictClause, &arbiterElems,
1259 : : &arbiterWhere, &arbiterConstraint);
1260 : :
1261 : : /* Process DO SELECT/UPDATE */
82 dean.a.rasheed@gmail 1262 [ + + ]:GNC 1535 : if (onConflictClause->action == ONCONFLICT_UPDATE ||
1263 [ + + ]: 610 : onConflictClause->action == ONCONFLICT_SELECT)
1264 : : {
1265 : : /*
1266 : : * Add the EXCLUDED pseudo relation to the query namespace, making it
1267 : : * available in SET and WHERE subexpressions.
1268 : : */
2315 tgl@sss.pgh.pa.us 1269 :CBC 1161 : addNSItemToQuery(pstate, exclNSItem, false, true, true);
1270 : :
1271 : : /* Process the UPDATE SET clause */
82 dean.a.rasheed@gmail 1272 [ + + ]:GNC 1161 : if (onConflictClause->action == ONCONFLICT_UPDATE)
1273 : : onConflictSet =
34 peter@eisentraut.org 1274 : 925 : transformUpdateTargetList(pstate, onConflictClause->targetList, NULL);
1275 : :
1276 : : /* Process the SELECT/UPDATE WHERE clause */
4015 andres@anarazel.de 1277 :CBC 1141 : onConflictWhere = transformWhereClause(pstate,
1278 : : onConflictClause->whereClause,
1279 : : EXPR_KIND_WHERE, "WHERE");
1280 : :
1281 : : /*
1282 : : * Remove the EXCLUDED pseudo relation from the query namespace, since
1283 : : * it's not supposed to be available in RETURNING. (Maybe someday we
1284 : : * could allow that, and drop this step.)
1285 : : */
1848 tgl@sss.pgh.pa.us 1286 [ - + ]: 1141 : Assert((ParseNamespaceItem *) llast(pstate->p_namespace) == exclNSItem);
1287 : 1141 : pstate->p_namespace = list_delete_last(pstate->p_namespace);
1288 : : }
1289 : :
1290 : : /* Finally, build ON CONFLICT DO [NOTHING | SELECT | UPDATE] expression */
4015 andres@anarazel.de 1291 : 1515 : result = makeNode(OnConflictExpr);
1292 : :
1293 : 1515 : result->action = onConflictClause->action;
1294 : 1515 : result->arbiterElems = arbiterElems;
1295 : 1515 : result->arbiterWhere = arbiterWhere;
1296 : 1515 : result->constraint = arbiterConstraint;
82 dean.a.rasheed@gmail 1297 :GNC 1515 : result->lockStrength = onConflictClause->lockStrength;
4015 andres@anarazel.de 1298 :CBC 1515 : result->onConflictSet = onConflictSet;
1299 : 1515 : result->onConflictWhere = onConflictWhere;
1300 : 1515 : result->exclRelIndex = exclRelIndex;
1301 : 1515 : result->exclRelTlist = exclRelTlist;
1302 : :
1303 : 1515 : return result;
1304 : : }
1305 : :
1306 : : /*
1307 : : * transformForPortionOfClause
1308 : : *
1309 : : * Transforms a ForPortionOfClause in an UPDATE/DELETE statement.
1310 : : *
1311 : : * - Look up the range/period requested.
1312 : : * - Build a compatible range value from the FROM and TO expressions.
1313 : : * - Build an "overlaps" expression for filtering, used later by the
1314 : : * rewriter.
1315 : : * - For UPDATEs, build an "intersects" expression the rewriter can add
1316 : : * to the targetList to change the temporal bounds.
1317 : : */
1318 : : static ForPortionOfExpr *
34 peter@eisentraut.org 1319 :GNC 981 : transformForPortionOfClause(ParseState *pstate,
1320 : : int rtindex,
1321 : : const ForPortionOfClause *forPortionOf,
1322 : : bool isUpdate)
1323 : : {
1324 : 981 : Relation targetrel = pstate->p_target_relation;
1325 : 981 : int range_attno = InvalidAttrNumber;
1326 : : Form_pg_attribute attr;
1327 : : Oid attbasetype;
1328 : : Oid opclass;
1329 : : Oid opfamily;
1330 : : Oid opcintype;
1331 : 981 : Oid funcid = InvalidOid;
1332 : : StrategyNumber strat;
1333 : : Oid opid;
1334 : : OpExpr *op;
1335 : : ForPortionOfExpr *result;
1336 : : Var *rangeVar;
1337 : :
1338 : : /* We don't support FOR PORTION OF FDW queries. */
1339 [ + + ]: 981 : if (targetrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1340 [ + - ]: 2 : ereport(ERROR,
1341 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1342 : : errmsg("foreign tables don't support FOR PORTION OF")));
1343 : :
1344 : 979 : result = makeNode(ForPortionOfExpr);
1345 : :
1346 : : /* Look up the FOR PORTION OF name requested. */
1347 : 979 : range_attno = attnameAttNum(targetrel, forPortionOf->range_name, false);
1348 [ + + ]: 979 : if (range_attno == InvalidAttrNumber)
1349 [ + - ]: 8 : ereport(ERROR,
1350 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
1351 : : errmsg("column \"%s\" of relation \"%s\" does not exist",
1352 : : forPortionOf->range_name,
1353 : : RelationGetRelationName(targetrel)),
1354 : : parser_errposition(pstate, forPortionOf->location)));
1355 : 971 : attr = TupleDescAttr(targetrel->rd_att, range_attno - 1);
1356 : :
1357 : 971 : attbasetype = getBaseType(attr->atttypid);
1358 : :
1359 : 971 : rangeVar = makeVar(rtindex,
1360 : : range_attno,
1361 : : attr->atttypid,
1362 : : attr->atttypmod,
1363 : : attr->attcollation,
1364 : : 0);
1365 : 971 : rangeVar->location = forPortionOf->location;
1366 : 971 : result->rangeVar = rangeVar;
1367 : :
1368 : : /* Require SELECT privilege on the application-time column. */
1369 : 971 : markVarForSelectPriv(pstate, rangeVar);
1370 : :
1371 : : /*
1372 : : * Use the basetype for the target, which shouldn't be required to follow
1373 : : * domain rules. The table's column type is in the Var if we need it.
1374 : : */
1375 : 971 : result->rangeType = attbasetype;
1376 : 971 : result->isDomain = attbasetype != attr->atttypid;
1377 : :
1378 [ + + ]: 971 : if (forPortionOf->target)
1379 : : {
1380 : 200 : Oid declared_target_type = attbasetype;
1381 : : Oid actual_target_type;
1382 : :
1383 : : /*
1384 : : * We were already given an expression for the target, so we don't
1385 : : * have to build anything. We still have to make sure we got the right
1386 : : * type. NULL will be caught be the executor.
1387 : : */
1388 : :
1389 : 400 : result->targetRange = transformExpr(pstate,
1390 : 200 : forPortionOf->target,
1391 : : EXPR_KIND_FOR_PORTION);
1392 : :
1393 : 200 : actual_target_type = exprType(result->targetRange);
1394 : :
1395 [ + + ]: 200 : if (!can_coerce_type(1, &actual_target_type, &declared_target_type, COERCION_IMPLICIT))
1396 [ + - ]: 32 : ereport(ERROR,
1397 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
1398 : : errmsg("could not coerce FOR PORTION OF target from %s to %s",
1399 : : format_type_be(actual_target_type),
1400 : : format_type_be(declared_target_type)),
1401 : : parser_errposition(pstate, exprLocation(forPortionOf->target))));
1402 : :
1403 : 168 : result->targetRange = coerce_type(pstate,
1404 : : result->targetRange,
1405 : : actual_target_type,
1406 : : declared_target_type,
1407 : : -1,
1408 : : COERCION_IMPLICIT,
1409 : : COERCE_IMPLICIT_CAST,
1410 : 168 : exprLocation(forPortionOf->target));
1411 : :
1412 : : /*
1413 : : * XXX: For now we only support ranges and multiranges, so we fail on
1414 : : * anything else.
1415 : : */
1416 [ + + + + ]: 168 : if (!type_is_range(attbasetype) && !type_is_multirange(attbasetype))
1417 [ + - ]: 24 : ereport(ERROR,
1418 : : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
1419 : : errmsg("column \"%s\" of relation \"%s\" is not a range or multirange type",
1420 : : forPortionOf->range_name,
1421 : : RelationGetRelationName(targetrel)),
1422 : : parser_errposition(pstate, forPortionOf->location)));
1423 : :
1424 : : }
1425 : : else
1426 : : {
1427 : : Oid rngsubtype;
1428 : : Oid declared_arg_types[2];
1429 : : Oid actual_arg_types[2];
1430 : : List *args;
1431 : :
1432 : : /*
1433 : : * Make sure it's a range column. XXX: We could support this syntax on
1434 : : * multirange columns too, if we just built a one-range multirange
1435 : : * from the FROM/TO phrases.
1436 : : */
1437 [ + + ]: 771 : if (!type_is_range(attbasetype))
1438 [ + - ]: 8 : ereport(ERROR,
1439 : : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
1440 : : errmsg("column \"%s\" of relation \"%s\" is not a range type",
1441 : : forPortionOf->range_name,
1442 : : RelationGetRelationName(targetrel)),
1443 : : parser_errposition(pstate, forPortionOf->location)));
1444 : :
1445 : 763 : rngsubtype = get_range_subtype(attbasetype);
1446 : 763 : declared_arg_types[0] = rngsubtype;
1447 : 763 : declared_arg_types[1] = rngsubtype;
1448 : :
1449 : : /*
1450 : : * Build a range from the FROM ... TO ... bounds. This should give a
1451 : : * constant result, so we accept functions like NOW() but not column
1452 : : * references, subqueries, etc.
1453 : : */
1454 : 1510 : result->targetFrom = transformExpr(pstate,
1455 : 763 : forPortionOf->target_start,
1456 : : EXPR_KIND_FOR_PORTION);
1457 : 1494 : result->targetTo = transformExpr(pstate,
1458 : 747 : forPortionOf->target_end,
1459 : : EXPR_KIND_FOR_PORTION);
1460 : 747 : actual_arg_types[0] = exprType(result->targetFrom);
1461 : 747 : actual_arg_types[1] = exprType(result->targetTo);
1462 : 747 : args = list_make2(copyObject(result->targetFrom),
1463 : : copyObject(result->targetTo));
1464 : :
1465 : : /*
1466 : : * Check the bound types separately, for better error message and
1467 : : * location
1468 : : */
1469 [ + + ]: 747 : if (!can_coerce_type(1, actual_arg_types, declared_arg_types, COERCION_IMPLICIT))
1470 [ + - ]: 8 : ereport(ERROR,
1471 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
1472 : : errmsg("could not coerce FOR PORTION OF %s bound from %s to %s",
1473 : : "FROM",
1474 : : format_type_be(actual_arg_types[0]),
1475 : : format_type_be(declared_arg_types[0])),
1476 : : parser_errposition(pstate, exprLocation(forPortionOf->target_start))));
1477 [ + + ]: 739 : if (!can_coerce_type(1, &actual_arg_types[1], &declared_arg_types[1], COERCION_IMPLICIT))
1478 [ + - ]: 8 : ereport(ERROR,
1479 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
1480 : : errmsg("could not coerce FOR PORTION OF %s bound from %s to %s",
1481 : : "TO",
1482 : : format_type_be(actual_arg_types[1]),
1483 : : format_type_be(declared_arg_types[1])),
1484 : : parser_errposition(pstate, exprLocation(forPortionOf->target_end))));
1485 : :
1486 : 731 : make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
1487 : 731 : result->targetRange = (Node *) makeFuncExpr(get_range_constructor2(attbasetype),
1488 : : attbasetype,
1489 : : args,
1490 : : InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL);
1491 : : }
1492 [ + + ]: 875 : if (contain_volatile_functions_after_planning((Expr *) result->targetRange))
1493 [ + - ]: 8 : ereport(ERROR,
1494 : : (errmsg("FOR PORTION OF bounds cannot contain volatile functions")));
1495 : :
1496 : : /*
1497 : : * Build overlapsExpr to use as an extra qual. This means we only hit rows
1498 : : * matching the FROM & TO bounds. We must look up the overlaps operator
1499 : : * (usually "&&").
1500 : : */
1501 : 859 : opclass = GetDefaultOpClass(attr->atttypid, GIST_AM_OID);
1502 [ - + ]: 859 : if (!OidIsValid(opclass))
34 peter@eisentraut.org 1503 [ # # ]:UNC 0 : ereport(ERROR,
1504 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
1505 : : errmsg("data type %s has no default operator class for access method \"%s\"",
1506 : : format_type_be(attr->atttypid), "gist"),
1507 : : errhint("You must define a default operator class for the data type.")));
1508 : :
1509 : : /* Look up the operators and functions we need. */
34 peter@eisentraut.org 1510 :GNC 859 : GetOperatorFromCompareType(opclass, InvalidOid, COMPARE_OVERLAP, &opid, &strat);
1511 : 859 : op = makeNode(OpExpr);
1512 : 859 : op->opno = opid;
1513 : 859 : op->opfuncid = get_opcode(opid);
1514 : 859 : op->opresulttype = BOOLOID;
1515 : 859 : op->args = list_make2(copyObject(rangeVar), copyObject(result->targetRange));
1516 : 859 : result->overlapsExpr = (Node *) op;
1517 : :
1518 : : /*
1519 : : * Look up the without_portion func. This computes the bounds of temporal
1520 : : * leftovers.
1521 : : *
1522 : : * XXX: Find a more extensible way to look up the function, permitting
1523 : : * user-defined types. An opclass support function doesn't make sense,
1524 : : * since there is no index involved. Perhaps a type support function.
1525 : : */
1526 [ + - ]: 859 : if (get_opclass_opfamily_and_input_type(opclass, &opfamily, &opcintype))
1527 [ + + - ]: 859 : switch (opcintype)
1528 : : {
1529 : 771 : case ANYRANGEOID:
1530 : 771 : result->withoutPortionProc = F_RANGE_MINUS_MULTI;
1531 : 771 : break;
1532 : 88 : case ANYMULTIRANGEOID:
1533 : 88 : result->withoutPortionProc = F_MULTIRANGE_MINUS_MULTI;
1534 : 88 : break;
34 peter@eisentraut.org 1535 :UNC 0 : default:
1536 [ # # ]: 0 : elog(ERROR, "unexpected opcintype: %u", opcintype);
1537 : : }
1538 : : else
1539 [ # # ]: 0 : elog(ERROR, "unexpected opclass: %u", opclass);
1540 : :
34 peter@eisentraut.org 1541 [ + + ]:GNC 859 : if (isUpdate)
1542 : : {
1543 : : /*
1544 : : * Now make sure we update the start/end time of the record. For a
1545 : : * range col (r) this is `r = r * targetRange` (where * is the
1546 : : * intersect operator).
1547 : : */
1548 : : Oid intersectoperoid;
1549 : : List *funcArgs;
1550 : : Node *rangeTLEExpr;
1551 : : TargetEntry *tle;
1552 : :
1553 : : /*
1554 : : * Whatever operator is used for intersect by temporal foreign keys,
1555 : : * we can use its backing procedure for intersects in FOR PORTION OF.
1556 : : * XXX: Share code with FindFKPeriodOpers?
1557 : : */
1558 [ + + - ]: 483 : switch (opcintype)
1559 : : {
1560 : 435 : case ANYRANGEOID:
1561 : 435 : intersectoperoid = OID_RANGE_INTERSECT_RANGE_OP;
1562 : 435 : break;
1563 : 48 : case ANYMULTIRANGEOID:
1564 : 48 : intersectoperoid = OID_MULTIRANGE_INTERSECT_MULTIRANGE_OP;
1565 : 48 : break;
34 peter@eisentraut.org 1566 :UNC 0 : default:
1567 [ # # ]: 0 : elog(ERROR, "unexpected opcintype: %u", opcintype);
1568 : : }
34 peter@eisentraut.org 1569 :GNC 483 : funcid = get_opcode(intersectoperoid);
1570 [ - + ]: 483 : if (!OidIsValid(funcid))
34 peter@eisentraut.org 1571 [ # # ]:UNC 0 : ereport(ERROR,
1572 : : errcode(ERRCODE_UNDEFINED_OBJECT),
1573 : : errmsg("could not identify an intersect function for type %s",
1574 : : format_type_be(opcintype)));
1575 : :
34 peter@eisentraut.org 1576 :GNC 483 : funcArgs = list_make2(copyObject(rangeVar),
1577 : : copyObject(result->targetRange));
1578 : 483 : rangeTLEExpr = (Node *) makeFuncExpr(funcid, attbasetype, funcArgs,
1579 : : InvalidOid, InvalidOid,
1580 : : COERCE_EXPLICIT_CALL);
1581 : :
1582 : : /*
1583 : : * Coerce to domain if necessary. If we skip this, we will allow
1584 : : * updating to forbidden values.
1585 : : */
1586 : 483 : rangeTLEExpr = coerce_type(pstate,
1587 : : rangeTLEExpr,
1588 : : attbasetype,
1589 : : attr->atttypid,
1590 : : -1,
1591 : : COERCION_IMPLICIT,
1592 : : COERCE_IMPLICIT_CAST,
1593 : 483 : exprLocation(forPortionOf->target));
1594 : :
1595 : : /* Make a TLE to set the range column */
1596 : 483 : result->rangeTargetList = NIL;
1597 : 483 : tle = makeTargetEntry((Expr *) rangeTLEExpr, range_attno,
1598 : 483 : forPortionOf->range_name, false);
1599 : 483 : result->rangeTargetList = lappend(result->rangeTargetList, tle);
1600 : :
1601 : : /*
1602 : : * The range column will change, but you don't need UPDATE permission
1603 : : * on it, so we don't add to updatedCols here. XXX: If
1604 : : * https://www.postgresql.org/message-id/CACJufxEtY1hdLcx%3DFhnqp-ERcV1PhbvELG5COy_CZjoEW76ZPQ%40mail.gmail.com
1605 : : * is merged (only validate CHECK constraints if they depend on one of
1606 : : * the columns being UPDATEd), we need to make sure that code knows
1607 : : * that we are updating the application-time column.
1608 : : */
1609 : : }
1610 : : else
1611 : 376 : result->rangeTargetList = NIL;
1612 : :
1613 : 859 : result->range_name = forPortionOf->range_name;
1614 : 859 : result->location = forPortionOf->location;
1615 : 859 : result->targetLocation = forPortionOf->target_location;
1616 : :
1617 : 859 : return result;
1618 : : }
1619 : :
1620 : : /*
1621 : : * BuildOnConflictExcludedTargetlist
1622 : : * Create target list for the EXCLUDED pseudo-relation of ON CONFLICT,
1623 : : * representing the columns of targetrel with varno exclRelIndex.
1624 : : *
1625 : : * Note: Exported for use in the rewriter.
1626 : : */
1627 : : List *
2831 tgl@sss.pgh.pa.us 1628 :CBC 1317 : BuildOnConflictExcludedTargetlist(Relation targetrel,
1629 : : Index exclRelIndex)
1630 : : {
1631 : 1317 : List *result = NIL;
1632 : : int attno;
1633 : : Var *var;
1634 : : TargetEntry *te;
1635 : :
1636 : : /*
1637 : : * Note that resnos of the tlist must correspond to attnos of the
1638 : : * underlying relation, hence we need entries for dropped columns too.
1639 : : */
1640 [ + + ]: 4722 : for (attno = 0; attno < RelationGetNumberOfAttributes(targetrel); attno++)
1641 : : {
1642 : 3405 : Form_pg_attribute attr = TupleDescAttr(targetrel->rd_att, attno);
1643 : : char *name;
1644 : :
1645 [ + + ]: 3405 : if (attr->attisdropped)
1646 : : {
1647 : : /*
1648 : : * can't use atttypid here, but it doesn't really matter what type
1649 : : * the Const claims to be.
1650 : : */
1651 : 74 : var = (Var *) makeNullConst(INT4OID, -1, InvalidOid);
2786 1652 : 74 : name = NULL;
1653 : : }
1654 : : else
1655 : : {
2831 1656 : 3331 : var = makeVar(exclRelIndex, attno + 1,
1657 : : attr->atttypid, attr->atttypmod,
1658 : : attr->attcollation,
1659 : : 0);
1660 : 3331 : name = pstrdup(NameStr(attr->attname));
1661 : : }
1662 : :
1663 : 3405 : te = makeTargetEntry((Expr *) var,
1664 : 3405 : attno + 1,
1665 : : name,
1666 : : false);
1667 : :
1668 : 3405 : result = lappend(result, te);
1669 : : }
1670 : :
1671 : : /*
1672 : : * Add a whole-row-Var entry to support references to "EXCLUDED.*". Like
1673 : : * the other entries in the EXCLUDED tlist, its resno must match the Var's
1674 : : * varattno, else the wrong things happen while resolving references in
1675 : : * setrefs.c. This is against normal conventions for targetlists, but
1676 : : * it's okay since we don't use this as a real tlist.
1677 : : */
1678 : 1317 : var = makeVar(exclRelIndex, InvalidAttrNumber,
1679 : 1317 : targetrel->rd_rel->reltype,
1680 : : -1, InvalidOid, 0);
1681 : 1317 : te = makeTargetEntry((Expr *) var, InvalidAttrNumber, NULL, true);
1682 : 1317 : result = lappend(result, te);
1683 : :
1684 : 1317 : return result;
1685 : : }
1686 : :
1687 : :
1688 : : /*
1689 : : * count_rowexpr_columns -
1690 : : * get number of columns contained in a ROW() expression;
1691 : : * return -1 if expression isn't a RowExpr or a Var referencing one.
1692 : : *
1693 : : * This is currently used only for hint purposes, so we aren't terribly
1694 : : * tense about recognizing all possible cases. The Var case is interesting
1695 : : * because that's what we'll get in the INSERT ... SELECT (...) case.
1696 : : */
1697 : : static int
5708 tgl@sss.pgh.pa.us 1698 :UBC 0 : count_rowexpr_columns(ParseState *pstate, Node *expr)
1699 : : {
1700 [ # # ]: 0 : if (expr == NULL)
1701 : 0 : return -1;
1702 [ # # ]: 0 : if (IsA(expr, RowExpr))
1703 : 0 : return list_length(((RowExpr *) expr)->args);
1704 [ # # ]: 0 : if (IsA(expr, Var))
1705 : : {
1706 : 0 : Var *var = (Var *) expr;
1707 : 0 : AttrNumber attnum = var->varattno;
1708 : :
1709 [ # # # # ]: 0 : if (attnum > 0 && var->vartype == RECORDOID)
1710 : : {
1711 : : RangeTblEntry *rte;
1712 : :
1713 : 0 : rte = GetRTEByRangeTablePosn(pstate, var->varno, var->varlevelsup);
1714 [ # # ]: 0 : if (rte->rtekind == RTE_SUBQUERY)
1715 : : {
1716 : : /* Subselect-in-FROM: examine sub-select's output expr */
1717 : 0 : TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,
1718 : : attnum);
1719 : :
1720 [ # # # # ]: 0 : if (ste == NULL || ste->resjunk)
1721 : 0 : return -1;
1722 : 0 : expr = (Node *) ste->expr;
1723 [ # # ]: 0 : if (IsA(expr, RowExpr))
1724 : 0 : return list_length(((RowExpr *) expr)->args);
1725 : : }
1726 : : }
1727 : : }
1728 : 0 : return -1;
1729 : : }
1730 : :
1731 : :
1732 : : /*
1733 : : * transformSelectStmt -
1734 : : * transforms a Select Statement
1735 : : *
1736 : : * This function is also used to transform the source expression of a
1737 : : * PLAssignStmt. In that usage, passthru is non-NULL and we need to
1738 : : * call transformPLAssignStmtTarget after the initial transformation of the
1739 : : * SELECT's targetlist. (We could generalize this into an arbitrary callback
1740 : : * function, but for now that would just be more notation with no benefit.)
1741 : : * All the rest is the same as a regular SelectStmt.
1742 : : *
1743 : : * Note: this covers only cases with no set operations and no VALUES lists;
1744 : : * see below for the other cases.
1745 : : */
1746 : : static Query *
220 tgl@sss.pgh.pa.us 1747 :GNC 310353 : transformSelectStmt(ParseState *pstate, SelectStmt *stmt,
1748 : : SelectStmtPassthrough *passthru)
1749 : : {
10466 bruce@momjian.us 1750 :CBC 310353 : Query *qry = makeNode(Query);
1751 : : Node *qual;
1752 : : ListCell *l;
1753 : :
10467 1754 : 310353 : qry->commandType = CMD_SELECT;
1755 : :
1756 : : /* process the WITH clause independently of all else */
6422 tgl@sss.pgh.pa.us 1757 [ + + ]: 310353 : if (stmt->withClause)
1758 : : {
1759 : 1710 : qry->hasRecursive = stmt->withClause->recursive;
1760 : 1710 : qry->cteList = transformWithClause(pstate, stmt->withClause);
5548 1761 : 1513 : qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
1762 : : }
1763 : :
1764 : : /* Complain if we get called from someplace where INTO is not allowed */
5160 1765 [ + + ]: 310156 : if (stmt->intoClause)
1766 [ + - ]: 12 : ereport(ERROR,
1767 : : (errcode(ERRCODE_SYNTAX_ERROR),
1768 : : errmsg("SELECT ... INTO is not allowed here"),
1769 : : parser_errposition(pstate,
1770 : : exprLocation((Node *) stmt->intoClause))));
1771 : :
1772 : : /* make FOR UPDATE/FOR SHARE info available to addRangeTableEntry */
5926 1773 : 310144 : pstate->p_locking_clause = stmt->lockingClause;
1774 : :
1775 : : /* make WINDOW info available for window functions, too */
1776 : 310144 : pstate->p_windowdefs = stmt->windowClause;
1777 : :
1778 : : /* process the FROM clause */
9211 1779 : 310144 : transformFromClause(pstate, stmt->fromClause);
1780 : :
1781 : : /* transform targetlist */
5016 1782 : 309658 : qry->targetList = transformTargetList(pstate, stmt->targetList,
1783 : : EXPR_KIND_SELECT_TARGET);
1784 : :
1785 : : /*
1786 : : * If we're within a PLAssignStmt, do further transformation of the
1787 : : * targetlist; that has to happen before we consider sorting or grouping.
1788 : : * Otherwise, mark column origins (which are useless in a PLAssignStmt).
1789 : : */
220 tgl@sss.pgh.pa.us 1790 [ + + ]:GNC 306017 : if (passthru)
1791 : 3541 : qry->targetList = transformPLAssignStmtTarget(pstate, qry->targetList,
1792 : : passthru);
1793 : : else
1794 : 302476 : markTargetListOrigins(pstate, qry->targetList);
1795 : :
1796 : : /* transform WHERE */
5016 tgl@sss.pgh.pa.us 1797 :CBC 306010 : qual = transformWhereClause(pstate, stmt->whereClause,
1798 : : EXPR_KIND_WHERE, "WHERE");
1799 : :
1800 : : /* initial processing of HAVING clause is much like WHERE clause */
8342 1801 : 305939 : qry->havingQual = transformWhereClause(pstate, stmt->havingClause,
1802 : : EXPR_KIND_HAVING, "HAVING");
1803 : :
1804 : : /*
1805 : : * Transform sorting/grouping stuff. Do ORDER BY first because both
1806 : : * transformGroupClause and transformDistinctClause need the results. Note
1807 : : * that these functions can also change the targetList, so it's passed to
1808 : : * them by reference.
1809 : : */
10467 bruce@momjian.us 1810 : 305935 : qry->sortClause = transformSortClause(pstate,
1811 : : stmt->sortClause,
1812 : : &qry->targetList,
1813 : : EXPR_KIND_ORDER_BY,
1814 : : false /* allow SQL92 rules */ );
1815 : :
8360 tgl@sss.pgh.pa.us 1816 : 611814 : qry->groupClause = transformGroupClause(pstate,
1817 : : stmt->groupClause,
218 tgl@sss.pgh.pa.us 1818 :GNC 305915 : stmt->groupByAll,
1819 : : &qry->groupingSets,
1820 : : &qry->targetList,
1821 : : qry->sortClause,
1822 : : EXPR_KIND_GROUP_BY,
1823 : : false /* allow SQL92 rules */ );
1874 tomas.vondra@postgre 1824 :CBC 305899 : qry->groupDistinct = stmt->groupDistinct;
218 tgl@sss.pgh.pa.us 1825 :GNC 305899 : qry->groupByAll = stmt->groupByAll;
1826 : :
6485 tgl@sss.pgh.pa.us 1827 [ + + ]:CBC 305899 : if (stmt->distinctClause == NIL)
1828 : : {
1829 : 303390 : qry->distinctClause = NIL;
1830 : 303390 : qry->hasDistinctOn = false;
1831 : : }
1832 [ + + ]: 2509 : else if (linitial(stmt->distinctClause) == NULL)
1833 : : {
1834 : : /* We had SELECT DISTINCT */
1835 : 2343 : qry->distinctClause = transformDistinctClause(pstate,
1836 : : &qry->targetList,
1837 : : qry->sortClause,
1838 : : false);
1839 : 2343 : qry->hasDistinctOn = false;
1840 : : }
1841 : : else
1842 : : {
1843 : : /* We had SELECT DISTINCT ON */
1844 : 166 : qry->distinctClause = transformDistinctOnClause(pstate,
1845 : : stmt->distinctClause,
1846 : : &qry->targetList,
1847 : : qry->sortClause);
1848 : 158 : qry->hasDistinctOn = true;
1849 : : }
1850 : :
1851 : : /* transform LIMIT */
8342 1852 : 305891 : qry->limitOffset = transformLimitClause(pstate, stmt->limitOffset,
1853 : : EXPR_KIND_OFFSET, "OFFSET",
1854 : : stmt->limitOption);
1855 : 305891 : qry->limitCount = transformLimitClause(pstate, stmt->limitCount,
1856 : : EXPR_KIND_LIMIT, "LIMIT",
1857 : : stmt->limitOption);
2219 alvherre@alvh.no-ip. 1858 : 305883 : qry->limitOption = stmt->limitOption;
1859 : :
1860 : : /* transform window clauses after we have seen all window functions */
6337 tgl@sss.pgh.pa.us 1861 : 305883 : qry->windowClause = transformWindowDefinitions(pstate,
1862 : : pstate->p_windowdefs,
1863 : : &qry->targetList);
1864 : :
1865 : : /* resolve any still-unresolved output columns as being type text */
3387 1866 [ + + ]: 305839 : if (pstate->p_resolve_unknowns)
1867 : 278452 : resolveTargetListUnknowns(pstate, qry->targetList);
1868 : :
8509 1869 : 305839 : qry->rtable = pstate->p_rtable;
1246 alvherre@alvh.no-ip. 1870 : 305839 : qry->rteperminfos = pstate->p_rteperminfos;
8509 tgl@sss.pgh.pa.us 1871 : 305839 : qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
1872 : :
9787 1873 : 305839 : qry->hasSubLinks = pstate->p_hasSubLinks;
6337 1874 : 305839 : qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
3521 1875 : 305839 : qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
5200 1876 : 305839 : qry->hasAggs = pstate->p_hasAggs;
1877 : :
7310 1878 [ + + + + : 310814 : foreach(l, stmt->lockingClause)
+ + ]
1879 : : {
6033 1880 : 5003 : transformLockingClause(pstate, qry,
1881 : 5003 : (LockingClause *) lfirst(l), false);
1882 : : }
1883 : :
5526 1884 : 305811 : assign_query_collations(pstate, qry);
1885 : :
1886 : : /* this must be done after collations, for reliable comparison of exprs */
2665 rhodiumtoad@postgres 1887 [ + + + + : 305783 : if (pstate->p_hasAggs || qry->groupClause || qry->groupingSets || qry->havingQual)
+ + + + ]
1888 : 27765 : parseCheckAggregates(pstate, qry);
1889 : :
9343 tgl@sss.pgh.pa.us 1890 : 305707 : return qry;
1891 : : }
1892 : :
1893 : : /*
1894 : : * transformValuesClause -
1895 : : * transforms a VALUES clause that's being used as a standalone SELECT
1896 : : *
1897 : : * We build a Query containing a VALUES RTE, rather as if one had written
1898 : : * SELECT * FROM (VALUES ...) AS "*VALUES*"
1899 : : */
1900 : : static Query *
7216 mail@joeconway.com 1901 : 5787 : transformValuesClause(ParseState *pstate, SelectStmt *stmt)
1902 : : {
1903 : 5787 : Query *qry = makeNode(Query);
1457 tgl@sss.pgh.pa.us 1904 : 5787 : List *exprsLists = NIL;
3435 1905 : 5787 : List *coltypes = NIL;
1906 : 5787 : List *coltypmods = NIL;
1907 : 5787 : List *colcollations = NIL;
6459 1908 : 5787 : List **colexprs = NULL;
7216 mail@joeconway.com 1909 : 5787 : int sublist_length = -1;
5007 tgl@sss.pgh.pa.us 1910 : 5787 : bool lateral = false;
1911 : : ParseNamespaceItem *nsitem;
1912 : : ListCell *lc;
1913 : : ListCell *lc2;
1914 : : int i;
1915 : :
7216 mail@joeconway.com 1916 : 5787 : qry->commandType = CMD_SELECT;
1917 : :
1918 : : /* Most SELECT stuff doesn't apply in a VALUES clause */
1919 [ - + ]: 5787 : Assert(stmt->distinctClause == NIL);
5160 tgl@sss.pgh.pa.us 1920 [ - + ]: 5787 : Assert(stmt->intoClause == NULL);
7216 mail@joeconway.com 1921 [ - + ]: 5787 : Assert(stmt->targetList == NIL);
1922 [ - + ]: 5787 : Assert(stmt->fromClause == NIL);
1923 [ - + ]: 5787 : Assert(stmt->whereClause == NULL);
1924 [ - + ]: 5787 : Assert(stmt->groupClause == NIL);
1925 [ - + ]: 5787 : Assert(stmt->havingClause == NULL);
6337 tgl@sss.pgh.pa.us 1926 [ - + ]: 5787 : Assert(stmt->windowClause == NIL);
7216 mail@joeconway.com 1927 [ - + ]: 5787 : Assert(stmt->op == SETOP_NONE);
1928 : :
1929 : : /* process the WITH clause independently of all else */
6422 tgl@sss.pgh.pa.us 1930 [ + + ]: 5787 : if (stmt->withClause)
1931 : : {
1932 : 40 : qry->hasRecursive = stmt->withClause->recursive;
1933 : 40 : qry->cteList = transformWithClause(pstate, stmt->withClause);
5548 1934 : 36 : qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
1935 : : }
1936 : :
1937 : : /*
1938 : : * For each row of VALUES, transform the raw expressions.
1939 : : *
1940 : : * Note that the intermediate representation we build is column-organized
1941 : : * not row-organized. That simplifies the type and collation processing
1942 : : * below.
1943 : : */
7216 mail@joeconway.com 1944 [ + - + + : 21492 : foreach(lc, stmt->valuesLists)
+ + ]
1945 : : {
7153 bruce@momjian.us 1946 : 15714 : List *sublist = (List *) lfirst(lc);
1947 : :
1948 : : /*
1949 : : * Do basic expression transformation (same as a ROW() expr, but here
1950 : : * we disallow SetToDefault)
1951 : : */
3451 tgl@sss.pgh.pa.us 1952 : 15714 : sublist = transformExpressionList(pstate, sublist,
1953 : : EXPR_KIND_VALUES, false);
1954 : :
1955 : : /*
1956 : : * All the sublists must be the same length, *after* transformation
1957 : : * (which might expand '*' into multiple items). The VALUES RTE can't
1958 : : * handle anything different.
1959 : : */
7216 mail@joeconway.com 1960 [ + + ]: 15709 : if (sublist_length < 0)
1961 : : {
1962 : : /* Remember post-transformation length of first sublist */
1963 : 5778 : sublist_length = list_length(sublist);
1964 : : /* and allocate array for per-column lists */
6459 tgl@sss.pgh.pa.us 1965 : 5778 : colexprs = (List **) palloc0(sublist_length * sizeof(List *));
1966 : : }
7216 mail@joeconway.com 1967 [ - + ]: 9931 : else if (sublist_length != list_length(sublist))
1968 : : {
7216 mail@joeconway.com 1969 [ # # ]:UBC 0 : ereport(ERROR,
1970 : : (errcode(ERRCODE_SYNTAX_ERROR),
1971 : : errmsg("VALUES lists must all be the same length"),
1972 : : parser_errposition(pstate,
1973 : : exprLocation((Node *) sublist))));
1974 : : }
1975 : :
1976 : : /* Build per-column expression lists */
7216 mail@joeconway.com 1977 :CBC 15709 : i = 0;
1978 [ + + + + : 37406 : foreach(lc2, sublist)
+ + ]
1979 : : {
7153 bruce@momjian.us 1980 : 21697 : Node *col = (Node *) lfirst(lc2);
1981 : :
6459 tgl@sss.pgh.pa.us 1982 : 21697 : colexprs[i] = lappend(colexprs[i], col);
7216 mail@joeconway.com 1983 : 21697 : i++;
1984 : : }
1985 : :
1986 : : /* Release sub-list's cells to save memory */
5496 tgl@sss.pgh.pa.us 1987 : 15709 : list_free(sublist);
1988 : :
1989 : : /* Prepare an exprsLists element for this row */
1457 1990 : 15709 : exprsLists = lappend(exprsLists, NIL);
1991 : : }
1992 : :
1993 : : /*
1994 : : * Now resolve the common types of the columns, and coerce everything to
1995 : : * those types. Then identify the common typmod and common collation, if
1996 : : * any, of each column.
1997 : : *
1998 : : * We must do collation processing now because (1) assign_query_collations
1999 : : * doesn't process rangetable entries, and (2) we need to label the VALUES
2000 : : * RTE with column collations for use in the outer query. We don't
2001 : : * consider conflict of implicit collations to be an error here; instead
2002 : : * the column will just show InvalidOid as its collation, and you'll get a
2003 : : * failure later if that results in failure to resolve a collation.
2004 : : *
2005 : : * Note we modify the per-column expression lists in-place.
2006 : : */
7216 mail@joeconway.com 2007 [ + + ]: 13289 : for (i = 0; i < sublist_length; i++)
2008 : : {
2009 : : Oid coltype;
2010 : : int32 coltypmod;
2011 : : Oid colcoll;
2012 : :
5496 tgl@sss.pgh.pa.us 2013 : 7511 : coltype = select_common_type(pstate, colexprs[i], "VALUES", NULL);
2014 : :
2015 [ + - + + : 29208 : foreach(lc, colexprs[i])
+ + ]
2016 : : {
2017 : 21697 : Node *col = (Node *) lfirst(lc);
2018 : :
2019 : 21697 : col = coerce_to_common_type(pstate, col, coltype, "VALUES");
523 peter@eisentraut.org 2020 : 21697 : lfirst(lc) = col;
2021 : : }
2022 : :
2016 2023 : 7511 : coltypmod = select_common_typmod(pstate, colexprs[i], coltype);
5496 tgl@sss.pgh.pa.us 2024 : 7511 : colcoll = select_common_collation(pstate, colexprs[i], true);
2025 : :
3435 2026 : 7511 : coltypes = lappend_oid(coltypes, coltype);
2027 : 7511 : coltypmods = lappend_int(coltypmods, coltypmod);
2028 : 7511 : colcollations = lappend_oid(colcollations, colcoll);
2029 : : }
2030 : :
2031 : : /*
2032 : : * Finally, rearrange the coerced expressions into row-organized lists.
2033 : : */
1457 2034 [ + + ]: 13289 : for (i = 0; i < sublist_length; i++)
2035 : : {
5496 2036 [ + - + + : 29208 : forboth(lc, colexprs[i], lc2, exprsLists)
+ - + + +
+ + - +
+ ]
2037 : : {
2038 : 21697 : Node *col = (Node *) lfirst(lc);
2039 : 21697 : List *sublist = lfirst(lc2);
2040 : :
2026 peter@eisentraut.org 2041 : 21697 : sublist = lappend(sublist, col);
1457 tgl@sss.pgh.pa.us 2042 : 21697 : lfirst(lc2) = sublist;
2043 : : }
5496 2044 : 7511 : list_free(colexprs[i]);
2045 : : }
2046 : :
2047 : : /*
2048 : : * Ordinarily there can't be any current-level Vars in the expression
2049 : : * lists, because the namespace was empty ... but if we're inside CREATE
2050 : : * RULE, then NEW/OLD references might appear. In that case we have to
2051 : : * mark the VALUES RTE as LATERAL.
2052 : : */
5007 2053 [ + + + - ]: 5783 : if (pstate->p_rtable != NIL &&
2054 : 5 : contain_vars_of_level((Node *) exprsLists, 0))
2055 : 5 : lateral = true;
2056 : :
2057 : : /*
2058 : : * Generate the VALUES RTE
2059 : : */
2315 2060 : 5778 : nsitem = addRangeTableEntryForValues(pstate, exprsLists,
2061 : : coltypes, coltypmods, colcollations,
2062 : : NULL, lateral, true);
2063 : 5778 : addNSItemToQuery(pstate, nsitem, true, true, true);
2064 : :
2065 : : /*
2066 : : * Generate a targetlist as though expanding "*"
2067 : : */
7216 mail@joeconway.com 2068 [ - + ]: 5778 : Assert(pstate->p_next_resno == 1);
1499 alvherre@alvh.no-ip. 2069 : 5778 : qry->targetList = expandNSItemAttrs(pstate, nsitem, 0, true, -1);
2070 : :
2071 : : /*
2072 : : * The grammar allows attaching ORDER BY, LIMIT, and FOR UPDATE to a
2073 : : * VALUES, so cope.
2074 : : */
7216 mail@joeconway.com 2075 : 5778 : qry->sortClause = transformSortClause(pstate,
2076 : : stmt->sortClause,
2077 : : &qry->targetList,
2078 : : EXPR_KIND_ORDER_BY,
2079 : : false /* allow SQL92 rules */ );
2080 : :
2081 : 5778 : qry->limitOffset = transformLimitClause(pstate, stmt->limitOffset,
2082 : : EXPR_KIND_OFFSET, "OFFSET",
2083 : : stmt->limitOption);
2084 : 5778 : qry->limitCount = transformLimitClause(pstate, stmt->limitCount,
2085 : : EXPR_KIND_LIMIT, "LIMIT",
2086 : : stmt->limitOption);
2219 alvherre@alvh.no-ip. 2087 : 5778 : qry->limitOption = stmt->limitOption;
2088 : :
7216 mail@joeconway.com 2089 [ - + ]: 5778 : if (stmt->lockingClause)
7216 mail@joeconway.com 2090 [ # # ]:UBC 0 : ereport(ERROR,
2091 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2092 : : /*------
2093 : : translator: %s is a SQL row locking clause such as FOR UPDATE */
2094 : : errmsg("%s cannot be applied to VALUES",
2095 : : LCS_asString(((LockingClause *)
2096 : : linitial(stmt->lockingClause))->strength))));
2097 : :
7216 mail@joeconway.com 2098 :CBC 5778 : qry->rtable = pstate->p_rtable;
1246 alvherre@alvh.no-ip. 2099 : 5778 : qry->rteperminfos = pstate->p_rteperminfos;
7216 mail@joeconway.com 2100 : 5778 : qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
2101 : :
2102 : 5778 : qry->hasSubLinks = pstate->p_hasSubLinks;
2103 : :
5526 tgl@sss.pgh.pa.us 2104 : 5778 : assign_query_collations(pstate, qry);
2105 : :
7216 mail@joeconway.com 2106 : 5778 : return qry;
2107 : : }
2108 : :
2109 : : /*
2110 : : * transformSetOperationStmt -
2111 : : * transforms a set-operations tree
2112 : : *
2113 : : * A set-operation tree is just a SELECT, but with UNION/INTERSECT/EXCEPT
2114 : : * structure to it. We must transform each leaf SELECT and build up a top-
2115 : : * level Query that contains the leaf SELECTs as subqueries in its rangetable.
2116 : : * The tree of set operations is converted into the setOperations field of
2117 : : * the top-level Query.
2118 : : */
2119 : : static Query *
8958 bruce@momjian.us 2120 : 8597 : transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
2121 : : {
9343 tgl@sss.pgh.pa.us 2122 : 8597 : Query *qry = makeNode(Query);
2123 : : SelectStmt *leftmostSelect;
2124 : : int leftmostRTI;
2125 : : Query *leftmostQuery;
2126 : : SetOperationStmt *sostmt;
2127 : : List *sortClause;
2128 : : Node *limitOffset;
2129 : : Node *limitCount;
2130 : : List *lockingClause;
2131 : : WithClause *withClause;
2132 : : Node *node;
2133 : : ListCell *left_tlist,
2134 : : *lct,
2135 : : *lcm,
2136 : : *lcc,
2137 : : *l;
2138 : : List *targetvars,
2139 : : *targetnames,
2140 : : *sv_namespace;
2141 : : int sv_rtable_length;
2142 : : ParseNamespaceItem *jnsitem;
2143 : : ParseNamespaceColumn *sortnscolumns;
2144 : : int sortcolindex;
2145 : : int tllen;
2146 : :
2147 : 8597 : qry->commandType = CMD_SELECT;
2148 : :
2149 : : /*
2150 : : * Find leftmost leaf SelectStmt. We currently only need to do this in
2151 : : * order to deliver a suitable error message if there's an INTO clause
2152 : : * there, implying the set-op tree is in a context that doesn't allow
2153 : : * INTO. (transformSetOperationTree would throw error anyway, but it
2154 : : * seems worth the trouble to throw a different error for non-leftmost
2155 : : * INTO, so we produce that error in transformSetOperationTree.)
2156 : : */
9312 2157 : 8597 : leftmostSelect = stmt->larg;
2158 [ + - + + ]: 13028 : while (leftmostSelect && leftmostSelect->op != SETOP_NONE)
2159 : 4431 : leftmostSelect = leftmostSelect->larg;
2160 [ + - + - : 8597 : Assert(leftmostSelect && IsA(leftmostSelect, SelectStmt) &&
- + ]
2161 : : leftmostSelect->larg == NULL);
5160 2162 [ - + ]: 8597 : if (leftmostSelect->intoClause)
5160 tgl@sss.pgh.pa.us 2163 [ # # ]:UBC 0 : ereport(ERROR,
2164 : : (errcode(ERRCODE_SYNTAX_ERROR),
2165 : : errmsg("SELECT ... INTO is not allowed here"),
2166 : : parser_errposition(pstate,
2167 : : exprLocation((Node *) leftmostSelect->intoClause))));
2168 : :
2169 : : /*
2170 : : * We need to extract ORDER BY and other top-level clauses here and not
2171 : : * let transformSetOperationTree() see them --- else it'll just recurse
2172 : : * right back here!
2173 : : */
9312 tgl@sss.pgh.pa.us 2174 :CBC 8597 : sortClause = stmt->sortClause;
2175 : 8597 : limitOffset = stmt->limitOffset;
2176 : 8597 : limitCount = stmt->limitCount;
7582 2177 : 8597 : lockingClause = stmt->lockingClause;
5026 2178 : 8597 : withClause = stmt->withClause;
2179 : :
9312 2180 : 8597 : stmt->sortClause = NIL;
2181 : 8597 : stmt->limitOffset = NULL;
2182 : 8597 : stmt->limitCount = NULL;
7310 2183 : 8597 : stmt->lockingClause = NIL;
5026 2184 : 8597 : stmt->withClause = NULL;
2185 : :
2186 : : /* We don't support FOR UPDATE/SHARE with set ops at the moment. */
7582 2187 [ + + ]: 8597 : if (lockingClause)
8326 2188 [ + - ]: 4 : ereport(ERROR,
2189 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2190 : : /*------
2191 : : translator: %s is a SQL row locking clause such as FOR UPDATE */
2192 : : errmsg("%s is not allowed with UNION/INTERSECT/EXCEPT",
2193 : : LCS_asString(((LockingClause *)
2194 : : linitial(lockingClause))->strength))));
2195 : :
2196 : : /* Process the WITH clause independently of all else */
5026 2197 [ + + ]: 8593 : if (withClause)
2198 : : {
2199 : 170 : qry->hasRecursive = withClause->recursive;
2200 : 170 : qry->cteList = transformWithClause(pstate, withClause);
2201 : 170 : qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
2202 : : }
2203 : :
2204 : : /*
2205 : : * Recursively transform the components of the tree.
2206 : : */
3360 peter_e@gmx.net 2207 : 8593 : sostmt = castNode(SetOperationStmt,
2208 : : transformSetOperationTree(pstate, stmt, true, NULL));
2209 [ - + ]: 8545 : Assert(sostmt);
9312 tgl@sss.pgh.pa.us 2210 : 8545 : qry->setOperations = (Node *) sostmt;
2211 : :
2212 : : /*
2213 : : * Re-find leftmost SELECT (now it's a sub-query in rangetable)
2214 : : */
2215 : 8545 : node = sostmt->larg;
9343 2216 [ + - + + ]: 12964 : while (node && IsA(node, SetOperationStmt))
2217 : 4419 : node = ((SetOperationStmt *) node)->larg;
2218 [ + - - + ]: 8545 : Assert(node && IsA(node, RangeTblRef));
9312 2219 : 8545 : leftmostRTI = ((RangeTblRef *) node)->rtindex;
2220 : 8545 : leftmostQuery = rt_fetch(leftmostRTI, pstate->p_rtable)->subquery;
9343 2221 [ - + ]: 8545 : Assert(leftmostQuery != NULL);
2222 : :
2223 : : /*
2224 : : * Generate dummy targetlist for outer query using column names of
2225 : : * leftmost select and common datatypes/collations of topmost set
2226 : : * operation. Also make lists of the dummy vars and their names for use
2227 : : * in parsing ORDER BY.
2228 : : *
2229 : : * Note: we use leftmostRTI as the varno of the dummy variables. It
2230 : : * shouldn't matter too much which RT index they have, as long as they
2231 : : * have one that corresponds to a real RT entry; else funny things may
2232 : : * happen when the tree is mashed by rule rewriting.
2233 : : */
2234 : 8545 : qry->targetList = NIL;
8773 2235 : 8545 : targetvars = NIL;
9210 2236 : 8545 : targetnames = NIL;
2237 : : sortnscolumns = (ParseNamespaceColumn *)
2315 2238 : 8545 : palloc0(list_length(sostmt->colTypes) * sizeof(ParseNamespaceColumn));
2239 : 8545 : sortcolindex = 0;
2240 : :
2623 2241 [ + + + + : 29488 : forfour(lct, sostmt->colTypes,
+ + + + +
+ + + + +
+ + + + +
- + - + -
+ + ]
2242 : : lcm, sostmt->colTypmods,
2243 : : lcc, sostmt->colCollations,
2244 : : left_tlist, leftmostQuery->targetList)
2245 : : {
7208 2246 : 20943 : Oid colType = lfirst_oid(lct);
2247 : 20943 : int32 colTypmod = lfirst_int(lcm);
5565 peter_e@gmx.net 2248 : 20943 : Oid colCollation = lfirst_oid(lcc);
7699 tgl@sss.pgh.pa.us 2249 : 20943 : TargetEntry *lefttle = (TargetEntry *) lfirst(left_tlist);
2250 : : char *colName;
2251 : : TargetEntry *tle;
2252 : : Var *var;
2253 : :
2254 [ - + ]: 20943 : Assert(!lefttle->resjunk);
2255 : 20943 : colName = pstrdup(lefttle->resname);
6422 2256 : 20943 : var = makeVar(leftmostRTI,
2257 : 20943 : lefttle->resno,
2258 : : colType,
2259 : : colTypmod,
2260 : : colCollation,
2261 : : 0);
2262 : 20943 : var->location = exprLocation((Node *) lefttle->expr);
2263 : 20943 : tle = makeTargetEntry((Expr *) var,
7699 2264 : 20943 : (AttrNumber) pstate->p_next_resno++,
2265 : : colName,
2266 : : false);
2267 : 20943 : qry->targetList = lappend(qry->targetList, tle);
6422 2268 : 20943 : targetvars = lappend(targetvars, var);
9210 2269 : 20943 : targetnames = lappend(targetnames, makeString(colName));
2315 2270 : 20943 : sortnscolumns[sortcolindex].p_varno = leftmostRTI;
2271 : 20943 : sortnscolumns[sortcolindex].p_varattno = lefttle->resno;
2272 : 20943 : sortnscolumns[sortcolindex].p_vartype = colType;
2273 : 20943 : sortnscolumns[sortcolindex].p_vartypmod = colTypmod;
2274 : 20943 : sortnscolumns[sortcolindex].p_varcollid = colCollation;
2275 : 20943 : sortnscolumns[sortcolindex].p_varnosyn = leftmostRTI;
2276 : 20943 : sortnscolumns[sortcolindex].p_varattnosyn = lefttle->resno;
2277 : 20943 : sortcolindex++;
2278 : : }
2279 : :
2280 : : /*
2281 : : * As a first step towards supporting sort clauses that are expressions
2282 : : * using the output columns, generate a namespace entry that makes the
2283 : : * output columns visible. A Join RTE node is handy for this, since we
2284 : : * can easily control the Vars generated upon matches.
2285 : : *
2286 : : * Note: we don't yet do anything useful with such cases, but at least
2287 : : * "ORDER BY upper(foo)" will draw the right error message rather than
2288 : : * "foo not found".
2289 : : */
6312 2290 : 8545 : sv_rtable_length = list_length(pstate->p_rtable);
2291 : :
2315 2292 : 8545 : jnsitem = addRangeTableEntryForJoin(pstate,
2293 : : targetnames,
2294 : : sortnscolumns,
2295 : : JOIN_INNER,
2296 : : 0,
2297 : : targetvars,
2298 : : NIL,
2299 : : NIL,
2300 : : NULL,
2301 : : NULL,
2302 : : false);
2303 : :
5018 2304 : 8545 : sv_namespace = pstate->p_namespace;
2305 : 8545 : pstate->p_namespace = NIL;
2306 : :
2307 : : /* add jnsitem to column namespace only */
2315 2308 : 8545 : addNSItemToQuery(pstate, jnsitem, false, false, true);
2309 : :
2310 : : /*
2311 : : * For now, we don't support resjunk sort clauses on the output of a
2312 : : * setOperation tree --- you can only use the SQL92-spec options of
2313 : : * selecting an output column by name or number. Enforce by checking that
2314 : : * transformSortClause doesn't add any items to tlist. Note, if changing
2315 : : * this, add_setop_child_rel_equivalences() will need to be updated.
2316 : : */
8010 neilc@samurai.com 2317 : 8545 : tllen = list_length(qry->targetList);
2318 : :
9343 tgl@sss.pgh.pa.us 2319 : 8545 : qry->sortClause = transformSortClause(pstate,
2320 : : sortClause,
2321 : : &qry->targetList,
2322 : : EXPR_KIND_ORDER_BY,
2323 : : false /* allow SQL92 rules */ );
2324 : :
2325 : : /* restore namespace, remove join RTE from rtable */
5018 2326 : 8541 : pstate->p_namespace = sv_namespace;
6312 2327 : 8541 : pstate->p_rtable = list_truncate(pstate->p_rtable, sv_rtable_length);
2328 : :
8010 neilc@samurai.com 2329 [ - + ]: 8541 : if (tllen != list_length(qry->targetList))
8326 tgl@sss.pgh.pa.us 2330 [ # # ]:UBC 0 : ereport(ERROR,
2331 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2332 : : errmsg("invalid UNION/INTERSECT/EXCEPT ORDER BY clause"),
2333 : : errdetail("Only result column names can be used, not expressions or functions."),
2334 : : errhint("Add the expression/function to every SELECT, or move the UNION into a FROM clause."),
2335 : : parser_errposition(pstate,
2336 : : exprLocation(list_nth(qry->targetList, tllen)))));
2337 : :
8342 tgl@sss.pgh.pa.us 2338 :CBC 8541 : qry->limitOffset = transformLimitClause(pstate, limitOffset,
2339 : : EXPR_KIND_OFFSET, "OFFSET",
2340 : : stmt->limitOption);
2341 : 8541 : qry->limitCount = transformLimitClause(pstate, limitCount,
2342 : : EXPR_KIND_LIMIT, "LIMIT",
2343 : : stmt->limitOption);
2219 alvherre@alvh.no-ip. 2344 : 8541 : qry->limitOption = stmt->limitOption;
2345 : :
8509 tgl@sss.pgh.pa.us 2346 : 8541 : qry->rtable = pstate->p_rtable;
1246 alvherre@alvh.no-ip. 2347 : 8541 : qry->rteperminfos = pstate->p_rteperminfos;
8509 tgl@sss.pgh.pa.us 2348 : 8541 : qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
2349 : :
9343 2350 : 8541 : qry->hasSubLinks = pstate->p_hasSubLinks;
6337 2351 : 8541 : qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
3521 2352 : 8541 : qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
5200 2353 : 8541 : qry->hasAggs = pstate->p_hasAggs;
2354 : :
7310 2355 [ - + - - : 8541 : foreach(l, lockingClause)
- + ]
2356 : : {
6033 tgl@sss.pgh.pa.us 2357 :UBC 0 : transformLockingClause(pstate, qry,
2358 : 0 : (LockingClause *) lfirst(l), false);
2359 : : }
2360 : :
5526 tgl@sss.pgh.pa.us 2361 :CBC 8541 : assign_query_collations(pstate, qry);
2362 : :
2363 : : /* this must be done after collations, for reliable comparison of exprs */
2665 rhodiumtoad@postgres 2364 [ + - + - : 8541 : if (pstate->p_hasAggs || qry->groupClause || qry->groupingSets || qry->havingQual)
+ - - + ]
2665 rhodiumtoad@postgres 2365 :UBC 0 : parseCheckAggregates(pstate, qry);
2366 : :
9343 tgl@sss.pgh.pa.us 2367 :CBC 8541 : return qry;
2368 : : }
2369 : :
2370 : : /*
2371 : : * Make a SortGroupClause node for a SetOperationStmt's groupClauses
2372 : : *
2373 : : * If require_hash is true, the caller is indicating that they need hash
2374 : : * support or they will fail. So look extra hard for hash support.
2375 : : */
2376 : : SortGroupClause *
1700 peter@eisentraut.org 2377 : 17582 : makeSortGroupClauseForSetOp(Oid rescoltype, bool require_hash)
2378 : : {
1919 2379 : 17582 : SortGroupClause *grpcl = makeNode(SortGroupClause);
2380 : : Oid sortop;
2381 : : Oid eqop;
2382 : : bool hashable;
2383 : :
2384 : : /* determine the eqop and optional sortop */
2385 : 17582 : get_sort_group_operators(rescoltype,
2386 : : false, true, false,
2387 : : &sortop, &eqop, NULL,
2388 : : &hashable);
2389 : :
2390 : : /*
2391 : : * The type cache doesn't believe that record is hashable (see
2392 : : * cache_record_field_properties()), but if the caller really needs hash
2393 : : * support, we can assume it does. Worst case, if any components of the
2394 : : * record don't support hashing, we will fail at execution.
2395 : : */
1700 2396 [ + + + + : 17582 : if (require_hash && (rescoltype == RECORDOID || rescoltype == RECORDARRAYOID))
+ + ]
2397 : 16 : hashable = true;
2398 : :
2399 : : /* we don't have a tlist yet, so can't assign sortgrouprefs */
1919 2400 : 17582 : grpcl->tleSortGroupRef = 0;
2401 : 17582 : grpcl->eqop = eqop;
2402 : 17582 : grpcl->sortop = sortop;
568 2403 : 17582 : grpcl->reverse_sort = false; /* Sort-op is "less than", or InvalidOid */
1919 2404 : 17582 : grpcl->nulls_first = false; /* OK with or without sortop */
2405 : 17582 : grpcl->hashable = hashable;
2406 : :
2407 : 17582 : return grpcl;
2408 : : }
2409 : :
2410 : : /*
2411 : : * transformSetOperationTree
2412 : : * Recursively transform leaves and internal nodes of a set-op tree
2413 : : *
2414 : : * In addition to returning the transformed node, if targetlist isn't NULL
2415 : : * then we return a list of its non-resjunk TargetEntry nodes. For a leaf
2416 : : * set-op node these are the actual targetlist entries; otherwise they are
2417 : : * dummy entries created to carry the type, typmod, collation, and location
2418 : : * (for error messages) of each output column of the set-op node. This info
2419 : : * is needed only during the internal recursion of this function, so outside
2420 : : * callers pass NULL for targetlist. Note: the reason for passing the
2421 : : * actual targetlist entries of a leaf node is so that upper levels can
2422 : : * replace UNKNOWN Consts with properly-coerced constants.
2423 : : */
2424 : : static Node *
6459 tgl@sss.pgh.pa.us 2425 : 34717 : transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
2426 : : bool isTopLevel, List **targetlist)
2427 : : {
2428 : : bool isLeaf;
2429 : :
9312 2430 [ + - - + ]: 34717 : Assert(stmt && IsA(stmt, SelectStmt));
2431 : :
2432 : : /* Guard against stack overflow due to overly complex set-expressions */
4923 2433 : 34717 : check_stack_depth();
2434 : :
2435 : : /*
2436 : : * Validity-check both leaf and internal SELECTs for disallowed ops.
2437 : : */
6948 2438 [ - + ]: 34717 : if (stmt->intoClause)
8326 tgl@sss.pgh.pa.us 2439 [ # # ]:UBC 0 : ereport(ERROR,
2440 : : (errcode(ERRCODE_SYNTAX_ERROR),
2441 : : errmsg("INTO is only allowed on first SELECT of UNION/INTERSECT/EXCEPT"),
2442 : : parser_errposition(pstate,
2443 : : exprLocation((Node *) stmt->intoClause))));
2444 : :
2445 : : /* We don't support FOR UPDATE/SHARE with set ops at the moment. */
7582 tgl@sss.pgh.pa.us 2446 [ - + ]:CBC 34717 : if (stmt->lockingClause)
8326 tgl@sss.pgh.pa.us 2447 [ # # ]:UBC 0 : ereport(ERROR,
2448 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2449 : : /*------
2450 : : translator: %s is a SQL row locking clause such as FOR UPDATE */
2451 : : errmsg("%s is not allowed with UNION/INTERSECT/EXCEPT",
2452 : : LCS_asString(((LockingClause *)
2453 : : linitial(stmt->lockingClause))->strength))));
2454 : :
2455 : : /*
2456 : : * If an internal node of a set-op tree has ORDER BY, LIMIT, FOR UPDATE,
2457 : : * or WITH clauses attached, we need to treat it like a leaf node to
2458 : : * generate an independent sub-Query tree. Otherwise, it can be
2459 : : * represented by a SetOperationStmt node underneath the parent Query.
2460 : : */
9312 tgl@sss.pgh.pa.us 2461 [ + + ]:CBC 34717 : if (stmt->op == SETOP_NONE)
2462 : : {
2463 [ + - - + ]: 21613 : Assert(stmt->larg == NULL && stmt->rarg == NULL);
2464 : 21613 : isLeaf = true;
2465 : : }
2466 : : else
2467 : : {
2468 [ + - - + ]: 13104 : Assert(stmt->larg != NULL && stmt->rarg != NULL);
2469 [ + + + - : 13104 : if (stmt->sortClause || stmt->limitOffset || stmt->limitCount ||
+ - ]
5026 2470 [ + - + + ]: 13088 : stmt->lockingClause || stmt->withClause)
9312 2471 : 40 : isLeaf = true;
2472 : : else
2473 : 13064 : isLeaf = false;
2474 : : }
2475 : :
2476 [ + + ]: 34717 : if (isLeaf)
2477 : : {
2478 : : /* Process leaf SELECT */
2479 : : Query *selectQuery;
2480 : : ParseNamespaceItem *nsitem;
2481 : : RangeTblRef *rtr;
2482 : :
2483 : : /*
2484 : : * Transform SelectStmt into a Query.
2485 : : *
2486 : : * This works the same as SELECT transformation normally would, except
2487 : : * that we prevent resolving unknown-type outputs as TEXT. This does
2488 : : * not change the subquery's semantics since if the column type
2489 : : * matters semantically, it would have been resolved to something else
2490 : : * anyway. Doing this lets us resolve such outputs using
2491 : : * select_common_type(), below.
2492 : : *
2493 : : * Note: previously transformed sub-queries don't affect the parsing
2494 : : * of this sub-query, because they are not in the toplevel pstate's
2495 : : * namespace list.
2496 : : */
3387 2497 : 21653 : selectQuery = parse_sub_analyze((Node *) stmt, pstate,
2498 : : NULL, false, false);
2499 : :
2500 : : /*
2501 : : * Check for bogus references to Vars on the current query level (but
2502 : : * upper-level references are okay). Normally this can't happen
2503 : : * because the namespace will be empty, but it could happen if we are
2504 : : * inside a rule.
2505 : : */
5018 2506 [ - + ]: 21633 : if (pstate->p_namespace)
2507 : : {
8482 tgl@sss.pgh.pa.us 2508 [ # # ]:UBC 0 : if (contain_vars_of_level((Node *) selectQuery, 1))
8326 2509 [ # # ]: 0 : ereport(ERROR,
2510 : : (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
2511 : : errmsg("UNION/INTERSECT/EXCEPT member statement cannot refer to other relations of same query level"),
2512 : : parser_errposition(pstate,
2513 : : locate_var_of_level((Node *) selectQuery, 1))));
2514 : : }
2515 : :
2516 : : /*
2517 : : * Extract a list of the non-junk TLEs for upper-level processing.
2518 : : */
5530 tgl@sss.pgh.pa.us 2519 [ + - ]:CBC 21633 : if (targetlist)
2520 : : {
2521 : : ListCell *tl;
2522 : :
2523 : 21633 : *targetlist = NIL;
2524 [ + + + + : 82952 : foreach(tl, selectQuery->targetList)
+ + ]
2525 : : {
2526 : 61319 : TargetEntry *tle = (TargetEntry *) lfirst(tl);
2527 : :
2528 [ + + ]: 61319 : if (!tle->resjunk)
2529 : 61311 : *targetlist = lappend(*targetlist, tle);
2530 : : }
2531 : : }
2532 : :
2533 : : /*
2534 : : * Make the leaf query be a subquery in the top-level rangetable.
2535 : : */
2315 2536 : 21633 : nsitem = addRangeTableEntryForSubquery(pstate,
2537 : : selectQuery,
2538 : : NULL,
2539 : : false,
2540 : : false);
2541 : :
2542 : : /*
2543 : : * Return a RangeTblRef to replace the SelectStmt in the set-op tree.
2544 : : */
9343 2545 : 21633 : rtr = makeNode(RangeTblRef);
2315 2546 : 21633 : rtr->rtindex = nsitem->p_rtindex;
9343 2547 : 21633 : return (Node *) rtr;
2548 : : }
2549 : : else
2550 : : {
2551 : : /* Process an internal node (set operation node) */
9312 2552 : 13064 : SetOperationStmt *op = makeNode(SetOperationStmt);
2553 : : List *ltargetlist;
2554 : : List *rtargetlist;
2555 : : const char *context;
1454 2556 [ + + ]: 13868 : bool recursive = (pstate->p_parent_cte &&
2557 [ + + ]: 804 : pstate->p_parent_cte->cterecursive);
2558 : :
9312 2559 [ + + ]: 13560 : context = (stmt->op == SETOP_UNION ? "UNION" :
2560 [ + + ]: 496 : (stmt->op == SETOP_INTERSECT ? "INTERSECT" :
2561 : : "EXCEPT"));
2562 : :
2563 : 13064 : op->op = stmt->op;
2564 : 13064 : op->all = stmt->all;
2565 : :
2566 : : /*
2567 : : * Recursively transform the left child node.
2568 : : */
6459 2569 : 13064 : op->larg = transformSetOperationTree(pstate, stmt->larg,
2570 : : false,
2571 : : <argetlist);
2572 : :
2573 : : /*
2574 : : * If we are processing a recursive union query, now is the time to
2575 : : * examine the non-recursive term's output columns and mark the
2576 : : * containing CTE as having those result columns. We should do this
2577 : : * only at the topmost setop of the CTE, of course.
2578 : : */
1700 peter@eisentraut.org 2579 [ + + + + ]: 13060 : if (isTopLevel && recursive)
5530 tgl@sss.pgh.pa.us 2580 : 708 : determineRecursiveColTypes(pstate, op->larg, ltargetlist);
2581 : :
2582 : : /*
2583 : : * Recursively transform the right child node.
2584 : : */
6459 2585 : 13060 : op->rarg = transformSetOperationTree(pstate, stmt->rarg,
2586 : : false,
2587 : : &rtargetlist);
2588 : :
53 peter@eisentraut.org 2589 :GNC 13044 : constructSetOpTargetlist(pstate, op, ltargetlist, rtargetlist, targetlist,
2590 : : context, recursive);
2591 : :
2592 : 13016 : return (Node *) op;
2593 : : }
2594 : : }
2595 : :
2596 : : /*
2597 : : * constructSetOpTargetlist
2598 : : * Compute the types, typmods and collations of the columns in the target
2599 : : * list of the given set operation.
2600 : : *
2601 : : * For every pair of columns in the targetlists of the children, compute the
2602 : : * common type, typmod, and collation representing the output (UNION) column.
2603 : : * If targetlist is not NULL, also build the dummy output targetlist
2604 : : * containing non-resjunk output columns. The values are stored into the
2605 : : * given SetOperationStmt node. context is a string for error messages
2606 : : * ("UNION" etc.). recursive is true if it is a recursive union.
2607 : : */
2608 : : void
2609 : 13422 : constructSetOpTargetlist(ParseState *pstate, SetOperationStmt *op,
2610 : : const List *ltargetlist, const List *rtargetlist,
2611 : : List **targetlist, const char *context, bool recursive)
2612 : : {
2613 : : ListCell *ltl;
2614 : : ListCell *rtl;
2615 : :
2616 : : /*
2617 : : * Verify that the two children have the same number of non-junk columns,
2618 : : * and determine the types of the merged output columns.
2619 : : */
2620 [ - + ]: 13422 : if (list_length(ltargetlist) != list_length(rtargetlist))
53 peter@eisentraut.org 2621 [ # # ]:UNC 0 : ereport(ERROR,
2622 : : (errcode(ERRCODE_SYNTAX_ERROR),
2623 : : errmsg("each %s query must have the same number of columns",
2624 : : context),
2625 : : parser_errposition(pstate,
2626 : : exprLocation((const Node *) rtargetlist))));
2627 : :
53 peter@eisentraut.org 2628 [ + + ]:GNC 13422 : if (targetlist)
2629 : 4721 : *targetlist = NIL;
2630 : 13422 : op->colTypes = NIL;
2631 : 13422 : op->colTypmods = NIL;
2632 : 13422 : op->colCollations = NIL;
2633 : 13422 : op->groupClauses = NIL;
2634 : :
2635 [ + + + + : 54665 : forboth(ltl, ltargetlist, rtl, rtargetlist)
+ + + + +
+ + - +
+ ]
2636 : : {
2637 : 41271 : TargetEntry *ltle = (TargetEntry *) lfirst(ltl);
2638 : 41271 : TargetEntry *rtle = (TargetEntry *) lfirst(rtl);
2639 : 41271 : Node *lcolnode = (Node *) ltle->expr;
2640 : 41271 : Node *rcolnode = (Node *) rtle->expr;
2641 : 41271 : Oid lcoltype = exprType(lcolnode);
2642 : 41271 : Oid rcoltype = exprType(rcolnode);
2643 : : Node *bestexpr;
2644 : : int bestlocation;
2645 : : Oid rescoltype;
2646 : : int32 rescoltypmod;
2647 : : Oid rescolcoll;
2648 : :
2649 : : /* select common type, same as CASE et al */
2650 : 41271 : rescoltype = select_common_type(pstate,
2651 : : list_make2(lcolnode, rcolnode),
2652 : : context,
2653 : : &bestexpr);
2654 : 41271 : bestlocation = exprLocation(bestexpr);
2655 : :
2656 : : /*
2657 : : * Verify the coercions are actually possible. If not, we'd fail
2658 : : * later anyway, but we want to fail now while we have sufficient
2659 : : * context to produce an error cursor position.
2660 : : *
2661 : : * For all non-UNKNOWN-type cases, we verify coercibility but we don't
2662 : : * modify the child's expression, for fear of changing the child
2663 : : * query's semantics.
2664 : : *
2665 : : * If a child expression is an UNKNOWN-type Const or Param, we want to
2666 : : * replace it with the coerced expression. This can only happen when
2667 : : * the child is a leaf set-op node. It's safe to replace the
2668 : : * expression because if the child query's semantics depended on the
2669 : : * type of this output column, it'd have already coerced the UNKNOWN
2670 : : * to something else. We want to do this because (a) we want to
2671 : : * verify that a Const is valid for the target type, or resolve the
2672 : : * actual type of an UNKNOWN Param, and (b) we want to avoid
2673 : : * unnecessary discrepancies between the output type of the child
2674 : : * query and the resolved target type. Such a discrepancy would
2675 : : * disable optimization in the planner.
2676 : : *
2677 : : * If it's some other UNKNOWN-type node, eg a Var, we do nothing
2678 : : * (knowing that coerce_to_common_type would fail). The planner is
2679 : : * sometimes able to fold an UNKNOWN Var to a constant before it has
2680 : : * to coerce the type, so failing now would just break cases that
2681 : : * might work.
2682 : : */
2683 [ + + ]: 41271 : if (lcoltype != UNKNOWNOID)
2684 : 36962 : lcolnode = coerce_to_common_type(pstate, lcolnode,
2685 : : rescoltype, context);
2686 [ - + ]: 4309 : else if (IsA(lcolnode, Const) ||
53 peter@eisentraut.org 2687 [ # # ]:UNC 0 : IsA(lcolnode, Param))
2688 : : {
53 peter@eisentraut.org 2689 :GNC 4309 : lcolnode = coerce_to_common_type(pstate, lcolnode,
2690 : : rescoltype, context);
2691 : 4309 : ltle->expr = (Expr *) lcolnode;
2692 : : }
2693 : :
2694 [ + + ]: 41271 : if (rcoltype != UNKNOWNOID)
2695 : 36418 : rcolnode = coerce_to_common_type(pstate, rcolnode,
2696 : : rescoltype, context);
2697 [ - + ]: 4853 : else if (IsA(rcolnode, Const) ||
53 peter@eisentraut.org 2698 [ # # ]:UNC 0 : IsA(rcolnode, Param))
2699 : : {
53 peter@eisentraut.org 2700 :GNC 4853 : rcolnode = coerce_to_common_type(pstate, rcolnode,
2701 : : rescoltype, context);
2702 : 4849 : rtle->expr = (Expr *) rcolnode;
2703 : : }
2704 : :
2705 : 41267 : rescoltypmod = select_common_typmod(pstate,
2706 : : list_make2(lcolnode, rcolnode),
2707 : : rescoltype);
2708 : :
2709 : : /*
2710 : : * Select common collation. A common collation is required for all
2711 : : * set operators except UNION ALL; see SQL:2008 7.13 <query
2712 : : * expression> Syntax Rule 15c. (If we fail to identify a common
2713 : : * collation for a UNION ALL column, the colCollations element will be
2714 : : * set to InvalidOid, which may result in a runtime error if something
2715 : : * at a higher query level wants to use the column's collation.)
2716 : : */
2717 : 41267 : rescolcoll = select_common_collation(pstate,
2718 : : list_make2(lcolnode, rcolnode),
2719 [ + + + + ]: 41267 : (op->op == SETOP_UNION && op->all));
2720 : :
2721 : : /* emit results */
2722 : 41243 : op->colTypes = lappend_oid(op->colTypes, rescoltype);
2723 : 41243 : op->colTypmods = lappend_int(op->colTypmods, rescoltypmod);
2724 : 41243 : op->colCollations = lappend_oid(op->colCollations, rescolcoll);
2725 : :
2726 : : /*
2727 : : * For all cases except UNION ALL, identify the grouping operators
2728 : : * (and, if available, sorting operators) that will be used to
2729 : : * eliminate duplicates.
2730 : : */
2731 [ + + + + ]: 41243 : if (op->op != SETOP_UNION || !op->all)
2732 : : {
2733 : : ParseCallbackState pcbstate;
2734 : :
2735 : 17566 : setup_parser_errposition_callback(&pcbstate, pstate,
2736 : : bestlocation);
2737 : :
2738 : : /* If it's a recursive union, we need to require hashing support. */
2739 : 17566 : op->groupClauses = lappend(op->groupClauses,
2740 : 17566 : makeSortGroupClauseForSetOp(rescoltype, recursive));
2741 : :
2742 : 17566 : cancel_parser_errposition_callback(&pcbstate);
2743 : : }
2744 : :
2745 : : /*
2746 : : * Construct a dummy tlist entry to return. We use a SetToDefault
2747 : : * node for the expression, since it carries exactly the fields
2748 : : * needed, but any other expression node type would do as well.
2749 : : */
2750 [ + + ]: 41243 : if (targetlist)
2751 : : {
2752 : 19941 : SetToDefault *rescolnode = makeNode(SetToDefault);
2753 : : TargetEntry *restle;
2754 : :
2755 : 19941 : rescolnode->typeId = rescoltype;
2756 : 19941 : rescolnode->typeMod = rescoltypmod;
2757 : 19941 : rescolnode->collation = rescolcoll;
2758 : 19941 : rescolnode->location = bestlocation;
2759 : 19941 : restle = makeTargetEntry((Expr *) rescolnode,
2760 : : 0, /* no need to set resno */
2761 : : NULL,
2762 : : false);
2763 : 19941 : *targetlist = lappend(*targetlist, restle);
2764 : : }
2765 : : }
10892 scrappy@hub.org 2766 :GIC 13394 : }
2767 : :
2768 : : /*
2769 : : * Process the outputs of the non-recursive term of a recursive union
2770 : : * to set up the parent CTE's columns
2771 : : */
2772 : : static void
5530 tgl@sss.pgh.pa.us 2773 :CBC 708 : determineRecursiveColTypes(ParseState *pstate, Node *larg, List *nrtargetlist)
2774 : : {
2775 : : Node *node;
2776 : : int leftmostRTI;
2777 : : Query *leftmostQuery;
2778 : : List *targetList;
2779 : : ListCell *left_tlist;
2780 : : ListCell *nrtl;
2781 : : int next_resno;
2782 : :
2783 : : /*
2784 : : * Find leftmost leaf SELECT
2785 : : */
6082 2786 : 708 : node = larg;
2787 [ + - + + ]: 712 : while (node && IsA(node, SetOperationStmt))
2788 : 4 : node = ((SetOperationStmt *) node)->larg;
2789 [ + - - + ]: 708 : Assert(node && IsA(node, RangeTblRef));
2790 : 708 : leftmostRTI = ((RangeTblRef *) node)->rtindex;
2791 : 708 : leftmostQuery = rt_fetch(leftmostRTI, pstate->p_rtable)->subquery;
2792 [ - + ]: 708 : Assert(leftmostQuery != NULL);
2793 : :
2794 : : /*
2795 : : * Generate dummy targetlist using column names of leftmost select and
2796 : : * dummy result expressions of the non-recursive term.
2797 : : */
2798 : 708 : targetList = NIL;
2799 : 708 : next_resno = 1;
2800 : :
2623 2801 [ + - + + : 2240 : forboth(nrtl, nrtargetlist, left_tlist, leftmostQuery->targetList)
+ - + + +
+ + - +
+ ]
2802 : : {
5530 2803 : 1532 : TargetEntry *nrtle = (TargetEntry *) lfirst(nrtl);
6082 2804 : 1532 : TargetEntry *lefttle = (TargetEntry *) lfirst(left_tlist);
2805 : : char *colName;
2806 : : TargetEntry *tle;
2807 : :
2808 [ - + ]: 1532 : Assert(!lefttle->resjunk);
2809 : 1532 : colName = pstrdup(lefttle->resname);
5530 2810 : 1532 : tle = makeTargetEntry(nrtle->expr,
6082 2811 : 1532 : next_resno++,
2812 : : colName,
2813 : : false);
2814 : 1532 : targetList = lappend(targetList, tle);
2815 : : }
2816 : :
2817 : : /* Now build CTE's output column info using dummy targetlist */
2818 : 708 : analyzeCTETargetList(pstate, pstate->p_parent_cte, targetList);
2819 : 708 : }
2820 : :
2821 : :
2822 : : /*
2823 : : * transformReturnStmt -
2824 : : * transforms a return statement
2825 : : */
2826 : : static Query *
1854 peter@eisentraut.org 2827 : 2738 : transformReturnStmt(ParseState *pstate, ReturnStmt *stmt)
2828 : : {
2829 : 2738 : Query *qry = makeNode(Query);
2830 : :
2831 : 2738 : qry->commandType = CMD_SELECT;
2832 : 2738 : qry->isReturn = true;
2833 : :
2834 : 2738 : qry->targetList = list_make1(makeTargetEntry((Expr *) transformExpr(pstate, stmt->returnval, EXPR_KIND_SELECT_TARGET),
2835 : : 1, NULL, false));
2836 : :
2837 [ + - ]: 2734 : if (pstate->p_resolve_unknowns)
2838 : 2734 : resolveTargetListUnknowns(pstate, qry->targetList);
2839 : 2734 : qry->rtable = pstate->p_rtable;
1246 alvherre@alvh.no-ip. 2840 : 2734 : qry->rteperminfos = pstate->p_rteperminfos;
1854 peter@eisentraut.org 2841 : 2734 : qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
2842 : 2734 : qry->hasSubLinks = pstate->p_hasSubLinks;
2843 : 2734 : qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
2844 : 2734 : qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
2845 : 2734 : qry->hasAggs = pstate->p_hasAggs;
2846 : :
2847 : 2734 : assign_query_collations(pstate, qry);
2848 : :
2849 : 2734 : return qry;
2850 : : }
2851 : :
2852 : :
2853 : : /*
2854 : : * transformUpdateStmt -
2855 : : * transforms an update statement
2856 : : */
2857 : : static Query *
10343 bruce@momjian.us 2858 : 9248 : transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
2859 : : {
10466 2860 : 9248 : Query *qry = makeNode(Query);
2861 : : ParseNamespaceItem *nsitem;
2862 : : Node *qual;
2863 : :
10467 2864 : 9248 : qry->commandType = CMD_UPDATE;
2865 : :
2866 : : /* process the WITH clause independently of all else */
5681 tgl@sss.pgh.pa.us 2867 [ + + ]: 9248 : if (stmt->withClause)
2868 : : {
2869 : 56 : qry->hasRecursive = stmt->withClause->recursive;
2870 : 56 : qry->cteList = transformWithClause(pstate, stmt->withClause);
5548 2871 : 56 : qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
2872 : : }
2873 : :
8810 2874 : 18495 : qry->resultRelation = setTargetTable(pstate, stmt->relation,
3420 2875 : 9248 : stmt->relation->inh,
2876 : : true,
2877 : : ACL_UPDATE);
2878 : :
2879 : : /* disallow UPDATE ... WHERE CURRENT OF on a view */
13 dean.a.rasheed@gmail 2880 [ + + ]: 9247 : if (stmt->whereClause &&
2881 [ + + ]: 6956 : IsA(stmt->whereClause, CurrentOfExpr) &&
2882 [ + + ]: 104 : pstate->p_target_relation->rd_rel->relkind == RELKIND_VIEW)
2883 [ + - ]: 4 : ereport(ERROR,
2884 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2885 : : errmsg("WHERE CURRENT OF on a view is not implemented"));
2886 : :
34 peter@eisentraut.org 2887 [ + + ]:GNC 9243 : if (stmt->forPortionOf)
2888 : 483 : qry->forPortionOf = transformForPortionOfClause(pstate,
2889 : : qry->resultRelation,
2890 : 548 : stmt->forPortionOf,
2891 : : true);
2892 : :
2315 tgl@sss.pgh.pa.us 2893 : 9178 : nsitem = pstate->p_target_nsitem;
2894 : :
2895 : : /* subqueries in FROM cannot access the result relation */
4501 tgl@sss.pgh.pa.us 2896 :CBC 9178 : nsitem->p_lateral_only = true;
4497 2897 : 9178 : nsitem->p_lateral_ok = false;
2898 : :
2899 : : /*
2900 : : * the FROM clause is non-standard SQL syntax. We used to be able to do
2901 : : * this with REPLACE in POSTQUEL so we keep the feature.
2902 : : */
9211 2903 : 9178 : transformFromClause(pstate, stmt->fromClause);
2904 : :
2905 : : /* remaining clauses can reference the result relation normally */
4501 2906 : 9162 : nsitem->p_lateral_only = false;
4497 2907 : 9162 : nsitem->p_lateral_ok = true;
2908 : :
5016 2909 : 9162 : qual = transformWhereClause(pstate, stmt->whereClause,
2910 : : EXPR_KIND_WHERE, "WHERE");
2911 : :
474 dean.a.rasheed@gmail 2912 : 9154 : transformReturningClause(pstate, qry, stmt->returningClause,
2913 : : EXPR_KIND_RETURNING);
2914 : :
2915 : : /*
2916 : : * Now we are done with SELECT-like processing, and can get on with
2917 : : * transforming the target list to match the UPDATE target columns.
2918 : : */
34 peter@eisentraut.org 2919 :GNC 9142 : qry->targetList = transformUpdateTargetList(pstate, stmt->targetList,
2920 : : qry->forPortionOf);
2921 : :
10467 bruce@momjian.us 2922 :CBC 9106 : qry->rtable = pstate->p_rtable;
1246 alvherre@alvh.no-ip. 2923 : 9106 : qry->rteperminfos = pstate->p_rteperminfos;
9349 tgl@sss.pgh.pa.us 2924 : 9106 : qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
2925 : :
3521 2926 : 9106 : qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
9366 2927 : 9106 : qry->hasSubLinks = pstate->p_hasSubLinks;
2928 : :
4015 andres@anarazel.de 2929 : 9106 : assign_query_collations(pstate, qry);
2930 : :
2931 : 9106 : return qry;
2932 : : }
2933 : :
2934 : : /*
2935 : : * transformUpdateTargetList -
2936 : : * handle SET clause in UPDATE/MERGE/INSERT ... ON CONFLICT UPDATE
2937 : : */
2938 : : List *
34 peter@eisentraut.org 2939 :GNC 11045 : transformUpdateTargetList(ParseState *pstate, List *origTlist, ForPortionOfExpr *forPortionOf)
2940 : : {
4000 bruce@momjian.us 2941 :CBC 11045 : List *tlist = NIL;
2942 : : RTEPermissionInfo *target_perminfo;
2943 : : ListCell *orig_tl;
2944 : : ListCell *tl;
2945 : :
4015 andres@anarazel.de 2946 : 11045 : tlist = transformTargetList(pstate, origTlist,
2947 : : EXPR_KIND_UPDATE_SOURCE);
2948 : :
2949 : : /* Prepare to assign non-conflicting resnos to resjunk attributes */
2950 teodor@sigaev.ru 2950 [ + + ]: 11013 : if (pstate->p_next_resno <= RelationGetNumberOfAttributes(pstate->p_target_relation))
2951 : 9356 : pstate->p_next_resno = RelationGetNumberOfAttributes(pstate->p_target_relation) + 1;
2952 : :
2953 : : /* Prepare non-junk columns for assignment to target table */
1246 alvherre@alvh.no-ip. 2954 : 11013 : target_perminfo = pstate->p_target_nsitem->p_perminfo;
4015 andres@anarazel.de 2955 : 11013 : orig_tl = list_head(origTlist);
2956 : :
2957 [ + - + + : 24656 : foreach(tl, tlist)
+ + ]
2958 : : {
9787 tgl@sss.pgh.pa.us 2959 : 13671 : TargetEntry *tle = (TargetEntry *) lfirst(tl);
2960 : : ResTarget *origTarget;
2961 : : int attrno;
2962 : :
7699 2963 [ + + ]: 13671 : if (tle->resjunk)
2964 : : {
2965 : : /*
2966 : : * Resjunk nodes need no additional processing, but be sure they
2967 : : * have resnos that do not match any target columns; else rewriter
2968 : : * or planner might get confused. They don't need a resname
2969 : : * either.
2970 : : */
2971 : 91 : tle->resno = (AttrNumber) pstate->p_next_resno++;
2972 : 91 : tle->resname = NULL;
9787 2973 : 91 : continue;
2974 : : }
4015 andres@anarazel.de 2975 [ - + ]: 13580 : if (orig_tl == NULL)
9787 tgl@sss.pgh.pa.us 2976 [ # # ]:UBC 0 : elog(ERROR, "UPDATE target count mismatch --- internal error");
3312 tgl@sss.pgh.pa.us 2977 :CBC 13580 : origTarget = lfirst_node(ResTarget, orig_tl);
2978 : :
7348 2979 : 13580 : attrno = attnameAttNum(pstate->p_target_relation,
2980 : 13580 : origTarget->name, true);
2981 [ + + ]: 13580 : if (attrno == InvalidAttrNumber)
2982 [ + - + + : 16 : ereport(ERROR,
+ - ]
2983 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
2984 : : errmsg("column \"%s\" of relation \"%s\" does not exist",
2985 : : origTarget->name,
2986 : : RelationGetRelationName(pstate->p_target_relation)),
2987 : : (origTarget->indirection != NIL &&
2988 : : strcmp(origTarget->name, pstate->p_target_nsitem->p_names->aliasname) == 0) ?
2989 : : errhint("SET target columns cannot be qualified with the relation name.") : 0,
2990 : : parser_errposition(pstate, origTarget->location)));
2991 : :
2992 : : /*
2993 : : * If this is a FOR PORTION OF update, forbid directly setting the
2994 : : * range column, since that would conflict with the implicit updates.
2995 : : */
34 peter@eisentraut.org 2996 [ + + ]:GNC 13564 : if (forPortionOf != NULL)
2997 : : {
2998 [ + + ]: 496 : if (attrno == forPortionOf->rangeVar->varattno)
2999 [ + - ]: 4 : ereport(ERROR,
3000 : : (errcode(ERRCODE_SYNTAX_ERROR),
3001 : : errmsg("cannot update column \"%s\" because it is used in FOR PORTION OF",
3002 : : origTarget->name),
3003 : : parser_errposition(pstate, origTarget->location)));
3004 : : }
3005 : :
9682 tgl@sss.pgh.pa.us 3006 :CBC 13560 : updateTargetListEntry(pstate, tle, origTarget->name,
3007 : : attrno,
3008 : : origTarget->indirection,
3009 : : origTarget->location);
3010 : :
3011 : : /* Mark the target column as requiring update permissions */
1246 alvherre@alvh.no-ip. 3012 : 13552 : target_perminfo->updatedCols = bms_add_member(target_perminfo->updatedCols,
3013 : : attrno - FirstLowInvalidHeapAttributeNumber);
3014 : :
2486 tgl@sss.pgh.pa.us 3015 : 13552 : orig_tl = lnext(origTlist, orig_tl);
3016 : : }
4015 andres@anarazel.de 3017 [ - + ]: 10985 : if (orig_tl != NULL)
9787 tgl@sss.pgh.pa.us 3018 [ # # ]:UBC 0 : elog(ERROR, "UPDATE target count mismatch --- internal error");
3019 : :
2269 peter@eisentraut.org 3020 :CBC 10985 : return tlist;
3021 : : }
3022 : :
3023 : : /*
3024 : : * addNSItemForReturning -
3025 : : * add a ParseNamespaceItem for the OLD or NEW alias in RETURNING.
3026 : : */
3027 : : static void
474 dean.a.rasheed@gmail 3028 : 4578 : addNSItemForReturning(ParseState *pstate, const char *aliasname,
3029 : : VarReturningType returning_type)
3030 : : {
3031 : : List *colnames;
3032 : : int numattrs;
3033 : : ParseNamespaceColumn *nscolumns;
3034 : : ParseNamespaceItem *nsitem;
3035 : :
3036 : : /* copy per-column data from the target relation */
3037 : 4578 : colnames = pstate->p_target_nsitem->p_rte->eref->colnames;
3038 : 4578 : numattrs = list_length(colnames);
3039 : :
146 michael@paquier.xyz 3040 :GNC 4578 : nscolumns = palloc_array(ParseNamespaceColumn, numattrs);
3041 : :
474 dean.a.rasheed@gmail 3042 :CBC 4578 : memcpy(nscolumns, pstate->p_target_nsitem->p_nscolumns,
3043 : : numattrs * sizeof(ParseNamespaceColumn));
3044 : :
3045 : : /* mark all columns as returning OLD/NEW */
3046 [ + + ]: 17974 : for (int i = 0; i < numattrs; i++)
3047 : 13396 : nscolumns[i].p_varreturningtype = returning_type;
3048 : :
3049 : : /* build the nsitem, copying most fields from the target relation */
146 michael@paquier.xyz 3050 :GNC 4578 : nsitem = palloc_object(ParseNamespaceItem);
474 dean.a.rasheed@gmail 3051 :CBC 4578 : nsitem->p_names = makeAlias(aliasname, colnames);
3052 : 4578 : nsitem->p_rte = pstate->p_target_nsitem->p_rte;
3053 : 4578 : nsitem->p_rtindex = pstate->p_target_nsitem->p_rtindex;
3054 : 4578 : nsitem->p_perminfo = pstate->p_target_nsitem->p_perminfo;
3055 : 4578 : nsitem->p_nscolumns = nscolumns;
3056 : 4578 : nsitem->p_returning_type = returning_type;
3057 : :
3058 : : /* add it to the query namespace as a table-only item */
3059 : 4578 : addNSItemToQuery(pstate, nsitem, false, true, false);
3060 : 4578 : }
3061 : :
3062 : : /*
3063 : : * transformReturningClause -
3064 : : * handle a RETURNING clause in INSERT/UPDATE/DELETE/MERGE
3065 : : */
3066 : : void
3067 : 14850 : transformReturningClause(ParseState *pstate, Query *qry,
3068 : : ReturningClause *returningClause,
3069 : : ParseExprKind exprKind)
3070 : : {
3071 : 14850 : int save_nslen = list_length(pstate->p_namespace);
3072 : : int save_next_resno;
3073 : :
3074 [ + + ]: 14850 : if (returningClause == NULL)
3075 : 12515 : return; /* nothing to do */
3076 : :
3077 : : /*
3078 : : * Scan RETURNING WITH(...) options for OLD/NEW alias names. Complain if
3079 : : * there is any conflict with existing relations.
3080 : : */
3081 [ + + + + : 4718 : foreach_node(ReturningOption, option, returningClause->options)
+ + ]
3082 : : {
3083 [ + + - ]: 80 : switch (option->option)
3084 : : {
3085 : 36 : case RETURNING_OPTION_OLD:
3086 [ + + ]: 36 : if (qry->returningOldAlias != NULL)
3087 [ + - ]: 4 : ereport(ERROR,
3088 : : errcode(ERRCODE_SYNTAX_ERROR),
3089 : : /* translator: %s is OLD or NEW */
3090 : : errmsg("%s cannot be specified multiple times", "OLD"),
3091 : : parser_errposition(pstate, option->location));
3092 : 32 : qry->returningOldAlias = option->value;
3093 : 32 : break;
3094 : :
3095 : 44 : case RETURNING_OPTION_NEW:
3096 [ + + ]: 44 : if (qry->returningNewAlias != NULL)
3097 [ + - ]: 4 : ereport(ERROR,
3098 : : errcode(ERRCODE_SYNTAX_ERROR),
3099 : : /* translator: %s is OLD or NEW */
3100 : : errmsg("%s cannot be specified multiple times", "NEW"),
3101 : : parser_errposition(pstate, option->location));
3102 : 40 : qry->returningNewAlias = option->value;
3103 : 40 : break;
3104 : :
474 dean.a.rasheed@gmail 3105 :UBC 0 : default:
3106 [ # # ]: 0 : elog(ERROR, "unrecognized returning option: %d", option->option);
3107 : : }
3108 : :
474 dean.a.rasheed@gmail 3109 [ + + ]:CBC 72 : if (refnameNamespaceItem(pstate, NULL, option->value, -1, NULL) != NULL)
3110 [ + - ]: 8 : ereport(ERROR,
3111 : : errcode(ERRCODE_DUPLICATE_ALIAS),
3112 : : errmsg("table name \"%s\" specified more than once",
3113 : : option->value),
3114 : : parser_errposition(pstate, option->location));
3115 : :
3116 : 64 : addNSItemForReturning(pstate, option->value,
3117 [ + + ]: 64 : option->option == RETURNING_OPTION_OLD ?
3118 : : VAR_RETURNING_OLD : VAR_RETURNING_NEW);
3119 : : }
3120 : :
3121 : : /*
3122 : : * If OLD/NEW alias names weren't explicitly specified, use "old"/"new"
3123 : : * unless masked by existing relations.
3124 : : */
3125 [ + + + + ]: 4618 : if (qry->returningOldAlias == NULL &&
3126 : 2299 : refnameNamespaceItem(pstate, NULL, "old", -1, NULL) == NULL)
3127 : : {
3128 : 2259 : qry->returningOldAlias = "old";
3129 : 2259 : addNSItemForReturning(pstate, "old", VAR_RETURNING_OLD);
3130 : : }
3131 [ + + + + ]: 4614 : if (qry->returningNewAlias == NULL &&
3132 : 2295 : refnameNamespaceItem(pstate, NULL, "new", -1, NULL) == NULL)
3133 : : {
3134 : 2255 : qry->returningNewAlias = "new";
3135 : 2255 : addNSItemForReturning(pstate, "new", VAR_RETURNING_NEW);
3136 : : }
3137 : :
3138 : : /*
3139 : : * We need to assign resnos starting at one in the RETURNING list. Save
3140 : : * and restore the main tlist's value of p_next_resno, just in case
3141 : : * someone looks at it later (probably won't happen).
3142 : : */
7206 tgl@sss.pgh.pa.us 3143 : 2319 : save_next_resno = pstate->p_next_resno;
3144 : 2319 : pstate->p_next_resno = 1;
3145 : :
3146 : : /* transform RETURNING expressions identically to a SELECT targetlist */
474 dean.a.rasheed@gmail 3147 : 2319 : qry->returningList = transformTargetList(pstate,
3148 : : returningClause->exprs,
3149 : : exprKind);
3150 : :
3151 : : /*
3152 : : * Complain if the nonempty tlist expanded to nothing (which is possible
3153 : : * if it contains only a star-expansion of a zero-column table). If we
3154 : : * allow this, the parsed Query will look like it didn't have RETURNING,
3155 : : * with results that would probably surprise the user.
3156 : : */
3157 [ + + ]: 2291 : if (qry->returningList == NIL)
4525 tgl@sss.pgh.pa.us 3158 [ + - ]: 4 : ereport(ERROR,
3159 : : (errcode(ERRCODE_SYNTAX_ERROR),
3160 : : errmsg("RETURNING must have at least one column"),
3161 : : parser_errposition(pstate,
3162 : : exprLocation(linitial(returningClause->exprs)))));
3163 : :
3164 : : /* mark column origins */
474 dean.a.rasheed@gmail 3165 : 2287 : markTargetListOrigins(pstate, qry->returningList);
3166 : :
3167 : : /* resolve any still-unresolved output columns as being type text */
3387 tgl@sss.pgh.pa.us 3168 [ + - ]: 2287 : if (pstate->p_resolve_unknowns)
474 dean.a.rasheed@gmail 3169 : 2287 : resolveTargetListUnknowns(pstate, qry->returningList);
3170 : :
3171 : : /* restore state */
3172 : 2287 : pstate->p_namespace = list_truncate(pstate->p_namespace, save_nslen);
7206 tgl@sss.pgh.pa.us 3173 : 2287 : pstate->p_next_resno = save_next_resno;
3174 : : }
3175 : :
3176 : :
3177 : : /*
3178 : : * transformPLAssignStmt -
3179 : : * transform a PL/pgSQL assignment statement
3180 : : *
3181 : : * If there is no opt_indirection, the transformed statement looks like
3182 : : * "SELECT a_expr ...", except the expression has been cast to the type of
3183 : : * the target. With indirection, it's still a SELECT, but the expression will
3184 : : * incorporate FieldStore and/or assignment SubscriptingRef nodes to compute a
3185 : : * new value for a container-type variable represented by the target. The
3186 : : * expression references the target as the container source.
3187 : : */
3188 : : static Query *
1947 3189 : 3547 : transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt)
3190 : : {
3191 : : Query *qry;
3192 : 3547 : ColumnRef *cref = makeNode(ColumnRef);
3193 : 3547 : List *indirection = stmt->indirection;
3194 : 3547 : int nnames = stmt->nnames;
3195 : : Node *target;
3196 : : SelectStmtPassthrough passthru;
3197 : : bool save_resolve_unknowns;
3198 : :
3199 : : /*
3200 : : * First, construct a ColumnRef for the target variable. If the target
3201 : : * has more than one dotted name, we have to pull the extra names out of
3202 : : * the indirection list.
3203 : : */
3204 : 3547 : cref->fields = list_make1(makeString(stmt->name));
3205 : 3547 : cref->location = stmt->location;
3206 [ + + ]: 3547 : if (nnames > 1)
3207 : : {
3208 : : /* avoid munging the raw parsetree */
3209 : 252 : indirection = list_copy(indirection);
3210 [ + + + - ]: 511 : while (--nnames > 0 && indirection != NIL)
3211 : : {
3212 : 259 : Node *ind = (Node *) linitial(indirection);
3213 : :
3214 [ - + ]: 259 : if (!IsA(ind, String))
1947 tgl@sss.pgh.pa.us 3215 [ # # ]:UBC 0 : elog(ERROR, "invalid name count in PLAssignStmt");
1947 tgl@sss.pgh.pa.us 3216 :CBC 259 : cref->fields = lappend(cref->fields, ind);
3217 : 259 : indirection = list_delete_first(indirection);
3218 : : }
3219 : : }
3220 : :
3221 : : /*
3222 : : * Transform the target reference. Typically we will get back a Param
3223 : : * node, but there's no reason to be too picky about its type. (Note that
3224 : : * we must do this before calling transformSelectStmt. It's tempting to
3225 : : * do it inside transformPLAssignStmtTarget, but we need to do it before
3226 : : * adding any FROM tables to the pstate's namespace, else we might wrongly
3227 : : * resolve the target as a table column.)
3228 : : */
3229 : 3547 : target = transformExpr(pstate, (Node *) cref,
3230 : : EXPR_KIND_UPDATE_TARGET);
3231 : :
3232 : : /* Set up passthrough data for transformPLAssignStmtTarget */
220 tgl@sss.pgh.pa.us 3233 :GNC 3541 : passthru.stmt = stmt;
3234 : 3541 : passthru.target = target;
3235 : 3541 : passthru.indirection = indirection;
3236 : :
3237 : : /*
3238 : : * To avoid duplicating a lot of code, we use transformSelectStmt to do
3239 : : * almost all of the work. However, we need to do additional processing
3240 : : * on the SELECT's targetlist after it's been transformed, but before
3241 : : * possible addition of targetlist items for ORDER BY or GROUP BY.
3242 : : * transformSelectStmt knows it should call transformPLAssignStmtTarget if
3243 : : * it's passed a passthru argument.
3244 : : *
3245 : : * Also, disable resolution of unknown-type tlist items; PL/pgSQL wants to
3246 : : * deal with that itself.
3247 : : */
3248 : 3541 : save_resolve_unknowns = pstate->p_resolve_unknowns;
3249 : 3541 : pstate->p_resolve_unknowns = false;
3250 : 3541 : qry = transformSelectStmt(pstate, stmt->val, &passthru);
3251 : 3534 : pstate->p_resolve_unknowns = save_resolve_unknowns;
3252 : :
3253 : 3534 : return qry;
3254 : : }
3255 : :
3256 : : /*
3257 : : * Callback function to adjust a SELECT's tlist to make the output suitable
3258 : : * for assignment to a PLAssignStmt's target variable.
3259 : : *
3260 : : * Note: we actually modify the tle->expr in-place, but the function's API
3261 : : * is set up to not presume that.
3262 : : */
3263 : : static List *
3264 : 3541 : transformPLAssignStmtTarget(ParseState *pstate, List *tlist,
3265 : : SelectStmtPassthrough *passthru)
3266 : : {
3267 : 3541 : PLAssignStmt *stmt = passthru->stmt;
3268 : 3541 : Node *target = passthru->target;
3269 : 3541 : List *indirection = passthru->indirection;
3270 : : Oid targettype;
3271 : : int32 targettypmod;
3272 : : Oid targetcollation;
3273 : : TargetEntry *tle;
3274 : : Oid type_id;
3275 : :
3276 : 3541 : targettype = exprType(target);
3277 : 3541 : targettypmod = exprTypmod(target);
3278 : 3541 : targetcollation = exprCollation(target);
3279 : :
3280 : : /* we should have exactly one targetlist item */
1947 tgl@sss.pgh.pa.us 3281 [ + + ]:CBC 3541 : if (list_length(tlist) != 1)
3282 [ + - ]: 2 : ereport(ERROR,
3283 : : (errcode(ERRCODE_SYNTAX_ERROR),
3284 : : errmsg_plural("assignment source returned %d column",
3285 : : "assignment source returned %d columns",
3286 : : list_length(tlist),
3287 : : list_length(tlist))));
3288 : :
3289 : 3539 : tle = linitial_node(TargetEntry, tlist);
3290 : :
3291 : : /*
3292 : : * This next bit is similar to transformAssignedExpr; the key difference
3293 : : * is we use COERCION_PLPGSQL not COERCION_ASSIGNMENT.
3294 : : */
3295 : 3539 : type_id = exprType((Node *) tle->expr);
3296 : :
3297 : 3539 : pstate->p_expr_kind = EXPR_KIND_UPDATE_TARGET;
3298 : :
3299 [ + + ]: 3539 : if (indirection)
3300 : : {
3301 : 60 : tle->expr = (Expr *)
3302 : 65 : transformAssignmentIndirection(pstate,
3303 : : target,
3304 : 65 : stmt->name,
3305 : : false,
3306 : : targettype,
3307 : : targettypmod,
3308 : : targetcollation,
3309 : : indirection,
3310 : : list_head(indirection),
3311 : 65 : (Node *) tle->expr,
3312 : : COERCION_PLPGSQL,
3313 : : exprLocation(target));
3314 : : }
3315 [ + + + + ]: 3474 : else if (targettype != type_id &&
3316 [ + + + + ]: 911 : (targettype == RECORDOID || ISCOMPLEX(targettype)) &&
3317 [ + + ]: 226 : (type_id == RECORDOID || ISCOMPLEX(type_id)))
3318 : : {
3319 : : /*
3320 : : * Hack: do not let coerce_to_target_type() deal with inconsistent
3321 : : * composite types. Just pass the expression result through as-is,
3322 : : * and let the PL/pgSQL executor do the conversion its way. This is
3323 : : * rather bogus, but it's needed for backwards compatibility.
3324 : : */
3325 : : }
3326 : : else
3327 : : {
3328 : : /*
3329 : : * For normal non-qualified target column, do type checking and
3330 : : * coercion.
3331 : : */
3332 : 3290 : Node *orig_expr = (Node *) tle->expr;
3333 : :
3334 : 3290 : tle->expr = (Expr *)
3335 : 3290 : coerce_to_target_type(pstate,
3336 : : orig_expr, type_id,
3337 : : targettype, targettypmod,
3338 : : COERCION_PLPGSQL,
3339 : : COERCE_IMPLICIT_CAST,
3340 : : -1);
3341 : : /* With COERCION_PLPGSQL, this error is probably unreachable */
3342 [ - + ]: 3290 : if (tle->expr == NULL)
1947 tgl@sss.pgh.pa.us 3343 [ # # ]:UBC 0 : ereport(ERROR,
3344 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
3345 : : errmsg("variable \"%s\" is of type %s"
3346 : : " but expression is of type %s",
3347 : : stmt->name,
3348 : : format_type_be(targettype),
3349 : : format_type_be(type_id)),
3350 : : errhint("You will need to rewrite or cast the expression."),
3351 : : parser_errposition(pstate, exprLocation(orig_expr))));
3352 : : }
3353 : :
1947 tgl@sss.pgh.pa.us 3354 :CBC 3534 : pstate->p_expr_kind = EXPR_KIND_NONE;
3355 : :
220 tgl@sss.pgh.pa.us 3356 :GNC 3534 : return list_make1(tle);
3357 : : }
3358 : :
3359 : :
3360 : : /*
3361 : : * transformDeclareCursorStmt -
3362 : : * transform a DECLARE CURSOR Statement
3363 : : *
3364 : : * DECLARE CURSOR is like other utility statements in that we emit it as a
3365 : : * CMD_UTILITY Query node; however, we must first transform the contained
3366 : : * query. We used to postpone that until execution, but it's really necessary
3367 : : * to do it during the normal parse analysis phase to ensure that side effects
3368 : : * of parser hooks happen at the expected time.
3369 : : */
3370 : : static Query *
6948 tgl@sss.pgh.pa.us 3371 :CBC 2699 : transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
3372 : : {
3373 : : Query *result;
3374 : : Query *query;
3375 : :
3376 [ + + ]: 2699 : if ((stmt->options & CURSOR_OPT_SCROLL) &&
3377 [ - + ]: 160 : (stmt->options & CURSOR_OPT_NO_SCROLL))
6948 tgl@sss.pgh.pa.us 3378 [ # # ]:UBC 0 : ereport(ERROR,
3379 : : (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
3380 : : /* translator: %s is a SQL keyword */
3381 : : errmsg("cannot specify both %s and %s",
3382 : : "SCROLL", "NO SCROLL")));
3383 : :
1854 peter@eisentraut.org 3384 [ - + ]:CBC 2699 : if ((stmt->options & CURSOR_OPT_ASENSITIVE) &&
1854 peter@eisentraut.org 3385 [ # # ]:UBC 0 : (stmt->options & CURSOR_OPT_INSENSITIVE))
3386 [ # # ]: 0 : ereport(ERROR,
3387 : : (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
3388 : : /* translator: %s is a SQL keyword */
3389 : : errmsg("cannot specify both %s and %s",
3390 : : "ASENSITIVE", "INSENSITIVE")));
3391 : :
3392 : : /* Transform contained query, not allowing SELECT INTO */
3398 tgl@sss.pgh.pa.us 3393 :CBC 2699 : query = transformStmt(pstate, stmt->query);
3394 : 2686 : stmt->query = (Node *) query;
3395 : :
3396 : : /* Grammar should not have allowed anything but SELECT */
3397 [ + - ]: 2686 : if (!IsA(query, Query) ||
3398 [ - + ]: 2686 : query->commandType != CMD_SELECT)
6455 tgl@sss.pgh.pa.us 3399 [ # # ]:UBC 0 : elog(ERROR, "unexpected non-SELECT command in DECLARE CURSOR");
3400 : :
3401 : : /*
3402 : : * We also disallow data-modifying WITH in a cursor. (This could be
3403 : : * allowed, but the semantics of when the updates occur might be
3404 : : * surprising.)
3405 : : */
3398 tgl@sss.pgh.pa.us 3406 [ - + ]:CBC 2686 : if (query->hasModifyingCTE)
5548 tgl@sss.pgh.pa.us 3407 [ # # ]:UBC 0 : ereport(ERROR,
3408 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3409 : : errmsg("DECLARE CURSOR must not contain data-modifying statements in WITH")));
3410 : :
3411 : : /* FOR UPDATE and WITH HOLD are not compatible */
3398 tgl@sss.pgh.pa.us 3412 [ + + - + ]:CBC 2686 : if (query->rowMarks != NIL && (stmt->options & CURSOR_OPT_HOLD))
6948 tgl@sss.pgh.pa.us 3413 [ # # ]:UBC 0 : ereport(ERROR,
3414 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3415 : : /*------
3416 : : translator: %s is a SQL row locking clause such as FOR UPDATE */
3417 : : errmsg("DECLARE CURSOR WITH HOLD ... %s is not supported",
3418 : : LCS_asString(((RowMarkClause *)
3419 : : linitial(query->rowMarks))->strength)),
3420 : : errdetail("Holdable cursors must be READ ONLY.")));
3421 : :
3422 : : /* FOR UPDATE and SCROLL are not compatible */
3398 tgl@sss.pgh.pa.us 3423 [ + + - + ]:CBC 2686 : if (query->rowMarks != NIL && (stmt->options & CURSOR_OPT_SCROLL))
6768 tgl@sss.pgh.pa.us 3424 [ # # ]:UBC 0 : ereport(ERROR,
3425 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3426 : : /*------
3427 : : translator: %s is a SQL row locking clause such as FOR UPDATE */
3428 : : errmsg("DECLARE SCROLL CURSOR ... %s is not supported",
3429 : : LCS_asString(((RowMarkClause *)
3430 : : linitial(query->rowMarks))->strength)),
3431 : : errdetail("Scrollable cursors must be READ ONLY.")));
3432 : :
3433 : : /* FOR UPDATE and INSENSITIVE are not compatible */
3398 tgl@sss.pgh.pa.us 3434 [ + + - + ]:CBC 2686 : if (query->rowMarks != NIL && (stmt->options & CURSOR_OPT_INSENSITIVE))
6768 tgl@sss.pgh.pa.us 3435 [ # # ]:UBC 0 : ereport(ERROR,
3436 : : (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
3437 : : /*------
3438 : : translator: %s is a SQL row locking clause such as FOR UPDATE */
3439 : : errmsg("DECLARE INSENSITIVE CURSOR ... %s is not valid",
3440 : : LCS_asString(((RowMarkClause *)
3441 : : linitial(query->rowMarks))->strength)),
3442 : : errdetail("Insensitive cursors must be READ ONLY.")));
3443 : :
3444 : : /* represent the command as a utility Query */
3398 tgl@sss.pgh.pa.us 3445 :CBC 2686 : result = makeNode(Query);
3446 : 2686 : result->commandType = CMD_UTILITY;
6948 3447 : 2686 : result->utilityStmt = (Node *) stmt;
3448 : :
3449 : 2686 : return result;
3450 : : }
3451 : :
3452 : :
3453 : : /*
3454 : : * transformExplainStmt -
3455 : : * transform an EXPLAIN Statement
3456 : : *
3457 : : * EXPLAIN is like other utility statements in that we emit it as a
3458 : : * CMD_UTILITY Query node; however, we must first transform the contained
3459 : : * query. We used to postpone that until execution, but it's really necessary
3460 : : * to do it during the normal parse analysis phase to ensure that side effects
3461 : : * of parser hooks happen at the expected time.
3462 : : */
3463 : : static Query *
3464 : 16491 : transformExplainStmt(ParseState *pstate, ExplainStmt *stmt)
3465 : : {
3466 : : Query *result;
1138 3467 : 16491 : bool generic_plan = false;
3468 : 16491 : Oid *paramTypes = NULL;
3469 : 16491 : int numParams = 0;
3470 : :
3471 : : /*
3472 : : * If we have no external source of parameter definitions, and the
3473 : : * GENERIC_PLAN option is specified, then accept variable parameter
3474 : : * definitions (similarly to PREPARE, for example).
3475 : : */
3476 [ + + ]: 16491 : if (pstate->p_paramref_hook == NULL)
3477 : : {
3478 : : ListCell *lc;
3479 : :
3480 [ + + + + : 32747 : foreach(lc, stmt->options)
+ + ]
3481 : : {
3482 : 16268 : DefElem *opt = (DefElem *) lfirst(lc);
3483 : :
3484 [ + + ]: 16268 : if (strcmp(opt->defname, "generic_plan") == 0)
3485 : 12 : generic_plan = defGetBoolean(opt);
3486 : : /* don't "break", as we want the last value */
3487 : : }
3488 [ + + ]: 16479 : if (generic_plan)
3489 : 12 : setup_parse_variable_parameters(pstate, ¶mTypes, &numParams);
3490 : : }
3491 : :
3492 : : /* transform contained query, allowing SELECT INTO */
3398 3493 : 16491 : stmt->query = (Node *) transformOptionalSelectInto(pstate, stmt->query);
3494 : :
3495 : : /* make sure all is well with parameter types */
1138 3496 [ + + ]: 16482 : if (generic_plan)
3497 : 12 : check_variable_parameters(pstate, (Query *) stmt->query);
3498 : :
3499 : : /* represent the command as a utility Query */
5160 3500 : 16482 : result = makeNode(Query);
3501 : 16482 : result->commandType = CMD_UTILITY;
3502 : 16482 : result->utilityStmt = (Node *) stmt;
3503 : :
3504 : 16482 : return result;
3505 : : }
3506 : :
3507 : :
3508 : : /*
3509 : : * transformCreateTableAsStmt -
3510 : : * transform a CREATE TABLE AS, SELECT ... INTO, or CREATE MATERIALIZED VIEW
3511 : : * Statement
3512 : : *
3513 : : * As with DECLARE CURSOR and EXPLAIN, transform the contained statement now.
3514 : : */
3515 : : static Query *
3516 : 1306 : transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt)
3517 : : {
3518 : : Query *result;
3519 : : Query *query;
3520 : :
3521 : : /* transform contained query, not allowing SELECT INTO */
4771 3522 : 1306 : query = transformStmt(pstate, stmt->query);
3523 : 1305 : stmt->query = (Node *) query;
3524 : :
3525 : : /* additional work needed for CREATE MATERIALIZED VIEW */
2124 michael@paquier.xyz 3526 [ + + ]: 1305 : if (stmt->objtype == OBJECT_MATVIEW)
3527 : : {
3528 : : ObjectAddress temp_object;
3529 : :
3530 : : /*
3531 : : * Prohibit a data-modifying CTE in the query used to create a
3532 : : * materialized view. It's not sufficiently clear what the user would
3533 : : * want to happen if the MV is refreshed or incrementally maintained.
3534 : : */
4771 tgl@sss.pgh.pa.us 3535 [ - + ]: 352 : if (query->hasModifyingCTE)
4771 tgl@sss.pgh.pa.us 3536 [ # # ]:UBC 0 : ereport(ERROR,
3537 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3538 : : errmsg("materialized views must not use data-modifying statements in WITH")));
3539 : :
3540 : : /*
3541 : : * Check whether any temporary database objects are used in the
3542 : : * creation query. It would be hard to refresh data or incrementally
3543 : : * maintain it if a source disappeared.
3544 : : */
162 tgl@sss.pgh.pa.us 3545 [ + + ]:GNC 352 : if (query_uses_temp_object(query, &temp_object))
4771 tgl@sss.pgh.pa.us 3546 [ + - ]:GBC 4 : ereport(ERROR,
3547 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3548 : : errmsg("materialized views must not use temporary objects"),
3549 : : errdetail("This view depends on temporary %s.",
3550 : : getObjectDescription(&temp_object, false))));
3551 : :
3552 : : /*
3553 : : * A materialized view would either need to save parameters for use in
3554 : : * maintaining/loading the data or prohibit them entirely. The latter
3555 : : * seems safer and more sane.
3556 : : */
4771 tgl@sss.pgh.pa.us 3557 [ - + ]:CBC 344 : if (query_contains_extern_params(query))
4771 tgl@sss.pgh.pa.us 3558 [ # # ]:UBC 0 : ereport(ERROR,
3559 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3560 : : errmsg("materialized views may not be defined using bound parameters")));
3561 : :
3562 : : /*
3563 : : * For now, we disallow unlogged materialized views, because it seems
3564 : : * like a bad idea for them to just go to empty after a crash. (If we
3565 : : * could mark them as unpopulated, that would be better, but that
3566 : : * requires catalog changes which crash recovery can't presently
3567 : : * handle.)
3568 : : */
4747 tgl@sss.pgh.pa.us 3569 [ - + ]:CBC 344 : if (stmt->into->rel->relpersistence == RELPERSISTENCE_UNLOGGED)
4747 tgl@sss.pgh.pa.us 3570 [ # # ]:UBC 0 : ereport(ERROR,
3571 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3572 : : errmsg("materialized views cannot be unlogged")));
3573 : :
3574 : : /*
3575 : : * At runtime, we'll need a copy of the parsed-but-not-rewritten Query
3576 : : * for purposes of creating the view's ON SELECT rule. We stash that
3577 : : * in the IntoClause because that's where intorel_startup() can
3578 : : * conveniently get it from.
3579 : : */
565 peter@eisentraut.org 3580 :CBC 344 : stmt->into->viewQuery = copyObject(query);
3581 : : }
3582 : :
3583 : : /* represent the command as a utility Query */
6948 tgl@sss.pgh.pa.us 3584 : 1297 : result = makeNode(Query);
3585 : 1297 : result->commandType = CMD_UTILITY;
3586 : 1297 : result->utilityStmt = (Node *) stmt;
3587 : :
3588 : 1297 : return result;
3589 : : }
3590 : :
3591 : : /*
3592 : : * transform a CallStmt
3593 : : */
3594 : : static Query *
2996 peter_e@gmx.net 3595 : 312 : transformCallStmt(ParseState *pstate, CallStmt *stmt)
3596 : : {
3597 : : List *targs;
3598 : : ListCell *lc;
3599 : : Node *node;
3600 : : FuncExpr *fexpr;
3601 : : HeapTuple proctup;
3602 : : Datum proargmodes;
3603 : : bool isNull;
1790 tgl@sss.pgh.pa.us 3604 : 312 : List *outargs = NIL;
3605 : : Query *result;
3606 : :
3607 : : /*
3608 : : * First, do standard parse analysis on the procedure call and its
3609 : : * arguments, allowing us to identify the called procedure.
3610 : : */
2996 peter_e@gmx.net 3611 : 312 : targs = NIL;
3612 [ + + + + : 763 : foreach(lc, stmt->funccall->args)
+ + ]
3613 : : {
3614 : 451 : targs = lappend(targs, transformExpr(pstate,
3615 : 451 : (Node *) lfirst(lc),
3616 : : EXPR_KIND_CALL_ARGUMENT));
3617 : : }
3618 : :
3619 : 312 : node = ParseFuncOrColumn(pstate,
3620 : 312 : stmt->funccall->funcname,
3621 : : targs,
3622 : : pstate->p_last_srf,
3623 : : stmt->funccall,
3624 : : true,
3625 : 312 : stmt->funccall->location);
3626 : :
2646 peter@eisentraut.org 3627 : 291 : assign_expr_collations(pstate, node);
3628 : :
1790 tgl@sss.pgh.pa.us 3629 : 291 : fexpr = castNode(FuncExpr, node);
3630 : :
3631 : 291 : proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(fexpr->funcid));
3632 [ - + ]: 291 : if (!HeapTupleIsValid(proctup))
1790 tgl@sss.pgh.pa.us 3633 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for function %u", fexpr->funcid);
3634 : :
3635 : : /*
3636 : : * Expand the argument list to deal with named-argument notation and
3637 : : * default arguments. For ordinary FuncExprs this'd be done during
3638 : : * planning, but a CallStmt doesn't go through planning, and there seems
3639 : : * no good reason not to do it here.
3640 : : */
1790 tgl@sss.pgh.pa.us 3641 :CBC 291 : fexpr->args = expand_function_arguments(fexpr->args,
3642 : : true,
3643 : : fexpr->funcresulttype,
3644 : : proctup);
3645 : :
3646 : : /* Fetch proargmodes; if it's null, there are no output args */
3647 : 291 : proargmodes = SysCacheGetAttr(PROCOID, proctup,
3648 : : Anum_pg_proc_proargmodes,
3649 : : &isNull);
3650 [ + + ]: 291 : if (!isNull)
3651 : : {
3652 : : /*
3653 : : * Split the list into input arguments in fexpr->args and output
3654 : : * arguments in stmt->outargs. INOUT arguments appear in both lists.
3655 : : */
3656 : : ArrayType *arr;
3657 : : int numargs;
3658 : : char *argmodes;
3659 : : List *inargs;
3660 : : int i;
3661 : :
3662 : 118 : arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */
3663 : 118 : numargs = list_length(fexpr->args);
3664 [ + - ]: 118 : if (ARR_NDIM(arr) != 1 ||
3665 [ + - ]: 118 : ARR_DIMS(arr)[0] != numargs ||
3666 [ + - ]: 118 : ARR_HASNULL(arr) ||
3667 [ - + ]: 118 : ARR_ELEMTYPE(arr) != CHAROID)
1790 tgl@sss.pgh.pa.us 3668 [ # # ]:UBC 0 : elog(ERROR, "proargmodes is not a 1-D char array of length %d or it contains nulls",
3669 : : numargs);
1790 tgl@sss.pgh.pa.us 3670 [ - + ]:CBC 118 : argmodes = (char *) ARR_DATA_PTR(arr);
3671 : :
3672 : 118 : inargs = NIL;
3673 : 118 : i = 0;
3674 [ + - + + : 393 : foreach(lc, fexpr->args)
+ + ]
3675 : : {
3676 : 275 : Node *n = lfirst(lc);
3677 : :
3678 [ + + + - ]: 275 : switch (argmodes[i])
3679 : : {
3680 : 91 : case PROARGMODE_IN:
3681 : : case PROARGMODE_VARIADIC:
3682 : 91 : inargs = lappend(inargs, n);
3683 : 91 : break;
3684 : 72 : case PROARGMODE_OUT:
3685 : 72 : outargs = lappend(outargs, n);
3686 : 72 : break;
3687 : 112 : case PROARGMODE_INOUT:
3688 : 112 : inargs = lappend(inargs, n);
3689 : 112 : outargs = lappend(outargs, copyObject(n));
3690 : 112 : break;
1790 tgl@sss.pgh.pa.us 3691 :UBC 0 : default:
3692 : : /* note we don't support PROARGMODE_TABLE */
3693 [ # # ]: 0 : elog(ERROR, "invalid argmode %c for procedure",
3694 : : argmodes[i]);
3695 : : break;
3696 : : }
1790 tgl@sss.pgh.pa.us 3697 :CBC 275 : i++;
3698 : : }
3699 : 118 : fexpr->args = inargs;
3700 : : }
3701 : :
3702 : 291 : stmt->funcexpr = fexpr;
3703 : 291 : stmt->outargs = outargs;
3704 : :
3705 : 291 : ReleaseSysCache(proctup);
3706 : :
3707 : : /* represent the command as a utility Query */
2996 peter_e@gmx.net 3708 : 291 : result = makeNode(Query);
3709 : 291 : result->commandType = CMD_UTILITY;
3710 : 291 : result->utilityStmt = (Node *) stmt;
3711 : :
3712 : 291 : return result;
3713 : : }
3714 : :
3715 : : /*
3716 : : * Produce a string representation of a LockClauseStrength value.
3717 : : * This should only be applied to valid values (not LCS_NONE).
3718 : : */
3719 : : const char *
4669 alvherre@alvh.no-ip. 3720 : 32 : LCS_asString(LockClauseStrength strength)
3721 : : {
3722 [ - - - + : 32 : switch (strength)
+ - ]
3723 : : {
4069 tgl@sss.pgh.pa.us 3724 :UBC 0 : case LCS_NONE:
3725 : 0 : Assert(false);
3726 : : break;
4669 alvherre@alvh.no-ip. 3727 : 0 : case LCS_FORKEYSHARE:
3728 : 0 : return "FOR KEY SHARE";
3729 : 0 : case LCS_FORSHARE:
3730 : 0 : return "FOR SHARE";
4669 alvherre@alvh.no-ip. 3731 :CBC 4 : case LCS_FORNOKEYUPDATE:
3732 : 4 : return "FOR NO KEY UPDATE";
3733 : 28 : case LCS_FORUPDATE:
3734 : 28 : return "FOR UPDATE";
3735 : : }
4382 bruce@momjian.us 3736 :UBC 0 : return "FOR some"; /* shouldn't happen */
3737 : : }
3738 : :
3739 : : /*
3740 : : * Check for features that are not supported with FOR [KEY] UPDATE/SHARE.
3741 : : *
3742 : : * exported so planner can check again after rewriting, query pullup, etc
3743 : : */
3744 : : void
4659 alvherre@alvh.no-ip. 3745 :CBC 11573 : CheckSelectLocking(Query *qry, LockClauseStrength strength)
3746 : : {
3240 tgl@sss.pgh.pa.us 3747 [ - + ]: 11573 : Assert(strength != LCS_NONE); /* else caller error */
3748 : :
9343 3749 [ - + ]: 11573 : if (qry->setOperations)
8326 tgl@sss.pgh.pa.us 3750 [ # # ]:UBC 0 : ereport(ERROR,
3751 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3752 : : /*------
3753 : : translator: %s is a SQL row locking clause such as FOR UPDATE */
3754 : : errmsg("%s is not allowed with UNION/INTERSECT/EXCEPT",
3755 : : LCS_asString(strength))));
9595 tgl@sss.pgh.pa.us 3756 [ - + ]:CBC 11573 : if (qry->distinctClause != NIL)
8326 tgl@sss.pgh.pa.us 3757 [ # # ]:UBC 0 : ereport(ERROR,
3758 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3759 : : /*------
3760 : : translator: %s is a SQL row locking clause such as FOR UPDATE */
3761 : : errmsg("%s is not allowed with DISTINCT clause",
3762 : : LCS_asString(strength))));
1799 tgl@sss.pgh.pa.us 3763 [ + + + + ]:CBC 11573 : if (qry->groupClause != NIL || qry->groupingSets != NIL)
8326 3764 [ + - ]: 8 : ereport(ERROR,
3765 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3766 : : /*------
3767 : : translator: %s is a SQL row locking clause such as FOR UPDATE */
3768 : : errmsg("%s is not allowed with GROUP BY clause",
3769 : : LCS_asString(strength))));
7726 3770 [ - + ]: 11565 : if (qry->havingQual != NULL)
7726 tgl@sss.pgh.pa.us 3771 [ # # ]:UBC 0 : ereport(ERROR,
3772 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3773 : : /*------
3774 : : translator: %s is a SQL row locking clause such as FOR UPDATE */
3775 : : errmsg("%s is not allowed with HAVING clause",
3776 : : LCS_asString(strength))));
9962 vadim4o@yahoo.com 3777 [ + + ]:CBC 11565 : if (qry->hasAggs)
8326 tgl@sss.pgh.pa.us 3778 [ + - ]: 4 : ereport(ERROR,
3779 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3780 : : /*------
3781 : : translator: %s is a SQL row locking clause such as FOR UPDATE */
3782 : : errmsg("%s is not allowed with aggregate functions",
3783 : : LCS_asString(strength))));
6337 3784 [ - + ]: 11561 : if (qry->hasWindowFuncs)
6337 tgl@sss.pgh.pa.us 3785 [ # # ]:UBC 0 : ereport(ERROR,
3786 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3787 : : /*------
3788 : : translator: %s is a SQL row locking clause such as FOR UPDATE */
3789 : : errmsg("%s is not allowed with window functions",
3790 : : LCS_asString(strength))));
3521 tgl@sss.pgh.pa.us 3791 [ - + ]:CBC 11561 : if (qry->hasTargetSRFs)
6035 tgl@sss.pgh.pa.us 3792 [ # # ]:UBC 0 : ereport(ERROR,
3793 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3794 : : /*------
3795 : : translator: %s is a SQL row locking clause such as FOR UPDATE */
3796 : : errmsg("%s is not allowed with set-returning functions in the target list",
3797 : : LCS_asString(strength))));
9962 vadim4o@yahoo.com 3798 :CBC 11561 : }
3799 : :
3800 : : /*
3801 : : * Transform a FOR [KEY] UPDATE/SHARE clause
3802 : : *
3803 : : * This basically involves replacing names by integer relids.
3804 : : *
3805 : : * NB: if you need to change this, see also markQueryForLocking()
3806 : : * in rewriteHandler.c, and isLockedRefname() in parse_relation.c.
3807 : : */
3808 : : static void
6033 tgl@sss.pgh.pa.us 3809 : 5009 : transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc,
3810 : : bool pushedDown)
3811 : : {
7582 3812 : 5009 : List *lockedRels = lc->lockedRels;
3813 : : ListCell *l;
3814 : : ListCell *rt;
3815 : : Index i;
3816 : : LockingClause *allrels;
3817 : :
4659 alvherre@alvh.no-ip. 3818 : 5009 : CheckSelectLocking(qry, lc->strength);
3819 : :
3820 : : /* make a clause we can pass down to subqueries to select all rels */
7582 tgl@sss.pgh.pa.us 3821 : 4997 : allrels = makeNode(LockingClause);
7507 bruce@momjian.us 3822 : 4997 : allrels->lockedRels = NIL; /* indicates all rels */
4850 alvherre@alvh.no-ip. 3823 : 4997 : allrels->strength = lc->strength;
4228 3824 : 4997 : allrels->waitPolicy = lc->waitPolicy;
3825 : :
7582 tgl@sss.pgh.pa.us 3826 [ + + ]: 4997 : if (lockedRels == NIL)
3827 : : {
3828 : : /*
3829 : : * Lock all regular tables used in query and its subqueries. We
3830 : : * examine inFromCl to exclude auto-added RTEs, particularly NEW/OLD
3831 : : * in rules. This is a bit of an abuse of a mostly-obsolete flag, but
3832 : : * it's convenient. We can't rely on the namespace mechanism that has
3833 : : * largely replaced inFromCl, since for example we need to lock
3834 : : * base-relation RTEs even if they are masked by upper joins.
3835 : : */
9349 3836 : 3798 : i = 0;
3837 [ + + + + : 7646 : foreach(rt, qry->rtable)
+ + ]
3838 : : {
3839 : 3848 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
3840 : :
3841 : 3848 : ++i;
1720 3842 [ + + ]: 3848 : if (!rte->inFromCl)
3843 : 8 : continue;
8217 3844 [ + - + ]: 3840 : switch (rte->rtekind)
3845 : : {
3846 : 3824 : case RTE_RELATION:
3847 : : {
3848 : : RTEPermissionInfo *perminfo;
3849 : :
1246 alvherre@alvh.no-ip. 3850 : 3824 : applyLockingClause(qry, i,
3851 : : lc->strength,
3852 : : lc->waitPolicy,
3853 : : pushedDown);
3854 : 3824 : perminfo = getRTEPermissionInfo(qry->rteperminfos, rte);
3855 : 3824 : perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE;
3856 : : }
8217 tgl@sss.pgh.pa.us 3857 : 3824 : break;
8217 tgl@sss.pgh.pa.us 3858 :UBC 0 : case RTE_SUBQUERY:
4228 alvherre@alvh.no-ip. 3859 : 0 : applyLockingClause(qry, i, lc->strength, lc->waitPolicy,
3860 : : pushedDown);
3861 : :
3862 : : /*
3863 : : * FOR UPDATE/SHARE of subquery is propagated to all of
3864 : : * subquery's rels, too. We could do this later (based on
3865 : : * the marking of the subquery RTE) but it is convenient
3866 : : * to have local knowledge in each query level about which
3867 : : * rels need to be opened with RowShareLock.
3868 : : */
6033 tgl@sss.pgh.pa.us 3869 : 0 : transformLockingClause(pstate, rte->subquery,
3870 : : allrels, true);
8217 3871 : 0 : break;
8217 tgl@sss.pgh.pa.us 3872 :CBC 16 : default:
3873 : : /* ignore JOIN, SPECIAL, FUNCTION, VALUES, CTE RTEs */
3874 : 16 : break;
3875 : : }
3876 : : }
3877 : : }
3878 : : else
3879 : : {
3880 : : /*
3881 : : * Lock just the named tables. As above, we allow locking any base
3882 : : * relation regardless of alias-visibility rules, so we need to
3883 : : * examine inFromCl to exclude OLD/NEW.
3884 : : */
7677 3885 [ + - + + : 2388 : foreach(l, lockedRels)
+ + ]
3886 : : {
6455 3887 : 1205 : RangeVar *thisrel = (RangeVar *) lfirst(l);
3888 : :
3889 : : /* For simplicity we insist on unqualified alias names here */
3890 [ + - - + ]: 1205 : if (thisrel->catalogname || thisrel->schemaname)
6455 tgl@sss.pgh.pa.us 3891 [ # # ]:UBC 0 : ereport(ERROR,
3892 : : (errcode(ERRCODE_SYNTAX_ERROR),
3893 : : /*------
3894 : : translator: %s is a SQL row locking clause such as FOR UPDATE */
3895 : : errmsg("%s must specify unqualified relation names",
3896 : : LCS_asString(lc->strength)),
3897 : : parser_errposition(pstate, thisrel->location)));
3898 : :
9349 tgl@sss.pgh.pa.us 3899 :CBC 1205 : i = 0;
3900 [ + - + + : 1399 : foreach(rt, qry->rtable)
+ + ]
3901 : : {
3902 : 1391 : RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
1385 dean.a.rasheed@gmail 3903 : 1391 : char *rtename = rte->eref->aliasname;
3904 : :
9349 tgl@sss.pgh.pa.us 3905 : 1391 : ++i;
1720 3906 [ + + ]: 1391 : if (!rte->inFromCl)
3907 : 16 : continue;
3908 : :
3909 : : /*
3910 : : * A join RTE without an alias is not visible as a relation
3911 : : * name and needs to be skipped (otherwise it might hide a
3912 : : * base relation with the same name), except if it has a USING
3913 : : * alias, which *is* visible.
3914 : : *
3915 : : * Subquery and values RTEs without aliases are never visible
3916 : : * as relation names and must always be skipped.
3917 : : */
1385 dean.a.rasheed@gmail 3918 [ + + ]: 1375 : if (rte->alias == NULL)
3919 : : {
3920 [ + + ]: 109 : if (rte->rtekind == RTE_JOIN)
3921 : : {
3922 [ + + ]: 40 : if (rte->join_using_alias == NULL)
3923 : 32 : continue;
3924 : 8 : rtename = rte->join_using_alias->aliasname;
3925 : : }
3926 [ + + ]: 69 : else if (rte->rtekind == RTE_SUBQUERY ||
3927 [ - + ]: 65 : rte->rtekind == RTE_VALUES)
1398 3928 : 4 : continue;
3929 : : }
3930 : :
3931 [ + + ]: 1339 : if (strcmp(rtename, thisrel->relname) == 0)
3932 : : {
8217 tgl@sss.pgh.pa.us 3933 [ + + + - : 1197 : switch (rte->rtekind)
- - - -
- ]
3934 : : {
3935 : 1183 : case RTE_RELATION:
3936 : : {
3937 : : RTEPermissionInfo *perminfo;
3938 : :
1246 alvherre@alvh.no-ip. 3939 : 1183 : applyLockingClause(qry, i,
3940 : : lc->strength,
3941 : : lc->waitPolicy,
3942 : : pushedDown);
3943 : 1183 : perminfo = getRTEPermissionInfo(qry->rteperminfos, rte);
3944 : 1183 : perminfo->requiredPerms |= ACL_SELECT_FOR_UPDATE;
3945 : : }
8217 tgl@sss.pgh.pa.us 3946 : 1183 : break;
3947 : 6 : case RTE_SUBQUERY:
4228 alvherre@alvh.no-ip. 3948 : 6 : applyLockingClause(qry, i, lc->strength,
3949 : : lc->waitPolicy, pushedDown);
3950 : : /* see comment above */
6033 tgl@sss.pgh.pa.us 3951 : 6 : transformLockingClause(pstate, rte->subquery,
3952 : : allrels, true);
8217 3953 : 6 : break;
3954 : 8 : case RTE_JOIN:
3955 [ + - ]: 8 : ereport(ERROR,
3956 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3957 : : /*------
3958 : : translator: %s is a SQL row locking clause such as FOR UPDATE */
3959 : : errmsg("%s cannot be applied to a join",
3960 : : LCS_asString(lc->strength)),
3961 : : parser_errposition(pstate, thisrel->location)));
3962 : : break;
8217 tgl@sss.pgh.pa.us 3963 :UBC 0 : case RTE_FUNCTION:
3964 [ # # ]: 0 : ereport(ERROR,
3965 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3966 : : /*------
3967 : : translator: %s is a SQL row locking clause such as FOR UPDATE */
3968 : : errmsg("%s cannot be applied to a function",
3969 : : LCS_asString(lc->strength)),
3970 : : parser_errposition(pstate, thisrel->location)));
3971 : : break;
3345 alvherre@alvh.no-ip. 3972 : 0 : case RTE_TABLEFUNC:
3973 [ # # ]: 0 : ereport(ERROR,
3974 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3975 : : /*------
3976 : : translator: %s is a SQL row locking clause such as FOR UPDATE */
3977 : : errmsg("%s cannot be applied to a table function",
3978 : : LCS_asString(lc->strength)),
3979 : : parser_errposition(pstate, thisrel->location)));
3980 : : break;
7216 mail@joeconway.com 3981 : 0 : case RTE_VALUES:
3982 [ # # ]: 0 : ereport(ERROR,
3983 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3984 : : /*------
3985 : : translator: %s is a SQL row locking clause such as FOR UPDATE */
3986 : : errmsg("%s cannot be applied to VALUES",
3987 : : LCS_asString(lc->strength)),
3988 : : parser_errposition(pstate, thisrel->location)));
3989 : : break;
6422 tgl@sss.pgh.pa.us 3990 : 0 : case RTE_CTE:
6034 3991 [ # # ]: 0 : ereport(ERROR,
3992 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3993 : : /*------
3994 : : translator: %s is a SQL row locking clause such as FOR UPDATE */
3995 : : errmsg("%s cannot be applied to a WITH query",
3996 : : LCS_asString(lc->strength)),
3997 : : parser_errposition(pstate, thisrel->location)));
3998 : : break;
3322 kgrittn@postgresql.o 3999 : 0 : case RTE_NAMEDTUPLESTORE:
4000 [ # # ]: 0 : ereport(ERROR,
4001 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4002 : : /*------
4003 : : translator: %s is a SQL row locking clause such as FOR UPDATE */
4004 : : errmsg("%s cannot be applied to a named tuplestore",
4005 : : LCS_asString(lc->strength)),
4006 : : parser_errposition(pstate, thisrel->location)));
4007 : : break;
4008 : :
4009 : : /* Shouldn't be possible to see RTE_RESULT here */
4010 : :
8217 tgl@sss.pgh.pa.us 4011 : 0 : default:
4012 [ # # ]: 0 : elog(ERROR, "unrecognized RTE type: %d",
4013 : : (int) rte->rtekind);
4014 : : break;
4015 : : }
8217 tgl@sss.pgh.pa.us 4016 :CBC 1189 : break; /* out of foreach loop */
4017 : : }
4018 : : }
8014 neilc@samurai.com 4019 [ + + ]: 1197 : if (rt == NULL)
8326 tgl@sss.pgh.pa.us 4020 [ + - ]: 8 : ereport(ERROR,
4021 : : (errcode(ERRCODE_UNDEFINED_TABLE),
4022 : : /*------
4023 : : translator: %s is a SQL row locking clause such as FOR UPDATE */
4024 : : errmsg("relation \"%s\" in %s clause not found in FROM clause",
4025 : : thisrel->relname,
4026 : : LCS_asString(lc->strength)),
4027 : : parser_errposition(pstate, thisrel->location)));
4028 : : }
4029 : : }
7310 4030 : 4981 : }
4031 : :
4032 : : /*
4033 : : * Record locking info for a single rangetable item
4034 : : */
4035 : : void
6033 4036 : 5077 : applyLockingClause(Query *qry, Index rtindex,
4037 : : LockClauseStrength strength, LockWaitPolicy waitPolicy,
4038 : : bool pushedDown)
4039 : : {
4040 : : RowMarkClause *rc;
4041 : :
3240 4042 [ - + ]: 5077 : Assert(strength != LCS_NONE); /* else caller error */
4043 : :
4044 : : /* If it's an explicit clause, make sure hasForUpdate gets set */
6033 4045 [ + + ]: 5077 : if (!pushedDown)
4046 : 5011 : qry->hasForUpdate = true;
4047 : :
4048 : : /* Check for pre-existing entry for same rtindex */
6035 4049 [ - + ]: 5077 : if ((rc = get_parse_rowmark(qry, rtindex)) != NULL)
4050 : : {
4051 : : /*
4052 : : * If the same RTE is specified with more than one locking strength,
4053 : : * use the strongest. (Reasonable, since you can't take both a shared
4054 : : * and exclusive lock at the same time; it'll end up being exclusive
4055 : : * anyway.)
4056 : : *
4057 : : * Similarly, if the same RTE is specified with more than one lock
4058 : : * wait policy, consider that NOWAIT wins over SKIP LOCKED, which in
4059 : : * turn wins over waiting for the lock (the default). This is a bit
4060 : : * more debatable but raising an error doesn't seem helpful. (Consider
4061 : : * for instance SELECT FOR UPDATE NOWAIT from a view that internally
4062 : : * contains a plain FOR UPDATE spec.) Having NOWAIT win over SKIP
4063 : : * LOCKED is reasonable since the former throws an error in case of
4064 : : * coming across a locked tuple, which may be undesirable in some
4065 : : * cases but it seems better than silently returning inconsistent
4066 : : * results.
4067 : : *
4068 : : * And of course pushedDown becomes false if any clause is explicit.
4069 : : */
4850 alvherre@alvh.no-ip. 4070 :UBC 0 : rc->strength = Max(rc->strength, strength);
4228 4071 : 0 : rc->waitPolicy = Max(rc->waitPolicy, waitPolicy);
6033 tgl@sss.pgh.pa.us 4072 : 0 : rc->pushedDown &= pushedDown;
7310 4073 : 0 : return;
4074 : : }
4075 : :
4076 : : /* Make a new RowMarkClause */
7310 tgl@sss.pgh.pa.us 4077 :CBC 5077 : rc = makeNode(RowMarkClause);
4078 : 5077 : rc->rti = rtindex;
4850 alvherre@alvh.no-ip. 4079 : 5077 : rc->strength = strength;
4228 4080 : 5077 : rc->waitPolicy = waitPolicy;
6033 tgl@sss.pgh.pa.us 4081 : 5077 : rc->pushedDown = pushedDown;
7310 4082 : 5077 : qry->rowMarks = lappend(qry->rowMarks, rc);
4083 : : }
4084 : :
4085 : : #ifdef DEBUG_NODE_TESTS_ENABLED
4086 : : /*
4087 : : * Coverage testing for raw_expression_tree_walker().
4088 : : *
4089 : : * When enabled, we run raw_expression_tree_walker() over every DML statement
4090 : : * submitted to parse analysis. Without this provision, that function is only
4091 : : * applied in limited cases involving CTEs, and we don't really want to have
4092 : : * to test everything inside as well as outside a CTE.
4093 : : */
4094 : : static bool
3634 tgl@sss.pgh.pa.us 4095 :UBC 0 : test_raw_expression_coverage(Node *node, void *context)
4096 : : {
4097 [ # # ]: 0 : if (node == NULL)
4098 : 0 : return false;
4099 : 0 : return raw_expression_tree_walker(node,
4100 : : test_raw_expression_coverage,
4101 : : context);
4102 : : }
4103 : : #endif /* DEBUG_NODE_TESTS_ENABLED */
|