diff options
Diffstat (limited to 'src/backend/parser')
-rw-r--r-- | src/backend/parser/analyze.c | 149 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 121 | ||||
-rw-r--r-- | src/backend/parser/parse_clause.c | 203 | ||||
-rw-r--r-- | src/backend/parser/parse_collate.c | 2 | ||||
-rw-r--r-- | src/backend/parser/parse_target.c | 11 |
5 files changed, 447 insertions, 39 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 2d320d100b..3eb4feabfd 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -52,6 +52,8 @@ static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt); static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt); static List *transformInsertRow(ParseState *pstate, List *exprlist, List *stmtcols, List *icolumns, List *attrnos); +static OnConflictExpr *transformOnConflictClause(ParseState *pstate, + OnConflictClause *onConflictClause); static int count_rowexpr_columns(ParseState *pstate, Node *expr); static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt); static Query *transformValuesClause(ParseState *pstate, SelectStmt *stmt); @@ -62,6 +64,8 @@ static void determineRecursiveColTypes(ParseState *pstate, Node *larg, List *nrtargetlist); static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt); static List *transformReturningList(ParseState *pstate, List *returningList); +static List *transformUpdateTargetList(ParseState *pstate, + List *targetList); static Query *transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt); static Query *transformExplainStmt(ParseState *pstate, @@ -419,6 +423,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) ListCell *icols; ListCell *attnos; ListCell *lc; + bool isOnConflictUpdate; + AclMode targetPerms; /* There can't be any outer WITH to worry about */ Assert(pstate->p_ctenamespace == NIL); @@ -434,6 +440,9 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) qry->hasModifyingCTE = pstate->p_hasModifyingCTE; } + isOnConflictUpdate = (stmt->onConflictClause && + stmt->onConflictClause->action == ONCONFLICT_UPDATE); + /* * We have three cases to deal with: DEFAULT VALUES (selectStmt == NULL), * VALUES list, or general SELECT input. We special-case VALUES, both for @@ -478,8 +487,11 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * mentioned in the SELECT part. Note that the target table is not added * to the joinlist or namespace. */ + targetPerms = ACL_INSERT; + if (isOnConflictUpdate) + targetPerms |= ACL_UPDATE; qry->resultRelation = setTargetTable(pstate, stmt->relation, - false, false, ACL_INSERT); + false, false, targetPerms); /* Validate stmt->cols list, or build default list if no list given */ icolumns = checkInsertTargets(pstate, stmt->cols, &attrnos); @@ -740,6 +752,11 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) attnos = lnext(attnos); } + /* Process ON CONFLICT, if any. */ + if (stmt->onConflictClause) + qry->onConflict = transformOnConflictClause(pstate, + stmt->onConflictClause); + /* * If we have a RETURNING clause, we need to add the target relation to * the query namespace before processing it, so that Var references in @@ -850,6 +867,85 @@ transformInsertRow(ParseState *pstate, List *exprlist, } /* + * transformSelectStmt - + * transforms an OnConflictClause in an INSERT + */ +static OnConflictExpr * +transformOnConflictClause(ParseState *pstate, + OnConflictClause *onConflictClause) +{ + List *arbiterElems; + Node *arbiterWhere; + Oid arbiterConstraint; + List *onConflictSet = NIL; + Node *onConflictWhere = NULL; + RangeTblEntry *exclRte = NULL; + int exclRelIndex = 0; + List *exclRelTlist = NIL; + OnConflictExpr *result; + + /* Process the arbiter clause, ON CONFLICT ON (...) */ + transformOnConflictArbiter(pstate, onConflictClause, &arbiterElems, + &arbiterWhere, &arbiterConstraint); + + /* Process DO UPDATE */ + if (onConflictClause->action == ONCONFLICT_UPDATE) + { + exclRte = addRangeTableEntryForRelation(pstate, + pstate->p_target_relation, + makeAlias("excluded", NIL), + false, false); + exclRelIndex = list_length(pstate->p_rtable); + + /* + * Build a targetlist for the EXCLUDED pseudo relation. Out of + * simplicity we do that here, because expandRelAttrs() happens to + * nearly do the right thing; specifically it also works with views. + * It'd be more proper to instead scan some pseudo scan node, but it + * doesn't seem worth the amount of code required. + * + * The only caveat of this hack is that the permissions expandRelAttrs + * adds have to be reset. markVarForSelectPriv() will add the exact + * required permissions back. + */ + exclRelTlist = expandRelAttrs(pstate, exclRte, + exclRelIndex, 0, -1); + exclRte->requiredPerms = 0; + exclRte->selectedCols = NULL; + + /* + * Add EXCLUDED and the target RTE to the namespace, so that they can + * be used in the UPDATE statement. + */ + addRTEtoQuery(pstate, exclRte, false, true, true); + addRTEtoQuery(pstate, pstate->p_target_rangetblentry, + false, true, true); + + onConflictSet = + transformUpdateTargetList(pstate, onConflictClause->targetList); + + onConflictWhere = transformWhereClause(pstate, + onConflictClause->whereClause, + EXPR_KIND_WHERE, "WHERE"); + } + + /* Finally, build ON CONFLICT DO [NOTHING | UPDATE] expression */ + result = makeNode(OnConflictExpr); + + result->action = onConflictClause->action; + result->arbiterElems = arbiterElems; + result->arbiterWhere = arbiterWhere; + result->constraint = arbiterConstraint; + result->onConflictSet = onConflictSet; + result->onConflictWhere = onConflictWhere; + result->exclRelIndex = exclRelIndex; + result->exclRelTlist = exclRelTlist; + + return result; +} + + +/* * count_rowexpr_columns - * get number of columns contained in a ROW() expression; * return -1 if expression isn't a RowExpr or a Var referencing one. @@ -1899,10 +1995,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) { Query *qry = makeNode(Query); ParseNamespaceItem *nsitem; - RangeTblEntry *target_rte; Node *qual; - ListCell *origTargetList; - ListCell *tl; qry->commandType = CMD_UPDATE; pstate->p_is_update = true; @@ -1937,23 +2030,41 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) nsitem->p_lateral_only = false; nsitem->p_lateral_ok = true; - qry->targetList = transformTargetList(pstate, stmt->targetList, - EXPR_KIND_UPDATE_SOURCE); - qual = transformWhereClause(pstate, stmt->whereClause, EXPR_KIND_WHERE, "WHERE"); qry->returningList = transformReturningList(pstate, stmt->returningList); + /* + * Now we are done with SELECT-like processing, and can get on with + * transforming the target list to match the UPDATE target columns. + */ + qry->targetList = transformUpdateTargetList(pstate, stmt->targetList); + qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; - /* - * Now we are done with SELECT-like processing, and can get on with - * transforming the target list to match the UPDATE target columns. - */ + assign_query_collations(pstate, qry); + + return qry; +} + +/* + * transformUpdateTargetList - + * handle SET clause in UPDATE/INSERT ... ON CONFLICT UPDATE + */ +static List * +transformUpdateTargetList(ParseState *pstate, List *origTlist) +{ + List *tlist = NIL; + RangeTblEntry *target_rte; + ListCell *orig_tl; + ListCell *tl; + + tlist = transformTargetList(pstate, origTlist, + EXPR_KIND_UPDATE_SOURCE); /* Prepare to assign non-conflicting resnos to resjunk attributes */ if (pstate->p_next_resno <= pstate->p_target_relation->rd_rel->relnatts) @@ -1961,9 +2072,9 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) /* Prepare non-junk columns for assignment to target table */ target_rte = pstate->p_target_rangetblentry; - origTargetList = list_head(stmt->targetList); + orig_tl = list_head(origTlist); - foreach(tl, qry->targetList) + foreach(tl, tlist) { TargetEntry *tle = (TargetEntry *) lfirst(tl); ResTarget *origTarget; @@ -1981,9 +2092,9 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) tle->resname = NULL; continue; } - if (origTargetList == NULL) + if (orig_tl == NULL) elog(ERROR, "UPDATE target count mismatch --- internal error"); - origTarget = (ResTarget *) lfirst(origTargetList); + origTarget = (ResTarget *) lfirst(orig_tl); Assert(IsA(origTarget, ResTarget)); attrno = attnameAttNum(pstate->p_target_relation, @@ -2005,14 +2116,12 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) target_rte->updatedCols = bms_add_member(target_rte->updatedCols, attrno - FirstLowInvalidHeapAttributeNumber); - origTargetList = lnext(origTargetList); + orig_tl = lnext(orig_tl); } - if (origTargetList != NULL) + if (orig_tl != NULL) elog(ERROR, "UPDATE target count mismatch --- internal error"); - assign_query_collations(pstate, qry); - - return qry; + return tlist; } /* diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 0180530a30..7a4c07365c 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -217,6 +217,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); RangeVar *range; IntoClause *into; WithClause *with; + InferClause *infer; + OnConflictClause *onconflict; A_Indices *aind; ResTarget *target; struct PrivTarget *privtarget; @@ -318,7 +320,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); opt_class opt_inline_handler opt_validator validator_clause opt_collate -%type <range> qualified_name OptConstrFromTable +%type <range> qualified_name insert_target OptConstrFromTable %type <str> all_Op MathOp @@ -344,7 +346,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); OptTableElementList TableElementList OptInherit definition OptTypedTableElementList TypedTableElementList reloptions opt_reloptions - OptWith opt_distinct opt_definition func_args func_args_list + OptWith distinct_clause opt_all_clause opt_definition func_args func_args_list func_args_with_defaults func_args_with_defaults_list aggr_args aggr_args_list func_as createfunc_opt_list alterfunc_opt_list @@ -389,7 +391,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type <node> for_locking_item %type <list> for_locking_clause opt_for_locking_clause for_locking_items %type <list> locked_rels_list -%type <boolean> opt_all +%type <boolean> all_or_distinct %type <node> join_outer join_qual %type <jtype> join_type @@ -418,6 +420,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type <defelt> SeqOptElem %type <istmt> insert_rest +%type <infer> opt_conf_expr +%type <onconflict> opt_on_conflict %type <vsetstmt> generic_set set_rest set_rest_more generic_reset reset_rest SetResetClause FunctionSetResetClause @@ -557,8 +561,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE CLUSTER COALESCE COLLATE COLLATION COLUMN COMMENT COMMENTS COMMIT - COMMITTED CONCURRENTLY CONFIGURATION CONNECTION CONSTRAINT CONSTRAINTS - CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE + COMMITTED CONCURRENTLY CONFIGURATION CONFLICT CONNECTION CONSTRAINT + CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE CROSS CSV CURRENT_P CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE @@ -9436,15 +9440,35 @@ DeallocateStmt: DEALLOCATE name *****************************************************************************/ InsertStmt: - opt_with_clause INSERT INTO qualified_name insert_rest returning_clause + opt_with_clause INSERT INTO insert_target insert_rest + opt_on_conflict returning_clause { $5->relation = $4; - $5->returningList = $6; + $5->onConflictClause = $6; + $5->returningList = $7; $5->withClause = $1; $$ = (Node *) $5; } ; +/* + * Can't easily make AS optional here, because VALUES in insert_rest would + * have a shift/reduce conflict with a values as a optional alias. We could + * easily allow unreserved_keywords as optional aliases, but that'd be a odd + * divergance from other places. So just require AS for now. + */ +insert_target: + qualified_name + { + $$ = $1; + } + | qualified_name AS ColId + { + $1->alias = makeAlias($3, NIL); + $$ = $1; + } + ; + insert_rest: SelectStmt { @@ -9484,6 +9508,56 @@ insert_column_item: } ; +opt_on_conflict: + ON CONFLICT opt_conf_expr DO UPDATE SET set_clause_list where_clause + { + $$ = makeNode(OnConflictClause); + $$->action = ONCONFLICT_UPDATE; + $$->infer = $3; + $$->targetList = $7; + $$->whereClause = $8; + $$->location = @1; + } + | + ON CONFLICT opt_conf_expr DO NOTHING + { + $$ = makeNode(OnConflictClause); + $$->action = ONCONFLICT_NOTHING; + $$->infer = $3; + $$->targetList = NIL; + $$->whereClause = NULL; + $$->location = @1; + } + | /*EMPTY*/ + { + $$ = NULL; + } + ; + +opt_conf_expr: + '(' index_params ')' where_clause + { + $$ = makeNode(InferClause); + $$->indexElems = $2; + $$->whereClause = $4; + $$->conname = NULL; + $$->location = @1; + } + | + ON CONSTRAINT name + { + $$ = makeNode(InferClause); + $$->indexElems = NIL; + $$->whereClause = NULL; + $$->conname = $3; + $$->location = @1; + } + | /*EMPTY*/ + { + $$ = NULL; + } + ; + returning_clause: RETURNING target_list { $$ = $2; } | /* EMPTY */ { $$ = NIL; } @@ -9870,7 +9944,21 @@ select_clause: * However, this is not checked by the grammar; parse analysis must check it. */ simple_select: - SELECT opt_distinct opt_target_list + SELECT opt_all_clause opt_target_list + into_clause from_clause where_clause + group_clause having_clause window_clause + { + SelectStmt *n = makeNode(SelectStmt); + n->targetList = $3; + n->intoClause = $4; + n->fromClause = $5; + n->whereClause = $6; + n->groupClause = $7; + n->havingClause = $8; + n->windowClause = $9; + $$ = (Node *)n; + } + | SELECT distinct_clause target_list into_clause from_clause where_clause group_clause having_clause window_clause { @@ -9905,15 +9993,15 @@ simple_select: n->fromClause = list_make1($2); $$ = (Node *)n; } - | select_clause UNION opt_all select_clause + | select_clause UNION all_or_distinct select_clause { $$ = makeSetOp(SETOP_UNION, $3, $1, $4); } - | select_clause INTERSECT opt_all select_clause + | select_clause INTERSECT all_or_distinct select_clause { $$ = makeSetOp(SETOP_INTERSECT, $3, $1, $4); } - | select_clause EXCEPT opt_all select_clause + | select_clause EXCEPT all_or_distinct select_clause { $$ = makeSetOp(SETOP_EXCEPT, $3, $1, $4); } @@ -10052,7 +10140,8 @@ opt_table: TABLE {} | /*EMPTY*/ {} ; -opt_all: ALL { $$ = TRUE; } +all_or_distinct: + ALL { $$ = TRUE; } | DISTINCT { $$ = FALSE; } | /*EMPTY*/ { $$ = FALSE; } ; @@ -10060,10 +10149,13 @@ opt_all: ALL { $$ = TRUE; } /* We use (NIL) as a placeholder to indicate that all target expressions * should be placed in the DISTINCT list during parsetree analysis. */ -opt_distinct: +distinct_clause: DISTINCT { $$ = list_make1(NIL); } | DISTINCT ON '(' expr_list ')' { $$ = $4; } - | ALL { $$ = NIL; } + ; + +opt_all_clause: + ALL { $$ = NIL;} | /*EMPTY*/ { $$ = NIL; } ; @@ -13367,6 +13459,7 @@ unreserved_keyword: | COMMIT | COMMITTED | CONFIGURATION + | CONFLICT | CONNECTION | CONSTRAINTS | CONTENT_P diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 8d90b5098a..73c505ed85 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -16,7 +16,9 @@ #include "postgres.h" #include "access/heapam.h" +#include "catalog/catalog.h" #include "catalog/heap.h" +#include "catalog/pg_constraint.h" #include "catalog/pg_type.h" #include "commands/defrem.h" #include "nodes/makefuncs.h" @@ -32,6 +34,7 @@ #include "parser/parse_oper.h" #include "parser/parse_relation.h" #include "parser/parse_target.h" +#include "parser/parse_type.h" #include "rewrite/rewriteManip.h" #include "utils/guc.h" #include "utils/lsyscache.h" @@ -75,6 +78,8 @@ static TargetEntry *findTargetlistEntrySQL99(ParseState *pstate, Node *node, List **tlist, ParseExprKind exprKind); static int get_matching_location(int sortgroupref, List *sortgrouprefs, List *exprs); +static List *resolve_unique_index_expr(ParseState *pstate, InferClause * infer, + Relation heapRel); static List *addTargetToGroupList(ParseState *pstate, TargetEntry *tle, List *grouplist, List *targetlist, int location, bool resolveUnknown); @@ -2167,6 +2172,204 @@ get_matching_location(int sortgroupref, List *sortgrouprefs, List *exprs) } /* + * resolve_unique_index_expr + * Infer a unique index from a list of indexElems, for ON + * CONFLICT clause + * + * Perform parse analysis of expressions and columns appearing within ON + * CONFLICT clause. During planning, the returned list of expressions is used + * to infer which unique index to use. + */ +static List * +resolve_unique_index_expr(ParseState *pstate, InferClause *infer, + Relation heapRel) +{ + List *result = NIL; + ListCell *l; + + foreach(l, infer->indexElems) + { + IndexElem *ielem = (IndexElem *) lfirst(l); + InferenceElem *pInfer = makeNode(InferenceElem); + Node *parse; + + /* + * Raw grammar re-uses CREATE INDEX infrastructure for unique index + * inference clause, and so will accept opclasses by name and so on. + * + * Make no attempt to match ASC or DESC ordering or NULLS FIRST/NULLS + * LAST ordering, since those are not significant for inference + * purposes (any unique index matching the inference specification in + * other regards is accepted indifferently). Actively reject this as + * wrong-headed. + */ + if (ielem->ordering != SORTBY_DEFAULT) + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), + errmsg("ASC/DESC is not allowed in ON CONFLICT clause"), + parser_errposition(pstate, + exprLocation((Node *) infer)))); + if (ielem->nulls_ordering != SORTBY_NULLS_DEFAULT) + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), + errmsg("NULLS FIRST/LAST is not allowed in ON CONFLICT clause"), + parser_errposition(pstate, + exprLocation((Node *) infer)))); + + if (!ielem->expr) + { + /* Simple index attribute */ + ColumnRef *n; + + /* + * Grammar won't have built raw expression for us in event of + * plain column reference. Create one directly, and perform + * expression transformation. Planner expects this, and performs + * its own normalization for the purposes of matching against + * pg_index. + */ + n = makeNode(ColumnRef); + n->fields = list_make1(makeString(ielem->name)); + /* Location is approximately that of inference specification */ + n->location = infer->location; + parse = (Node *) n; + } + else + { + /* Do parse transformation of the raw expression */ + parse = (Node *) ielem->expr; + } + + /* + * transformExpr() should have already rejected subqueries, + * aggregates, and window functions, based on the EXPR_KIND_ for an + * index expression. Expressions returning sets won't have been + * rejected, but don't bother doing so here; there should be no + * available expression unique index to match any such expression + * against anyway. + */ + pInfer->expr = transformExpr(pstate, parse, EXPR_KIND_INDEX_EXPRESSION); + + /* Perform lookup of collation and operator class as required */ + if (!ielem->collation) + pInfer->infercollid = InvalidOid; + else + pInfer->infercollid = LookupCollation(pstate, ielem->collation, + exprLocation(pInfer->expr)); + + if (!ielem->opclass) + { + pInfer->inferopfamily = InvalidOid; + pInfer->inferopcinputtype = InvalidOid; + } + else + { + Oid opclass = get_opclass_oid(BTREE_AM_OID, ielem->opclass, + false); + + pInfer->inferopfamily = get_opclass_family(opclass); + pInfer->inferopcinputtype = get_opclass_input_type(opclass); + } + + result = lappend(result, pInfer); + } + + return result; +} + +/* + * transformOnConflictArbiter - + * transform arbiter expressions in an ON CONFLICT clause. + * + * Transformed expressions used to infer one unique index relation to serve as + * an ON CONFLICT arbiter. Partial unique indexes may be inferred using WHERE + * clause from inference specification clause. + */ +void +transformOnConflictArbiter(ParseState *pstate, + OnConflictClause *onConflictClause, + List **arbiterExpr, Node **arbiterWhere, + Oid *constraint) +{ + InferClause *infer = onConflictClause->infer; + + *arbiterExpr = NIL; + *arbiterWhere = NULL; + *constraint = InvalidOid; + + if (onConflictClause->action == ONCONFLICT_UPDATE && !infer) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ON CONFLICT DO UPDATE requires inference specification or constraint name"), + errhint("For example, ON CONFLICT ON CONFLICT (<column>)."), + parser_errposition(pstate, + exprLocation((Node *) onConflictClause)))); + + /* + * To simplify certain aspects of its design, speculative insertion into + * system catalogs is disallowed + */ + if (IsCatalogRelation(pstate->p_target_relation)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("ON CONFLICT not supported with system catalog tables"), + parser_errposition(pstate, + exprLocation((Node *) onConflictClause)))); + + /* Same applies to table used by logical decoding as catalog table */ + if (RelationIsUsedAsCatalogTable(pstate->p_target_relation)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("ON CONFLICT not supported on table \"%s\" used as a catalog table", + RelationGetRelationName(pstate->p_target_relation)), + parser_errposition(pstate, + exprLocation((Node *) onConflictClause)))); + + /* ON CONFLICT DO NOTHING does not require an inference clause */ + if (infer) + { + List *save_namespace; + + /* + * While we process the arbiter expressions, accept only + * non-qualified references to the target table. Hide any other + * relations. + */ + save_namespace = pstate->p_namespace; + pstate->p_namespace = NIL; + addRTEtoQuery(pstate, pstate->p_target_rangetblentry, + false, false, true); + + if (infer->indexElems) + *arbiterExpr = resolve_unique_index_expr(pstate, infer, + pstate->p_target_relation); + + /* + * Handling inference WHERE clause (for partial unique index + * inference) + */ + if (infer->whereClause) + *arbiterWhere = transformExpr(pstate, infer->whereClause, + EXPR_KIND_INDEX_PREDICATE); + + pstate->p_namespace = save_namespace; + + if (infer->conname) + *constraint = get_relation_constraint_oid(RelationGetRelid(pstate->p_target_relation), + infer->conname, false); + } + + /* + * It's convenient to form a list of expressions based on the + * representation used by CREATE INDEX, since the same restrictions are + * appropriate (e.g. on subqueries). However, from here on, a dedicated + * primnode representation is used for inference elements, and so + * assign_query_collations() can be trusted to do the right thing with the + * post parse analysis query tree inference clause representation. + */ +} + +/* * addTargetToSortList * If the given targetlist entry isn't already in the SortGroupClause * list, add it to the end of the list, using the given sort ordering diff --git a/src/backend/parser/parse_collate.c b/src/backend/parser/parse_collate.c index 7c6a11c757..4c85b708d3 100644 --- a/src/backend/parser/parse_collate.c +++ b/src/backend/parser/parse_collate.c @@ -479,9 +479,11 @@ assign_collations_walker(Node *node, assign_collations_context *context) parser_errposition(context->pstate, loccontext.location2))); break; + case T_InferenceElem: case T_RangeTblRef: case T_JoinExpr: case T_FromExpr: + case T_OnConflictExpr: case T_SortGroupClause: (void) expression_tree_walker(node, assign_collations_walker, diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 2d85cf08e7..59973ba9c3 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -537,11 +537,12 @@ transformAssignedExpr(ParseState *pstate, /* * updateTargetListEntry() - * This is used in UPDATE statements only. It prepares an UPDATE - * TargetEntry for assignment to a column of the target table. - * This includes coercing the given value to the target column's type - * (if necessary), and dealing with any subfield names or subscripts - * attached to the target column itself. + * This is used in UPDATE statements (and ON CONFLICT DO UPDATE) + * only. It prepares an UPDATE TargetEntry for assignment to a + * column of the target table. This includes coercing the given + * value to the target column's type (if necessary), and dealing with + * any subfield names or subscripts attached to the target column + * itself. * * pstate parse state * tle target list entry to be modified |