diff options
author | Pavan Deolasee | 2017-06-14 05:42:18 +0000 |
---|---|---|
committer | Pavan Deolasee | 2017-06-14 05:42:18 +0000 |
commit | 15dd5274c323fb93e4e3ea9ad2185aaaec10f79c (patch) | |
tree | 9dafb4c7f735d9429ea461dc792933af87493c33 /src/backend/parser | |
parent | dfbb88e3bbb526dcb204b456b9e5cfd9d10d0d0a (diff) | |
parent | d5cb3bab564e0927ffac7c8729eacf181a12dd40 (diff) |
Merge from PG master upto d5cb3bab564e0927ffac7c8729eacf181a12dd40
This is the result of the "git merge remotes/PGSQL/master" upto the said commit
point. We have done some basic analysis, fixed compilation problems etc, but
bulk of the logical problems in conflict resolution etc will be handled by
subsequent commits.
Diffstat (limited to 'src/backend/parser')
-rw-r--r-- | src/backend/parser/Makefile | 12 | ||||
-rw-r--r-- | src/backend/parser/analyze.c | 308 | ||||
-rw-r--r-- | src/backend/parser/check_keywords.pl | 33 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 2702 | ||||
-rw-r--r-- | src/backend/parser/parse_agg.c | 18 | ||||
-rw-r--r-- | src/backend/parser/parse_clause.c | 384 | ||||
-rw-r--r-- | src/backend/parser/parse_coerce.c | 70 | ||||
-rw-r--r-- | src/backend/parser/parse_collate.c | 19 | ||||
-rw-r--r-- | src/backend/parser/parse_cte.c | 13 | ||||
-rw-r--r-- | src/backend/parser/parse_enr.c | 29 | ||||
-rw-r--r-- | src/backend/parser/parse_expr.c | 304 | ||||
-rw-r--r-- | src/backend/parser/parse_func.c | 228 | ||||
-rw-r--r-- | src/backend/parser/parse_node.c | 8 | ||||
-rw-r--r-- | src/backend/parser/parse_oper.c | 30 | ||||
-rw-r--r-- | src/backend/parser/parse_param.c | 4 | ||||
-rw-r--r-- | src/backend/parser/parse_relation.c | 321 | ||||
-rw-r--r-- | src/backend/parser/parse_target.c | 170 | ||||
-rw-r--r-- | src/backend/parser/parse_type.c | 7 | ||||
-rw-r--r-- | src/backend/parser/parse_utilcmd.c | 845 | ||||
-rw-r--r-- | src/backend/parser/parser.c | 5 | ||||
-rw-r--r-- | src/backend/parser/scan.l | 9 | ||||
-rw-r--r-- | src/backend/parser/scansup.c | 2 |
22 files changed, 3996 insertions, 1525 deletions
diff --git a/src/backend/parser/Makefile b/src/backend/parser/Makefile index fdd8485cec..4b97f83803 100644 --- a/src/backend/parser/Makefile +++ b/src/backend/parser/Makefile @@ -14,18 +14,13 @@ override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS) OBJS= analyze.o gram.o scan.o parser.o \ parse_agg.o parse_clause.o parse_coerce.o parse_collate.o parse_cte.o \ - parse_expr.o parse_func.o parse_node.o parse_oper.o parse_param.o \ - parse_relation.o parse_target.o parse_type.o parse_utilcmd.o scansup.o + parse_enr.o parse_expr.o parse_func.o parse_node.o parse_oper.o \ + parse_param.o parse_relation.o parse_target.o parse_type.o \ + parse_utilcmd.o scansup.o include $(top_srcdir)/src/backend/common.mk -# Latest flex causes warnings in this file. -ifeq ($(GCC),yes) -scan.o: CFLAGS += -Wno-error -endif - - # There is no correct way to write a rule that generates two files. # Rules with two targets don't have that meaning, they are merely # shorthand for two otherwise separate rules. To be safe for parallel @@ -41,6 +36,7 @@ gram.c: BISON_CHECK_CMD = $(PERL) $(srcdir)/check_keywords.pl $< $(top_srcdir)/s scan.c: FLEXFLAGS = -CF -p -p scan.c: FLEX_NO_BACKUP=yes +scan.c: FLEX_FIX_WARNING=yes # Force these dependencies to be known even without dependency info built: diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 90603dd5e5..020d6f74c4 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -15,7 +15,7 @@ * * * Portions Copyright (c) 2012-2014, TransLattice, Inc. - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * src/backend/parser/analyze.c @@ -74,6 +74,7 @@ /* Hook for plugins to get control at end of parse analysis */ post_parse_analyze_hook_type post_parse_analyze_hook = NULL; +static Query *transformOptionalSelectInto(ParseState *pstate, Node *parseTree); static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt); static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt); static List *transformInsertRow(ParseState *pstate, List *exprlist, @@ -126,8 +127,9 @@ static void ParseAnalyze_substitute_func(FuncExpr *funcexpr); * a dummy CMD_UTILITY Query node. */ Query * -parse_analyze(Node *parseTree, const char *sourceText, - Oid *paramTypes, int numParams) +parse_analyze(RawStmt *parseTree, const char *sourceText, + Oid *paramTypes, int numParams, + QueryEnvironment *queryEnv) { ParseState *pstate = make_parsestate(NULL); Query *query; @@ -139,6 +141,8 @@ parse_analyze(Node *parseTree, const char *sourceText, if (numParams > 0) parse_fixed_parameters(pstate, paramTypes, numParams); + pstate->p_queryEnv = queryEnv; + query = transformTopLevelStmt(pstate, parseTree); if (post_parse_analyze_hook) @@ -157,7 +161,7 @@ parse_analyze(Node *parseTree, const char *sourceText, * be modified or enlarged (via repalloc). */ Query * -parse_analyze_varparams(Node *parseTree, const char *sourceText, +parse_analyze_varparams(RawStmt *parseTree, const char *sourceText, Oid **paramTypes, int *numParams) { ParseState *pstate = make_parsestate(NULL); @@ -189,13 +193,15 @@ parse_analyze_varparams(Node *parseTree, const char *sourceText, Query * parse_sub_analyze(Node *parseTree, ParseState *parentParseState, CommonTableExpr *parentCTE, - bool locked_from_parent) + bool locked_from_parent, + bool resolve_unknowns) { ParseState *pstate = make_parsestate(parentParseState); Query *query; pstate->p_parent_cte = parentCTE; pstate->p_locked_from_parent = locked_from_parent; + pstate->p_resolve_unknowns = resolve_unknowns; query = transformStmt(pstate, parseTree); @@ -208,14 +214,35 @@ parse_sub_analyze(Node *parseTree, ParseState *parentParseState, * transformTopLevelStmt - * transform a Parse tree into a Query tree. * + * This function is just responsible for transferring statement location data + * from the RawStmt into the finished Query. + */ +Query * +transformTopLevelStmt(ParseState *pstate, RawStmt *parseTree) +{ + Query *result; + + /* We're at top level, so allow SELECT INTO */ + result = transformOptionalSelectInto(pstate, parseTree->stmt); + + result->stmt_location = parseTree->stmt_location; + result->stmt_len = parseTree->stmt_len; + + return result; +} + +/* + * transformOptionalSelectInto - + * If SELECT has INTO, convert it to CREATE TABLE AS. + * * The only thing we do here that we don't do in transformStmt() is to * convert SELECT ... INTO into CREATE TABLE AS. Since utility statements * aren't allowed within larger statements, this is only allowed at the top * of the parse tree, and so we only try it before entering the recursive * transformStmt() processing. */ -Query * -transformTopLevelStmt(ParseState *pstate, Node *parseTree) +static Query * +transformOptionalSelectInto(ParseState *pstate, Node *parseTree) { if (IsA(parseTree, SelectStmt)) { @@ -359,11 +386,11 @@ transformStmt(ParseState *pstate, Node *parseTree) * Classification here should match transformStmt(). */ bool -analyze_requires_snapshot(Node *parseTree) +analyze_requires_snapshot(RawStmt *parseTree) { bool result; - switch (nodeTag(parseTree)) + switch (nodeTag(parseTree->stmt)) { /* * Optimizable statements @@ -379,10 +406,6 @@ analyze_requires_snapshot(Node *parseTree) * Special cases */ case T_DeclareCursorStmt: - /* yes, because it's analyzed just like SELECT */ - result = true; - break; - case T_ExplainStmt: case T_CreateTableAsStmt: /* yes, because we must analyze the contained statement */ @@ -432,7 +455,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) /* set up range table with just the result rel */ qry->resultRelation = setTargetTable(pstate, stmt->relation, - interpretInhOption(stmt->relation->inhOpt), + stmt->relation->inh, true, ACL_DELETE); @@ -469,6 +492,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; + qry->hasTargetSRFs = pstate->p_hasTargetSRFs; qry->hasAggs = pstate->p_hasAggs; if (pstate->p_hasAggs) parseCheckAggregates(pstate, qry); @@ -515,6 +539,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) qry->hasModifyingCTE = pstate->p_hasModifyingCTE; } + qry->override = stmt->override; + isOnConflictUpdate = (stmt->onConflictClause && stmt->onConflictClause->action == ONCONFLICT_UPDATE); @@ -603,10 +629,17 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * otherwise the behavior of SELECT within INSERT might be different * from a stand-alone SELECT. (Indeed, Postgres up through 6.5 had * bugs of just that nature...) + * + * The sole exception is that we prevent resolving unknown-type + * outputs as TEXT. This does not change the semantics since if the + * column type matters semantically, it would have been resolved to + * something else anyway. Doing this lets us resolve such outputs as + * the target column's type, which we handle below. */ sub_pstate->p_rtable = sub_rtable; sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ sub_pstate->p_namespace = sub_namespace; + sub_pstate->p_resolve_unknowns = false; selectQuery = transformStmt(sub_pstate, stmt->selectStmt); @@ -614,8 +647,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* The grammar should have produced a SELECT */ if (!IsA(selectQuery, Query) || - selectQuery->commandType != CMD_SELECT || - selectQuery->utilityStmt != NULL) + selectQuery->commandType != CMD_SELECT) elog(ERROR, "unexpected non-SELECT command in INSERT ... SELECT"); /* @@ -684,10 +716,11 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * RTE. */ List *exprsLists = NIL; - List *collations = NIL; + List *coltypes = NIL; + List *coltypmods = NIL; + List *colcollations = NIL; int sublist_length = -1; bool lateral = false; - int i; Assert(selectStmt->intoClause == NULL); @@ -695,8 +728,12 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) { List *sublist = (List *) lfirst(lc); - /* Do basic expression transformation (same as a ROW() expr) */ - sublist = transformExpressionList(pstate, sublist, EXPR_KIND_VALUES); + /* + * Do basic expression transformation (same as a ROW() expr, but + * allow SetToDefault at top level) + */ + sublist = transformExpressionList(pstate, sublist, + EXPR_KIND_VALUES, true); /* * All the sublists must be the same length, *after* @@ -750,11 +787,20 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) } /* - * Although we don't really need collation info, let's just make sure - * we provide a correctly-sized list in the VALUES RTE. + * Construct column type/typmod/collation lists for the VALUES RTE. + * Every expression in each column has been coerced to the type/typmod + * of the corresponding target column or subfield, so it's sufficient + * to look at the exprType/exprTypmod of the first row. We don't care + * about the collation labeling, so just fill in InvalidOid for that. */ - for (i = 0; i < sublist_length; i++) - collations = lappend_oid(collations, InvalidOid); + foreach(lc, (List *) linitial(exprsLists)) + { + Node *val = (Node *) lfirst(lc); + + coltypes = lappend_oid(coltypes, exprType(val)); + coltypmods = lappend_int(coltypmods, exprTypmod(val)); + colcollations = lappend_oid(colcollations, InvalidOid); + } /* * Ordinarily there can't be any current-level Vars in the expression @@ -769,7 +815,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* * Generate the VALUES RTE */ - rte = addRangeTableEntryForValues(pstate, exprsLists, collations, + rte = addRangeTableEntryForValues(pstate, exprsLists, + coltypes, coltypmods, colcollations, NULL, lateral, true); rtr = makeNode(RangeTblRef); /* assume new rte is at end */ @@ -803,10 +850,14 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) Assert(list_length(valuesLists) == 1); Assert(selectStmt->intoClause == NULL); - /* Do basic expression transformation (same as a ROW() expr) */ + /* + * Do basic expression transformation (same as a ROW() expr, but allow + * SetToDefault at top level) + */ exprList = transformExpressionList(pstate, (List *) linitial(valuesLists), - EXPR_KIND_VALUES); + EXPR_KIND_VALUES_SINGLE, + true); /* Prepare row for assignment to target table */ exprList = transformInsertRow(pstate, exprList, @@ -830,8 +881,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) AttrNumber attr_num; TargetEntry *tle; - col = (ResTarget *) lfirst(icols); - Assert(IsA(col, ResTarget)); + col = lfirst_node(ResTarget, icols); attr_num = (AttrNumber) lfirst_int(attnos); tle = makeTargetEntry(expr, @@ -849,8 +899,16 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* Process ON CONFLICT, if any. */ if (stmt->onConflictClause) + { + /* Bail out if target relation is partitioned table */ + if (pstate->p_target_rangetblentry->relkind == RELKIND_PARTITIONED_TABLE) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("ON CONFLICT clause is not supported with partitioned tables"))); + qry->onConflict = transformOnConflictClause(pstate, stmt->onConflictClause); + } /* * If we have a RETURNING clause, we need to add the target relation to @@ -871,6 +929,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); + qry->hasTargetSRFs = pstate->p_hasTargetSRFs; qry->hasSubLinks = pstate->p_hasSubLinks; assign_query_collations(pstate, qry); @@ -947,8 +1006,7 @@ transformInsertRow(ParseState *pstate, List *exprlist, Expr *expr = (Expr *) lfirst(lc); ResTarget *col; - col = (ResTarget *) lfirst(icols); - Assert(IsA(col, ResTarget)); + col = lfirst_node(ResTarget, icols); expr = transformAssignedExpr(pstate, expr, EXPR_KIND_INSERT_TARGET, @@ -990,7 +1048,7 @@ transformInsertRow(ParseState *pstate, List *exprlist, } /* - * transformSelectStmt - + * transformOnConflictClause - * transforms an OnConflictClause in an INSERT */ static OnConflictExpr * @@ -1038,11 +1096,10 @@ transformOnConflictClause(ParseState *pstate, exclRelIndex = list_length(pstate->p_rtable); /* - * Build a targetlist for the EXCLUDED pseudo relation. Have to be - * careful to use resnos that correspond to attnos of the underlying - * relation. + * Build a targetlist representing the columns of the EXCLUDED pseudo + * relation. Have to be careful to use resnos that correspond to + * attnos of the underlying relation. */ - Assert(pstate->p_next_resno == 1); for (attno = 0; attno < targetrel->rd_rel->relnatts; attno++) { Form_pg_attribute attr = targetrel->rd_att->attrs[attno]; @@ -1063,14 +1120,11 @@ transformOnConflictClause(ParseState *pstate, attr->atttypid, attr->atttypmod, attr->attcollation, 0); - var->location = -1; - - name = NameStr(attr->attname); + name = pstrdup(NameStr(attr->attname)); } - Assert(pstate->p_next_resno == attno + 1); te = makeTargetEntry((Expr *) var, - pstate->p_next_resno++, + attno + 1, name, false); @@ -1079,15 +1133,16 @@ transformOnConflictClause(ParseState *pstate, } /* - * Additionally add a whole row tlist entry for EXCLUDED. That's - * really only needed for ruleutils' benefit, which expects to find - * corresponding entries in child tlists. Alternatively we could do - * this only when required, but that doesn't seem worth the trouble. + * Add a whole-row-Var entry to support references to "EXCLUDED.*". + * Like the other entries in exclRelTlist, its resno must match the + * Var's varattno, else the wrong things happen while resolving + * references in setrefs.c. This is against normal conventions for + * targetlists, but it's okay since we don't use this as a real tlist. */ var = makeVar(exclRelIndex, InvalidAttrNumber, - RelationGetRelid(targetrel), + targetrel->rd_rel->reltype, -1, InvalidOid, 0); - te = makeTargetEntry((Expr *) var, 0, NULL, true); + te = makeTargetEntry((Expr *) var, InvalidAttrNumber, NULL, true); exclRelTlist = lappend(exclRelTlist, te); /* @@ -1232,7 +1287,6 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) stmt->sortClause, &qry->targetList, EXPR_KIND_ORDER_BY, - true /* fix unknowns */ , false /* allow SQL92 rules */ ); qry->groupClause = transformGroupClause(pstate, @@ -1278,11 +1332,16 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) pstate->p_windowdefs, &qry->targetList); + /* resolve any still-unresolved output columns as being type text */ + if (pstate->p_resolve_unknowns) + resolveTargetListUnknowns(pstate, qry->targetList); + qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; + qry->hasTargetSRFs = pstate->p_hasTargetSRFs; qry->hasAggs = pstate->p_hasAggs; if (pstate->p_hasAggs || qry->groupClause || qry->groupingSets || qry->havingQual) parseCheckAggregates(pstate, qry); @@ -1310,7 +1369,9 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) { Query *qry = makeNode(Query); List *exprsLists; - List *collations; + List *coltypes = NIL; + List *coltypmods = NIL; + List *colcollations = NIL; List **colexprs = NULL; int sublist_length = -1; bool lateral = false; @@ -1342,9 +1403,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) } /* - * For each row of VALUES, transform the raw expressions. This is also a - * handy place to reject DEFAULT nodes, which the grammar allows for - * simplicity. + * For each row of VALUES, transform the raw expressions. * * Note that the intermediate representation we build is column-organized * not row-organized. That simplifies the type and collation processing @@ -1354,8 +1413,12 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) { List *sublist = (List *) lfirst(lc); - /* Do basic expression transformation (same as a ROW() expr) */ - sublist = transformExpressionList(pstate, sublist, EXPR_KIND_VALUES); + /* + * Do basic expression transformation (same as a ROW() expr, but here + * we disallow SetToDefault) + */ + sublist = transformExpressionList(pstate, sublist, + EXPR_KIND_VALUES, false); /* * All the sublists must be the same length, *after* transformation @@ -1378,17 +1441,12 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) exprLocation((Node *) sublist)))); } - /* Check for DEFAULT and build per-column expression lists */ + /* Build per-column expression lists */ i = 0; foreach(lc2, sublist) { Node *col = (Node *) lfirst(lc2); - if (IsA(col, SetToDefault)) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("DEFAULT can only appear in a VALUES list within INSERT"), - parser_errposition(pstate, exprLocation(col)))); colexprs[i] = lappend(colexprs[i], col); i++; } @@ -1399,8 +1457,8 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) /* * Now resolve the common types of the columns, and coerce everything to - * those types. Then identify the common collation, if any, of each - * column. + * those types. Then identify the common typmod and common collation, if + * any, of each column. * * We must do collation processing now because (1) assign_query_collations * doesn't process rangetable entries, and (2) we need to label the VALUES @@ -1411,11 +1469,12 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) * * Note we modify the per-column expression lists in-place. */ - collations = NIL; for (i = 0; i < sublist_length; i++) { Oid coltype; + int32 coltypmod = -1; Oid colcoll; + bool first = true; coltype = select_common_type(pstate, colexprs[i], "VALUES", NULL); @@ -1425,11 +1484,24 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) col = coerce_to_common_type(pstate, col, coltype, "VALUES"); lfirst(lc) = (void *) col; + if (first) + { + coltypmod = exprTypmod(col); + first = false; + } + else + { + /* As soon as we see a non-matching typmod, fall back to -1 */ + if (coltypmod >= 0 && coltypmod != exprTypmod(col)) + coltypmod = -1; + } } colcoll = select_common_collation(pstate, colexprs[i], true); - collations = lappend_oid(collations, colcoll); + coltypes = lappend_oid(coltypes, coltype); + coltypmods = lappend_int(coltypmods, coltypmod); + colcollations = lappend_oid(colcollations, colcoll); } /* @@ -1471,7 +1543,8 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) /* * Generate the VALUES RTE */ - rte = addRangeTableEntryForValues(pstate, exprsLists, collations, + rte = addRangeTableEntryForValues(pstate, exprsLists, + coltypes, coltypmods, colcollations, NULL, lateral, true); addRTEtoQuery(pstate, rte, true, true, true); @@ -1493,7 +1566,6 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) stmt->sortClause, &qry->targetList, EXPR_KIND_ORDER_BY, - true /* fix unknowns */ , false /* allow SQL92 rules */ ); qry->limitOffset = transformLimitClause(pstate, stmt->limitOffset, @@ -1616,10 +1688,9 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) /* * Recursively transform the components of the tree. */ - sostmt = (SetOperationStmt *) transformSetOperationTree(pstate, stmt, - true, - NULL); - Assert(sostmt && IsA(sostmt, SetOperationStmt)); + sostmt = castNode(SetOperationStmt, + transformSetOperationTree(pstate, stmt, true, NULL)); + Assert(sostmt); qry->setOperations = (Node *) sostmt; /* @@ -1717,7 +1788,6 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) sortClause, &qry->targetList, EXPR_KIND_ORDER_BY, - false /* no unknowns expected */ , false /* allow SQL92 rules */ ); /* restore namespace, remove jrte from rtable */ @@ -1743,6 +1813,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; + qry->hasTargetSRFs = pstate->p_hasTargetSRFs; qry->hasAggs = pstate->p_hasAggs; if (pstate->p_hasAggs || qry->groupClause || qry->groupingSets || qry->havingQual) parseCheckAggregates(pstate, qry); @@ -1836,11 +1907,19 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, /* * Transform SelectStmt into a Query. * + * This works the same as SELECT transformation normally would, except + * that we prevent resolving unknown-type outputs as TEXT. This does + * not change the subquery's semantics since if the column type + * matters semantically, it would have been resolved to something else + * anyway. Doing this lets us resolve such outputs using + * select_common_type(), below. + * * Note: previously transformed sub-queries don't affect the parsing * of this sub-query, because they are not in the toplevel pstate's * namespace list. */ - selectQuery = parse_sub_analyze((Node *) stmt, pstate, NULL, false); + selectQuery = parse_sub_analyze((Node *) stmt, pstate, + NULL, false, false); /* * Check for bogus references to Vars on the current query level (but @@ -2187,7 +2266,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) } qry->resultRelation = setTargetTable(pstate, stmt->relation, - interpretInhOption(stmt->relation->inhOpt), + stmt->relation->inh, true, ACL_UPDATE); @@ -2222,6 +2301,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); + qry->hasTargetSRFs = pstate->p_hasTargetSRFs; qry->hasSubLinks = pstate->p_hasSubLinks; assign_query_collations(pstate, qry); @@ -2272,8 +2352,7 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) } if (orig_tl == NULL) elog(ERROR, "UPDATE target count mismatch --- internal error"); - origTarget = (ResTarget *) lfirst(orig_tl); - Assert(IsA(origTarget, ResTarget)); + origTarget = lfirst_node(ResTarget, orig_tl); attrno = attnameAttNum(pstate->p_target_relation, origTarget->name, true); @@ -2342,6 +2421,10 @@ transformReturningList(ParseState *pstate, List *returningList) /* mark column origins */ markTargetListOrigins(pstate, rlist); + /* resolve any still-unresolved output columns as being type text */ + if (pstate->p_resolve_unknowns) + resolveTargetListUnknowns(pstate, rlist); + /* restore state */ pstate->p_next_resno = save_next_resno; @@ -2353,17 +2436,17 @@ transformReturningList(ParseState *pstate, List *returningList) * transformDeclareCursorStmt - * transform a DECLARE CURSOR Statement * - * DECLARE CURSOR is a hybrid case: it's an optimizable statement (in fact not - * significantly different from a SELECT) as far as parsing/rewriting/planning - * are concerned, but it's not passed to the executor and so in that sense is - * a utility statement. We transform it into a Query exactly as if it were - * a SELECT, then stick the original DeclareCursorStmt into the utilityStmt - * field to carry the cursor name and options. + * DECLARE CURSOR is like other utility statements in that we emit it as a + * CMD_UTILITY Query node; however, we must first transform the contained + * query. We used to postpone that until execution, but it's really necessary + * to do it during the normal parse analysis phase to ensure that side effects + * of parser hooks happen at the expected time. */ static Query * transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt) { Query *result; + Query *query; /* * Don't allow both SCROLL and NO SCROLL to be specified @@ -2374,12 +2457,13 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt) (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), errmsg("cannot specify both SCROLL and NO SCROLL"))); - result = transformStmt(pstate, stmt->query); + /* Transform contained query, not allowing SELECT INTO */ + query = transformStmt(pstate, stmt->query); + stmt->query = (Node *) query; /* Grammar should not have allowed anything but SELECT */ - if (!IsA(result, Query) || - result->commandType != CMD_SELECT || - result->utilityStmt != NULL) + if (!IsA(query, Query) || + query->commandType != CMD_SELECT) elog(ERROR, "unexpected non-SELECT command in DECLARE CURSOR"); /* @@ -2387,47 +2471,47 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt) * allowed, but the semantics of when the updates occur might be * surprising.) */ - if (result->hasModifyingCTE) + if (query->hasModifyingCTE) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("DECLARE CURSOR must not contain data-modifying statements in WITH"))); /* FOR UPDATE and WITH HOLD are not compatible */ - if (result->rowMarks != NIL && (stmt->options & CURSOR_OPT_HOLD)) + if (query->rowMarks != NIL && (stmt->options & CURSOR_OPT_HOLD)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), /*------ translator: %s is a SQL row locking clause such as FOR UPDATE */ errmsg("DECLARE CURSOR WITH HOLD ... %s is not supported", LCS_asString(((RowMarkClause *) - linitial(result->rowMarks))->strength)), + linitial(query->rowMarks))->strength)), errdetail("Holdable cursors must be READ ONLY."))); /* FOR UPDATE and SCROLL are not compatible */ - if (result->rowMarks != NIL && (stmt->options & CURSOR_OPT_SCROLL)) + if (query->rowMarks != NIL && (stmt->options & CURSOR_OPT_SCROLL)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), /*------ translator: %s is a SQL row locking clause such as FOR UPDATE */ errmsg("DECLARE SCROLL CURSOR ... %s is not supported", LCS_asString(((RowMarkClause *) - linitial(result->rowMarks))->strength)), + linitial(query->rowMarks))->strength)), errdetail("Scrollable cursors must be READ ONLY."))); /* FOR UPDATE and INSENSITIVE are not compatible */ - if (result->rowMarks != NIL && (stmt->options & CURSOR_OPT_INSENSITIVE)) + if (query->rowMarks != NIL && (stmt->options & CURSOR_OPT_INSENSITIVE)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), /*------ translator: %s is a SQL row locking clause such as FOR UPDATE */ errmsg("DECLARE INSENSITIVE CURSOR ... %s is not supported", LCS_asString(((RowMarkClause *) - linitial(result->rowMarks))->strength)), + linitial(query->rowMarks))->strength)), errdetail("Insensitive cursors must be READ ONLY."))); - /* We won't need the raw querytree any more */ - stmt->query = NULL; - + /* represent the command as a utility Query */ + result = makeNode(Query); + result->commandType = CMD_UTILITY; result->utilityStmt = (Node *) stmt; return result; @@ -2450,7 +2534,7 @@ transformExplainStmt(ParseState *pstate, ExplainStmt *stmt) Query *result; /* transform contained query, allowing SELECT INTO */ - stmt->query = (Node *) transformTopLevelStmt(pstate, stmt->query); + stmt->query = (Node *) transformOptionalSelectInto(pstate, stmt->query); /* represent the command as a utility Query */ result = makeNode(Query); @@ -2466,7 +2550,7 @@ transformExplainStmt(ParseState *pstate, ExplainStmt *stmt) * transform a CREATE TABLE AS, SELECT ... INTO, or CREATE MATERIALIZED VIEW * Statement * - * As with EXPLAIN, transform the contained statement now. + * As with DECLARE CURSOR and EXPLAIN, transform the contained statement now. */ static Query * transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt) @@ -2474,7 +2558,7 @@ transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt) Query *result; Query *query; - /* transform contained query */ + /* transform contained query, not allowing SELECT INTO */ query = transformStmt(pstate, stmt->query); stmt->query = (Node *) query; @@ -2529,7 +2613,7 @@ transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt) * in the IntoClause because that's where intorel_startup() can * conveniently get it from. */ - stmt->into->viewQuery = copyObject(query); + stmt->into->viewQuery = (Node *) copyObject(query); } /* represent the command as a utility Query */ @@ -2622,8 +2706,9 @@ transformExecDirectStmt(ParseState *pstate, ExecDirectStmt *stmt) */ foreach(raw_parsetree_item, raw_parsetree_list) { - Node *parsetree = (Node *) lfirst(raw_parsetree_item); - result = parse_analyze(parsetree, query, NULL, 0); + RawStmt *parsetree = lfirst_node(RawStmt, raw_parsetree_item); + List *result_list = pg_analyze_and_rewrite(parsetree, query, NULL, 0, NULL); + result = linitial_node(Query, result_list); } /* Default list of parameters to set */ @@ -2784,8 +2869,7 @@ CheckSelectLocking(Query *qry, LockClauseStrength strength) translator: %s is a SQL row locking clause such as FOR UPDATE */ errmsg("%s is not allowed with window functions", LCS_asString(strength)))); - - if (expression_returns_set((Node *) qry->targetList)) + if (qry->hasTargetSRFs) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), /*------ @@ -2913,6 +2997,15 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, LCS_asString(lc->strength)), parser_errposition(pstate, thisrel->location))); break; + case RTE_TABLEFUNC: + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s cannot be applied to a table function", + LCS_asString(lc->strength)), + parser_errposition(pstate, thisrel->location))); + break; case RTE_VALUES: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), @@ -2931,6 +3024,15 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, LCS_asString(lc->strength)), parser_errposition(pstate, thisrel->location))); break; + case RTE_NAMEDTUPLESTORE: + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s cannot be applied to a named tuplestore", + LCS_asString(lc->strength)), + parser_errposition(pstate, thisrel->location))); + break; default: elog(ERROR, "unrecognized RTE type: %d", (int) rte->rtekind); diff --git a/src/backend/parser/check_keywords.pl b/src/backend/parser/check_keywords.pl index 568ef696fd..6eb0aea96b 100644 --- a/src/backend/parser/check_keywords.pl +++ b/src/backend/parser/check_keywords.pl @@ -4,7 +4,7 @@ # Usage: check_keywords.pl gram.y kwlist.h # src/backend/parser/check_keywords.pl -# Copyright (c) 2009-2016, PostgreSQL Global Development Group +# Copyright (c) 2009-2017, PostgreSQL Global Development Group use warnings; use strict; @@ -14,7 +14,7 @@ my $kwlist_filename = $ARGV[1]; my $errors = 0; -sub error(@) +sub error { print STDERR @_; $errors = 1; @@ -29,18 +29,18 @@ $keyword_categories{'col_name_keyword'} = 'COL_NAME_KEYWORD'; $keyword_categories{'type_func_name_keyword'} = 'TYPE_FUNC_NAME_KEYWORD'; $keyword_categories{'reserved_keyword'} = 'RESERVED_KEYWORD'; -open(GRAM, $gram_filename) || die("Could not open : $gram_filename"); +open(my $gram, '<', $gram_filename) || die("Could not open : $gram_filename"); -my ($S, $s, $k, $n, $kcat); +my $kcat; my $comment; my @arr; my %keywords; -line: while (<GRAM>) +line: while (my $S = <$gram>) { - chomp; # strip record separator + chomp $S; # strip record separator - $S = $_; + my $s; # Make sure any braces are split $s = '{', $S =~ s/$s/ { /g; @@ -54,7 +54,7 @@ line: while (<GRAM>) { # Is this the beginning of a keyword list? - foreach $k (keys %keyword_categories) + foreach my $k (keys %keyword_categories) { if ($S =~ m/^($k):/) { @@ -66,7 +66,7 @@ line: while (<GRAM>) } # Now split the line into individual fields - $n = (@arr = split(' ', $S)); + my $n = (@arr = split(' ', $S)); # Ok, we're in a keyword list. Go through each field in turn for (my $fieldIndexer = 0; $fieldIndexer < $n; $fieldIndexer++) @@ -109,15 +109,15 @@ line: while (<GRAM>) push @{ $keywords{$kcat} }, $arr[$fieldIndexer]; } } -close GRAM; +close $gram; # Check that each keyword list is in alphabetical order (just for neatnik-ism) -my ($prevkword, $kword, $bare_kword); -foreach $kcat (keys %keyword_categories) +my ($prevkword, $bare_kword); +foreach my $kcat (keys %keyword_categories) { $prevkword = ''; - foreach $kword (@{ $keywords{$kcat} }) + foreach my $kword (@{ $keywords{$kcat} }) { # Some keyword have a _P suffix. Remove it for the comparison. @@ -149,12 +149,13 @@ while (my ($kcat, $kcat_id) = each(%keyword_categories)) # Now read in kwlist.h -open(KWLIST, $kwlist_filename) || die("Could not open : $kwlist_filename"); +open(my $kwlist, '<', $kwlist_filename) + || die("Could not open : $kwlist_filename"); my $prevkwstring = ''; my $bare_kwname; my %kwhash; -kwlist_line: while (<KWLIST>) +kwlist_line: while (<$kwlist>) { my ($line) = $_; @@ -219,7 +220,7 @@ kwlist_line: while (<KWLIST>) } } } -close KWLIST; +close $kwlist; # Check that we've paired up all keywords from gram.y with lines in kwlist.h while (my ($kwcat, $kwcat_id) = each(%keyword_categories)) diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 54af976917..7fa2f21e3f 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -6,7 +6,7 @@ * gram.y * POSTGRESQL BISON rules/actions * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 2010-2012 Postgres-XC Development Group * @@ -29,12 +29,11 @@ * current transaction and are just parsing commands to find the next * ROLLBACK or COMMIT. If you make use of SET variables, then you * will do the wrong thing in multi-query strings like this: - * SET SQL_inheritance TO off; SELECT * FROM foo; + * SET constraint_exclusion TO off; SELECT * FROM foo; * because the entire string is parsed by gram.y before the SET gets * executed. Anything that depends on the database or changeable state * should be handled during parse analysis so that it happens at the - * right time not the wrong time. The handling of SQL_inheritance is - * a good example. + * right time not the wrong time. * * WARNINGS * If you use a list, make sure the datum is a node so that the printing @@ -85,7 +84,8 @@ /* * The above macro assigns -1 (unknown) as the parse location of any - * nonterminal that was reduced from an empty rule. This is problematic + * nonterminal that was reduced from an empty rule, or whose leftmost + * component was reduced from an empty rule. This is problematic * for nonterminals defined like * OptFooList: / * EMPTY * / { ... } | OptFooList Foo { ... } ; * because we'll set -1 as the location during the first reduction and then @@ -96,6 +96,12 @@ * (Although we have many nonterminals that follow this pattern, we only * bother with fixing @$ like this when the nonterminal's parse location * is actually referenced in some rule.) + * + * A cleaner answer would be to make YYLLOC_DEFAULT scan all the Rhs + * locations until it's found one that's not -1. Then we'd get a correct + * location for any nonterminal that isn't entirely empty. But this way + * would add overhead to every rule reduction, and so far there's not been + * a compelling reason to pay that overhead. */ /* @@ -146,6 +152,8 @@ typedef struct StmtMulti static void base_yyerror(YYLTYPE *yylloc, core_yyscan_t yyscanner, const char *msg); +static RawStmt *makeRawStmt(Node *stmt, int stmt_location); +static void updateRawStmtEnd(RawStmt *rs, int end_location); static Node *makeColumnRef(char *colname, List *indirection, int location, core_yyscan_t yyscanner); static Node *makeTypeCast(Node *arg, TypeName *typename, int location); @@ -157,7 +165,7 @@ static Node *makeBitStringConst(char *str, int location); static Node *makeNullAConst(int location); static Node *makeAConst(Value *v, int location); static Node *makeBoolAConst(bool state, int location); -static Node *makeRoleSpec(RoleSpecType type, int location); +static RoleSpec *makeRoleSpec(RoleSpecType type, int location); static void check_qualified_name(List *names, core_yyscan_t yyscanner); static List *check_func_name(List *names, core_yyscan_t yyscanner); static List *check_indirection(List *indirection, core_yyscan_t yyscanner); @@ -177,6 +185,8 @@ static Node *makeAndExpr(Node *lexpr, Node *rexpr, int location); static Node *makeOrExpr(Node *lexpr, Node *rexpr, int location); static Node *makeNotExpr(Node *expr, int location); static Node *makeAArrayExpr(List *elements, int location); +static Node *makeSQLValueFunction(SQLValueFunctionOp op, int32 typmod, + int location); static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args, int location); static List *mergeTableFuncParameters(List *func_args, List *columns); @@ -220,7 +230,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); TypeName *typnam; FunctionParameter *fun_param; FunctionParameterMode fun_param_mode; - FuncWithArgs *funwithargs; + ObjectWithArgs *objwithargs; DefElem *defelt; SortBy *sortby; WindowDef *windef; @@ -244,10 +254,14 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); DistributeBy *distby; PGXCSubCluster *subclus; /* PGXC_END */ + PartitionElem *partelem; + PartitionSpec *partspec; + PartitionBoundSpec *partboundspec; + RoleSpec *rolespec; } %type <node> stmt schema_stmt - AlterEventTrigStmt + AlterEventTrigStmt AlterCollationStmt AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt AlterFdwStmt AlterForeignServerStmt AlterGroupStmt AlterObjectDependsStmt AlterObjectSchemaStmt AlterOwnerStmt @@ -260,16 +274,16 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt CreateDomainStmt CreateExtensionStmt CreateGroupStmt CreateOpClassStmt CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt - CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt + CreateSchemaStmt CreateSeqStmt CreateStmt CreateStatsStmt CreateTableSpaceStmt CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt CreateAssertStmt CreateTransformStmt CreateTrigStmt CreateEventTrigStmt CreateUserStmt CreateUserMappingStmt CreateRoleStmt CreatePolicyStmt CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt - DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt - DropPolicyStmt DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt + DropAssertStmt DropCastStmt DropRoleStmt + DropUserStmt DropdbStmt DropTableSpaceStmt DropTransformStmt - DropForeignServerStmt DropUserMappingStmt ExplainStmt ExecDirectStmt FetchStmt + DropUserMappingStmt ExplainStmt ExecDirectStmt FetchStmt GrantStmt GrantRoleStmt ImportForeignSchemaStmt IndexStmt InsertStmt ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt @@ -285,6 +299,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); BarrierStmt PauseStmt AlterNodeStmt CreateNodeStmt DropNodeStmt CreateNodeGroupStmt DropNodeGroupStmt CreateMatViewStmt RefreshMatViewStmt CreateAmStmt + CreatePublicationStmt AlterPublicationStmt + CreateSubscriptionStmt AlterSubscriptionStmt DropSubscriptionStmt %type <node> select_no_parens select_with_parens select_clause simple_select values_clause @@ -293,8 +309,10 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type <ival> add_drop opt_asc_desc opt_nulls_order %type <node> alter_table_cmd alter_type_cmd opt_collate_clause - replica_identity + replica_identity partition_cmd %type <list> alter_table_cmds alter_type_cmds +%type <list> alter_identity_column_option_list +%type <defelt> alter_identity_column_option %type <dbehavior> opt_drop_behavior @@ -329,6 +347,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type <list> TriggerEvents TriggerOneEvent %type <value> TriggerFuncArg %type <node> TriggerWhen +%type <str> TransitionRelName +%type <boolean> TransitionRowOrTable TransitionOldOrNew +%type <node> TriggerTransition %type <list> event_trigger_when_list event_trigger_value_list %type <defelt> event_trigger_when_item @@ -349,17 +370,18 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type <str> all_Op MathOp %type <str> row_security_cmd RowSecurityDefaultForCmd +%type <boolean> RowSecurityDefaultPermissive %type <node> RowSecurityOptionalWithCheck RowSecurityOptionalExpr %type <list> RowSecurityDefaultToRole RowSecurityOptionalToRole %type <str> iso_level opt_encoding -%type <node> grantee +%type <rolespec> grantee %type <list> grantee_list %type <accesspriv> privilege %type <list> privileges privilege_list %type <privtarget> privilege_target -%type <funwithargs> function_with_argtypes -%type <list> function_with_argtypes_list +%type <objwithargs> function_with_argtypes aggregate_with_argtypes operator_with_argtypes +%type <list> function_with_argtypes_list aggregate_with_argtypes_list operator_with_argtypes_list %type <ival> defacl_privilege_target %type <defelt> DefACLOption %type <list> DefACLOptionList @@ -383,8 +405,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); qualified_name_list any_name any_name_list type_name_list any_operator expr_list attrs target_list opt_target_list insert_column_list set_target_list - set_clause_list set_clause multiple_set_clause - ctext_expr_list ctext_row def_list operator_def_list indirection opt_indirection + set_clause_list set_clause + def_list operator_def_list indirection opt_indirection reloption_list group_clause TriggerFuncArgs select_limit opt_select_limit opclass_item_list opclass_drop_list opclass_purpose opt_opfamily transaction_mode_list_or_empty @@ -395,10 +417,14 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); create_generic_options alter_generic_options relation_expr_list dostmt_opt_list transform_element_list transform_type_list + TriggerTransitions TriggerReferencing + publication_name_list %type <list> group_by_list %type <node> group_by_item empty_grouping_set rollup_clause cube_clause %type <node> grouping_sets_clause +%type <node> opt_publication_for_tables publication_for_tables +%type <value> publication_name_item %type <list> opt_fdw_options fdw_options %type <defelt> fdw_option @@ -441,14 +467,16 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type <boolean> copy_from opt_program %type <ival> opt_column event cursor_options opt_hold opt_set_data -%type <objtype> drop_type comment_type security_label_type +%type <objtype> drop_type_any_name drop_type_name drop_type_name_on_any_name + comment_type_any_name comment_type_name + security_label_type_any_name security_label_type_name %type <node> fetch_args limit_clause select_limit_value offset_clause select_offset_value select_offset_value2 opt_select_fetch_first_value %type <ival> row_or_rows first_or_next -%type <list> OptSeqOptList SeqOptList +%type <list> OptSeqOptList SeqOptList OptParenthesizedSeqOptList %type <defelt> SeqOptElem %type <istmt> insert_rest @@ -463,8 +491,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type <defelt> def_elem reloption_elem old_aggr_elem operator_def_elem %type <node> def_arg columnElem where_clause where_or_current_clause a_expr b_expr c_expr AexprConst indirection_el opt_slice_bound - columnref in_expr having_clause func_table array_expr - ExclusionWhereClause + columnref in_expr having_clause func_table xmltable array_expr + ExclusionWhereClause operator_def_arg %type <list> rowsfrom_item rowsfrom_list opt_col_def_list %type <boolean> opt_ordinality %type <list> ExclusionConstraintList ExclusionConstraintElem @@ -474,7 +502,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type <node> case_expr case_arg when_clause case_default %type <list> when_clause_list %type <ival> sub_type -%type <node> ctext_expr %type <value> NumericOnly %type <list> NumericOnly_list %type <alias> alias_clause opt_alias_clause @@ -486,7 +513,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type <range> relation_expr %type <range> relation_expr_opt_alias %type <node> tablesample_clause opt_repeatable_clause -%type <target> target_el single_set_clause set_target insert_column_item +%type <target> target_el set_target insert_column_item %type <str> generic_option_name %type <node> generic_option_arg @@ -513,7 +540,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); Bit ConstBit BitWithLength BitWithoutLength %type <str> character %type <str> extract_arg -%type <str> opt_charset %type <boolean> opt_varying opt_timezone opt_no_inherit %type <ival> Iconst SignedIconst @@ -524,7 +550,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type <str> NonReservedWord NonReservedWord_or_Sconst %type <str> createdb_opt_name %type <node> var_value zone_value -%type <node> auth_ident RoleSpec opt_granted_by +%type <rolespec> auth_ident RoleSpec opt_granted_by %type <keyword> unreserved_keyword type_func_name_keyword %type <keyword> col_name_keyword reserved_keyword @@ -540,7 +566,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type <list> constraints_set_list %type <boolean> constraints_set_mode %type <str> OptTableSpace OptConsTableSpace -%type <node> OptTableSpaceOwner +%type <rolespec> OptTableSpaceOwner %type <ival> opt_check_option %type <str> opt_provider security_label @@ -551,6 +577,11 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type <node> xmlexists_argument %type <ival> document_or_content %type <boolean> xml_whitespace_option +%type <list> xmltable_column_list xmltable_column_option_list +%type <node> xmltable_column_el +%type <defelt> xmltable_column_option_el +%type <list> xml_namespace_list +%type <target> xml_namespace_el %type <node> func_application func_expr_common_subexpr %type <node> func_expr func_expr_windowless @@ -570,15 +601,23 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type <subclus> OptSubCluster OptSubClusterInternal /* PGXC_END */ %type <boolean> opt_if_not_exists +%type <ival> generated_when override_kind +%type <partspec> PartitionSpec OptPartitionSpec +%type <str> part_strategy +%type <partelem> part_elem +%type <list> part_params +%type <partboundspec> ForValues +%type <node> partbound_datum PartitionRangeDatum +%type <list> partbound_datum_list range_datum_list /* * Non-keyword token types. These are hard-wired into the "flex" lexer. * They must be listed first so that their numeric codes do not depend on - * the set of keywords. PL/pgsql depends on this so that it can share the - * same lexer. If you add/change tokens here, fix PL/pgsql to match! + * the set of keywords. PL/pgSQL depends on this so that it can share the + * same lexer. If you add/change tokens here, fix PL/pgSQL to match! * * DOT_DOT is unused in the core SQL grammar, and so will always provoke - * parse errors. It is needed by PL/pgsql. + * parse errors. It is needed by PL/pgSQL. */ %token <str> IDENT FCONST SCONST BCONST XCONST Op %token <ival> ICONST PARAM @@ -596,27 +635,24 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); /* PGXC - added DISTRIBUTE, DISTRIBUTED, DISTSYLE, DISTKEY, RANDOMLY, DIRECT, COORDINATOR, CLEAN, NODE, BARRIER */ %token <keyword> ABORT_P ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC - ASSERTION ASSIGNMENT ASYMMETRIC AT ATTRIBUTE AUTHORIZATION + ASSERTION ASSIGNMENT ASYMMETRIC AT ATTACH ATTRIBUTE AUTHORIZATION BACKWARD BARRIER BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT BOOLEAN_P BOTH BY CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLEAN CLOSE - CLUSTER COALESCE COLLATE COLLATION COLUMN COMMENT COMMENTS COMMIT - COMMITTED CONCURRENTLY CONFIGURATION CONFLICT CONNECTION CONSTRAINT CONSTRAINTS - CONTENT_P CONTINUE_P CONVERSION_P COORDINATOR COPY COST CREATE + CLUSTER COALESCE COLLATE COLLATION COLUMN COLUMNS COMMENT COMMENTS COMMIT + COMMITTED CONCURRENTLY CONFIGURATION CONFLICT CONNECTION CONSTRAINT + CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COORDINATOR COPY COST CREATE CROSS CSV CUBE CURRENT_P CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE DATA_P DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS DEPENDS DESC -/* PGXC_BEGIN */ - DICTIONARY DIRECT DISABLE_P DISCARD DISTINCT DISTKEY DISTRIBUTE DISTRIBUTED - DISTSTYLE DO DOCUMENT_P DOMAIN_P DOUBLE_P -/* PGXC_END */ - DROP + DETACH DICTIONARY DIRECT DISABLE_P DISCARD DISTINCT DISTKEY DISTRIBUTE DISTRIBUTED DISTSTYLE DO DOCUMENT_P DOMAIN_P + DOUBLE_P DROP EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN @@ -625,7 +661,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); FALSE_P FAMILY FETCH FILTER FIRST_P FLOAT_P FOLLOWING FOR FORCE FOREIGN FORWARD FREEZE FROM FULL FUNCTION FUNCTIONS - GLOBAL GRANT GRANTED GREATEST GROUP_P GROUPING + GENERATED GLOBAL GRANT GRANTED GREATEST GROUP_P GROUPING HANDLER HAVING HEADER_P HOLD HOUR_P @@ -644,29 +680,29 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); MAPPING MATCH MATERIALIZED MAXVALUE METHOD MINUTE_P MINVALUE MODE MONTH_P MOVE - NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NODE NONE + NAME_P NAMES NATIONAL NATURAL NCHAR NEW NEXT NO NODE NONE NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF NULLS_P NUMERIC - OBJECT_P OF OFF OFFSET OIDS ON ONLY OPERATOR OPTION OPTIONS OR - ORDER ORDINALITY OUT_P OUTER_P OVER OVERLAPS OVERLAY OWNED OWNER + OBJECT_P OF OFF OFFSET OIDS OLD ON ONLY OPERATOR OPTION OPTIONS OR + ORDER ORDINALITY OUT_P OUTER_P OVER OVERLAPS OVERLAY OVERRIDING OWNED OWNER PARALLEL PARSER PARTIAL PARTITION PASSING PASSWORD PAUSE PLACING PLANS POLICY POSITION PRECEDING PRECISION PREFERRED PRESERVE PREPARE PREPARED PRIMARY - PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROGRAM + PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROGRAM PUBLICATION QUOTE - RANDOMLY RANGE READ REAL REASSIGN RECHECK RECURSIVE REF REFERENCES REFRESH REINDEX - RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA + RANDOMLY RANGE READ REAL REASSIGN RECHECK RECURSIVE REF REFERENCES REFERENCING + REFRESH REINDEX RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA RESET RESTART RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROLLUP ROW ROWS RULE - SAVEPOINT SCHEMA SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE SEQUENCES + SAVEPOINT SCHEMA SCHEMAS SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE SEQUENCES SERIALIZABLE SERVER SESSION SESSION_USER SET SETS SETOF SHARE SHOW - SIMILAR SIMPLE SKIP SMALLINT SNAPSHOT SOME SQL_P STABLE STANDALONE_P START - STATEMENT STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P SUBSTRING - SYMMETRIC SYSID SYSTEM_P + SIMILAR SIMPLE SKIP SMALLINT SNAPSHOT SOME SQL_P STABLE STANDALONE_P + START STATEMENT STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P + SUBSCRIPTION SUBSTRING SYMMETRIC SYSID SYSTEM_P TABLE TABLES TABLESAMPLE TABLESPACE TEMP TEMPLATE TEMPORARY TEXT_P THEN TIME TIMESTAMP TO TRAILING TRANSACTION TRANSFORM TREAT TRIGGER TRIM TRUE_P @@ -680,8 +716,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); WHEN WHERE WHITESPACE_P WINDOW WITH WITHIN WITHOUT WORK WRAPPER WRITE - XML_P XMLATTRIBUTES XMLCONCAT XMLELEMENT XMLEXISTS XMLFOREST XMLPARSE - XMLPI XMLROOT XMLSERIALIZE + XML_P XMLATTRIBUTES XMLCONCAT XMLELEMENT XMLEXISTS XMLFOREST XMLNAMESPACES + XMLPARSE XMLPI XMLROOT XMLSERIALIZE XMLTABLE YEAR_P YES_P @@ -720,6 +756,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); * same as if they weren't keywords). We need to do this for PARTITION, * RANGE, ROWS to support opt_existing_window_name; and for RANGE, ROWS * so that they can follow a_expr without creating postfix-operator problems; + * for GENERATED so that it can follow b_expr; * and for NULL so that it can follow b_expr in ColQualList without creating * postfix-operator problems. * @@ -738,7 +775,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); * blame any funny behavior of UNBOUNDED on the SQL standard, though. */ %nonassoc UNBOUNDED /* ideally should have same precedence as IDENT */ -%nonassoc IDENT NULL_P PARTITION RANGE ROWS PRECEDING FOLLOWING CUBE ROLLUP +%nonassoc IDENT GENERATED NULL_P PARTITION RANGE ROWS PRECEDING FOLLOWING CUBE ROLLUP %left Op OPERATOR /* multi-character ops and user-defined operators */ %left '+' '-' %left '*' '/' '%' @@ -774,9 +811,32 @@ stmtblock: stmtmulti } ; -/* the thrashing around here is to discard "empty" statements... */ +/* + * At top level, we wrap each stmt with a RawStmt node carrying start location + * and length of the stmt's text. Notice that the start loc/len are driven + * entirely from semicolon locations (@2). It would seem natural to use + * @1 or @3 to get the true start location of a stmt, but that doesn't work + * for statements that can start with empty nonterminals (opt_with_clause is + * the main offender here); as noted in the comments for YYLLOC_DEFAULT, + * we'd get -1 for the location in such cases. + * We also take care to discard empty statements entirely. + */ stmtmulti: stmtmulti ';' stmt { + /* + * XXX PG10MERGE: Looks like support for obtaining raw + * query string for individual commands is added in PG10. + * If so, we can make use of the same infrastructure. + * + * XXX The following gives a compilation WARNING because + * stmtmulti is defined as a List in PG10, but we have our + * own definition. + */ + if ($1 != NIL) + { + /* update length of previous stmt */ + updateRawStmtEnd(llast_node(RawStmt, $1), @2); + } if ($3 != NULL) { char *query; @@ -819,6 +879,8 @@ stmtmulti: stmtmulti ';' stmt $$ = n; } } + if ($3 != NULL) + $$ = lappend($1, makeRawStmt($3, @2 + 1)); else $$ = $1; } @@ -851,6 +913,8 @@ stmtmulti: stmtmulti ';' stmt n->queries = list_make1(query); $$ = n; } + if ($1 != NULL) + $$ = list_make1(makeRawStmt($1, 0)); else $$ = NULL; } @@ -858,6 +922,7 @@ stmtmulti: stmtmulti ';' stmt stmt : AlterEventTrigStmt + | AlterCollationStmt | AlterDatabaseStmt | AlterDatabaseSetStmt | AlterDefaultPrivilegesStmt @@ -881,8 +946,10 @@ stmt : | AlterTableStmt | AlterTblSpcStmt | AlterCompositeTypeStmt + | AlterPublicationStmt | AlterRoleSetStmt | AlterRoleStmt + | AlterSubscriptionStmt | AlterTSConfigurationStmt | AlterTSDictionaryStmt | AlterUserMappingStmt @@ -914,12 +981,15 @@ stmt : | CreateNodeStmt | CreateOpClassStmt | CreateOpFamilyStmt + | CreatePublicationStmt | AlterOpFamilyStmt | CreatePolicyStmt | CreatePLangStmt | CreateSchemaStmt | CreateSeqStmt | CreateStmt + | CreateSubscriptionStmt + | CreateStatsStmt | CreateTableSpaceStmt | CreateTransformStmt | CreateTrigStmt @@ -936,21 +1006,17 @@ stmt : | DoStmt | DropAssertStmt | DropCastStmt - | DropFdwStmt - | DropForeignServerStmt | DropGroupStmt | DropNodeGroupStmt | DropNodeStmt | DropOpClassStmt | DropOpFamilyStmt | DropOwnedStmt - | DropPolicyStmt | DropPLangStmt - | DropRuleStmt | DropStmt + | DropSubscriptionStmt | DropTableSpaceStmt | DropTransformStmt - | DropTrigStmt | DropRoleStmt | DropUserStmt | DropUserMappingStmt @@ -1037,38 +1103,46 @@ AlterOptRoleElem: PASSWORD Sconst { $$ = makeDefElem("password", - (Node *)makeString($2)); + (Node *)makeString($2), @1); } | PASSWORD NULL_P { - $$ = makeDefElem("password", NULL); + $$ = makeDefElem("password", NULL, @1); } | ENCRYPTED PASSWORD Sconst { - $$ = makeDefElem("encryptedPassword", - (Node *)makeString($3)); + /* + * These days, passwords are always stored in encrypted + * form, so there is no difference between PASSWORD and + * ENCRYPTED PASSWORD. + */ + $$ = makeDefElem("password", + (Node *)makeString($3), @1); } | UNENCRYPTED PASSWORD Sconst { - $$ = makeDefElem("unencryptedPassword", - (Node *)makeString($3)); + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("UNENCRYPTED PASSWORD is no longer supported"), + errhint("Remove UNENCRYPTED to store the password in encrypted form instead."), + parser_errposition(@1))); } | INHERIT { - $$ = makeDefElem("inherit", (Node *)makeInteger(TRUE)); + $$ = makeDefElem("inherit", (Node *)makeInteger(TRUE), @1); } | CONNECTION LIMIT SignedIconst { - $$ = makeDefElem("connectionlimit", (Node *)makeInteger($3)); + $$ = makeDefElem("connectionlimit", (Node *)makeInteger($3), @1); } | VALID UNTIL Sconst { - $$ = makeDefElem("validUntil", (Node *)makeString($3)); + $$ = makeDefElem("validUntil", (Node *)makeString($3), @1); } /* Supported but not documented for roles, for use by ALTER GROUP. */ | USER role_list { - $$ = makeDefElem("rolemembers", (Node *)$2); + $$ = makeDefElem("rolemembers", (Node *)$2, @1); } | IDENT { @@ -1078,36 +1152,36 @@ AlterOptRoleElem: * size of the main parser. */ if (strcmp($1, "superuser") == 0) - $$ = makeDefElem("superuser", (Node *)makeInteger(TRUE)); + $$ = makeDefElem("superuser", (Node *)makeInteger(TRUE), @1); else if (strcmp($1, "nosuperuser") == 0) - $$ = makeDefElem("superuser", (Node *)makeInteger(FALSE)); + $$ = makeDefElem("superuser", (Node *)makeInteger(FALSE), @1); else if (strcmp($1, "createrole") == 0) - $$ = makeDefElem("createrole", (Node *)makeInteger(TRUE)); + $$ = makeDefElem("createrole", (Node *)makeInteger(TRUE), @1); else if (strcmp($1, "nocreaterole") == 0) - $$ = makeDefElem("createrole", (Node *)makeInteger(FALSE)); + $$ = makeDefElem("createrole", (Node *)makeInteger(FALSE), @1); else if (strcmp($1, "replication") == 0) - $$ = makeDefElem("isreplication", (Node *)makeInteger(TRUE)); + $$ = makeDefElem("isreplication", (Node *)makeInteger(TRUE), @1); else if (strcmp($1, "noreplication") == 0) - $$ = makeDefElem("isreplication", (Node *)makeInteger(FALSE)); + $$ = makeDefElem("isreplication", (Node *)makeInteger(FALSE), @1); else if (strcmp($1, "createdb") == 0) - $$ = makeDefElem("createdb", (Node *)makeInteger(TRUE)); + $$ = makeDefElem("createdb", (Node *)makeInteger(TRUE), @1); else if (strcmp($1, "nocreatedb") == 0) - $$ = makeDefElem("createdb", (Node *)makeInteger(FALSE)); + $$ = makeDefElem("createdb", (Node *)makeInteger(FALSE), @1); else if (strcmp($1, "login") == 0) - $$ = makeDefElem("canlogin", (Node *)makeInteger(TRUE)); + $$ = makeDefElem("canlogin", (Node *)makeInteger(TRUE), @1); else if (strcmp($1, "nologin") == 0) - $$ = makeDefElem("canlogin", (Node *)makeInteger(FALSE)); + $$ = makeDefElem("canlogin", (Node *)makeInteger(FALSE), @1); else if (strcmp($1, "bypassrls") == 0) - $$ = makeDefElem("bypassrls", (Node *)makeInteger(TRUE)); + $$ = makeDefElem("bypassrls", (Node *)makeInteger(TRUE), @1); else if (strcmp($1, "nobypassrls") == 0) - $$ = makeDefElem("bypassrls", (Node *)makeInteger(FALSE)); + $$ = makeDefElem("bypassrls", (Node *)makeInteger(FALSE), @1); else if (strcmp($1, "noinherit") == 0) { /* * Note that INHERIT is a keyword, so it's handled by main parser, but * NOINHERIT is handled here. */ - $$ = makeDefElem("inherit", (Node *)makeInteger(FALSE)); + $$ = makeDefElem("inherit", (Node *)makeInteger(FALSE), @1); } else ereport(ERROR, @@ -1122,23 +1196,23 @@ CreateOptRoleElem: /* The following are not supported by ALTER ROLE/USER/GROUP */ | SYSID Iconst { - $$ = makeDefElem("sysid", (Node *)makeInteger($2)); + $$ = makeDefElem("sysid", (Node *)makeInteger($2), @1); } | ADMIN role_list { - $$ = makeDefElem("adminmembers", (Node *)$2); + $$ = makeDefElem("adminmembers", (Node *)$2, @1); } | ROLE role_list { - $$ = makeDefElem("rolemembers", (Node *)$2); + $$ = makeDefElem("rolemembers", (Node *)$2, @1); } | IN_P ROLE role_list { - $$ = makeDefElem("addroleto", (Node *)$3); + $$ = makeDefElem("addroleto", (Node *)$3, @1); } | IN_P GROUP_P role_list { - $$ = makeDefElem("addroleto", (Node *)$3); + $$ = makeDefElem("addroleto", (Node *)$3, @1); } ; @@ -1315,7 +1389,7 @@ AlterGroupStmt: n->role = $3; n->action = $4; n->options = list_make1(makeDefElem("rolemembers", - (Node *)$6)); + (Node *)$6, @6)); $$ = (Node *)n; } ; @@ -1892,6 +1966,24 @@ AlterTableStmt: n->missing_ok = true; $$ = (Node *)n; } + | ALTER TABLE relation_expr partition_cmd + { + AlterTableStmt *n = makeNode(AlterTableStmt); + n->relation = $3; + n->cmds = list_make1($4); + n->relkind = OBJECT_TABLE; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER TABLE IF_P EXISTS relation_expr partition_cmd + { + AlterTableStmt *n = makeNode(AlterTableStmt); + n->relation = $5; + n->cmds = list_make1($6); + n->relkind = OBJECT_TABLE; + n->missing_ok = true; + $$ = (Node *)n; + } | ALTER TABLE ALL IN_P TABLESPACE name SET TABLESPACE name opt_nowait { AlterTableMoveAllStmt *n = @@ -2037,6 +2129,35 @@ alter_table_cmds: | alter_table_cmds ',' alter_table_cmd { $$ = lappend($1, $3); } ; +partition_cmd: + /* ALTER TABLE <name> ATTACH PARTITION <table_name> FOR VALUES */ + ATTACH PARTITION qualified_name ForValues + { + AlterTableCmd *n = makeNode(AlterTableCmd); + PartitionCmd *cmd = makeNode(PartitionCmd); + + n->subtype = AT_AttachPartition; + cmd->name = $3; + cmd->bound = $4; + n->def = (Node *) cmd; + + $$ = (Node *) n; + } + /* ALTER TABLE <name> DETACH PARTITION <partition_name> */ + | DETACH PARTITION qualified_name + { + AlterTableCmd *n = makeNode(AlterTableCmd); + PartitionCmd *cmd = makeNode(PartitionCmd); + + n->subtype = AT_DetachPartition; + cmd->name = $3; + cmd->bound = NULL; + n->def = (Node *) cmd; + + $$ = (Node *) n; + } + ; + alter_table_cmd: /* ALTER TABLE <name> ADD <coldef> */ ADD_P columnDef @@ -2135,6 +2256,50 @@ alter_table_cmd: n->def = (Node *) makeString($6); $$ = (Node *)n; } + /* ALTER TABLE <name> ALTER [COLUMN] <colname> ADD GENERATED ... AS IDENTITY ... */ + | ALTER opt_column ColId ADD_P GENERATED generated_when AS IDENTITY_P OptParenthesizedSeqOptList + { + AlterTableCmd *n = makeNode(AlterTableCmd); + Constraint *c = makeNode(Constraint); + + c->contype = CONSTR_IDENTITY; + c->generated_when = $6; + c->options = $9; + c->location = @5; + + n->subtype = AT_AddIdentity; + n->name = $3; + n->def = (Node *) c; + + $$ = (Node *)n; + } + /* ALTER TABLE <name> ALTER [COLUMN] <colname> SET <sequence options>/RESET */ + | ALTER opt_column ColId alter_identity_column_option_list + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_SetIdentity; + n->name = $3; + n->def = (Node *) $4; + $$ = (Node *)n; + } + /* ALTER TABLE <name> ALTER [COLUMN] <colname> DROP IDENTITY */ + | ALTER opt_column ColId DROP IDENTITY_P + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_DropIdentity; + n->name = $3; + n->missing_ok = false; + $$ = (Node *)n; + } + /* ALTER TABLE <name> ALTER [COLUMN] <colname> DROP IDENTITY IF EXISTS */ + | ALTER opt_column ColId DROP IDENTITY_P IF_P EXISTS + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_DropIdentity; + n->name = $3; + n->missing_ok = true; + $$ = (Node *)n; + } /* ALTER TABLE <name> DROP [COLUMN] IF EXISTS <colname> [RESTRICT|CASCADE] */ | DROP opt_column IF_P EXISTS ColId opt_drop_behavior { @@ -2589,23 +2754,123 @@ reloption_list: reloption_elem: ColLabel '=' def_arg { - $$ = makeDefElem($1, (Node *) $3); + $$ = makeDefElem($1, (Node *) $3, @1); } | ColLabel { - $$ = makeDefElem($1, NULL); + $$ = makeDefElem($1, NULL, @1); } | ColLabel '.' ColLabel '=' def_arg { $$ = makeDefElemExtended($1, $3, (Node *) $5, - DEFELEM_UNSPEC); + DEFELEM_UNSPEC, @1); } | ColLabel '.' ColLabel { - $$ = makeDefElemExtended($1, $3, NULL, DEFELEM_UNSPEC); + $$ = makeDefElemExtended($1, $3, NULL, DEFELEM_UNSPEC, @1); } ; +alter_identity_column_option_list: + alter_identity_column_option + { $$ = list_make1($1); } + | alter_identity_column_option_list alter_identity_column_option + { $$ = lappend($1, $2); } + ; + +alter_identity_column_option: + RESTART + { + $$ = makeDefElem("restart", NULL, @1); + } + | RESTART opt_with NumericOnly + { + $$ = makeDefElem("restart", (Node *)$3, @1); + } + | SET SeqOptElem + { + if (strcmp($2->defname, "as") == 0 || + strcmp($2->defname, "restart") == 0 || + strcmp($2->defname, "owned_by") == 0) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("sequence option \"%s\" not supported here", $2->defname), + parser_errposition(@2))); + $$ = $2; + } + | SET GENERATED generated_when + { + $$ = makeDefElem("generated", (Node *) makeInteger($3), @1); + } + ; + +ForValues: + /* a LIST partition */ + FOR VALUES IN_P '(' partbound_datum_list ')' + { + PartitionBoundSpec *n = makeNode(PartitionBoundSpec); + + n->strategy = PARTITION_STRATEGY_LIST; + n->listdatums = $5; + n->location = @3; + + $$ = n; + } + + /* a RANGE partition */ + | FOR VALUES FROM '(' range_datum_list ')' TO '(' range_datum_list ')' + { + PartitionBoundSpec *n = makeNode(PartitionBoundSpec); + + n->strategy = PARTITION_STRATEGY_RANGE; + n->lowerdatums = $5; + n->upperdatums = $9; + n->location = @3; + + $$ = n; + } + ; + +partbound_datum: + Sconst { $$ = makeStringConst($1, @1); } + | NumericOnly { $$ = makeAConst($1, @1); } + | NULL_P { $$ = makeNullAConst(@1); } + ; + +partbound_datum_list: + partbound_datum { $$ = list_make1($1); } + | partbound_datum_list ',' partbound_datum + { $$ = lappend($1, $3); } + ; + +range_datum_list: + PartitionRangeDatum { $$ = list_make1($1); } + | range_datum_list ',' PartitionRangeDatum + { $$ = lappend($1, $3); } + ; + +PartitionRangeDatum: + UNBOUNDED + { + PartitionRangeDatum *n = makeNode(PartitionRangeDatum); + + n->infinite = true; + n->value = NULL; + n->location = @1; + + $$ = (Node *) n; + } + | partbound_datum + { + PartitionRangeDatum *n = makeNode(PartitionRangeDatum); + + n->infinite = false; + n->value = $1; + n->location = @1; + + $$ = (Node *) n; + } + ; /***************************************************************************** * @@ -2812,59 +3077,59 @@ copy_opt_list: copy_opt_item: BINARY { - $$ = makeDefElem("format", (Node *)makeString("binary")); + $$ = makeDefElem("format", (Node *)makeString("binary"), @1); } | OIDS { - $$ = makeDefElem("oids", (Node *)makeInteger(TRUE)); + $$ = makeDefElem("oids", (Node *)makeInteger(TRUE), @1); } | FREEZE { - $$ = makeDefElem("freeze", (Node *)makeInteger(TRUE)); + $$ = makeDefElem("freeze", (Node *)makeInteger(TRUE), @1); } | DELIMITER opt_as Sconst { - $$ = makeDefElem("delimiter", (Node *)makeString($3)); + $$ = makeDefElem("delimiter", (Node *)makeString($3), @1); } | NULL_P opt_as Sconst { - $$ = makeDefElem("null", (Node *)makeString($3)); + $$ = makeDefElem("null", (Node *)makeString($3), @1); } | CSV { - $$ = makeDefElem("format", (Node *)makeString("csv")); + $$ = makeDefElem("format", (Node *)makeString("csv"), @1); } | HEADER_P { - $$ = makeDefElem("header", (Node *)makeInteger(TRUE)); + $$ = makeDefElem("header", (Node *)makeInteger(TRUE), @1); } | QUOTE opt_as Sconst { - $$ = makeDefElem("quote", (Node *)makeString($3)); + $$ = makeDefElem("quote", (Node *)makeString($3), @1); } | ESCAPE opt_as Sconst { - $$ = makeDefElem("escape", (Node *)makeString($3)); + $$ = makeDefElem("escape", (Node *)makeString($3), @1); } | FORCE QUOTE columnList { - $$ = makeDefElem("force_quote", (Node *)$3); + $$ = makeDefElem("force_quote", (Node *)$3, @1); } | FORCE QUOTE '*' { - $$ = makeDefElem("force_quote", (Node *)makeNode(A_Star)); + $$ = makeDefElem("force_quote", (Node *)makeNode(A_Star), @1); } | FORCE NOT NULL_P columnList { - $$ = makeDefElem("force_not_null", (Node *)$4); + $$ = makeDefElem("force_not_null", (Node *)$4, @1); } | FORCE NULL_P columnList { - $$ = makeDefElem("force_null", (Node *)$3); + $$ = makeDefElem("force_null", (Node *)$3, @1); } | ENCODING Sconst { - $$ = makeDefElem("encoding", (Node *)makeString($2)); + $$ = makeDefElem("encoding", (Node *)makeString($2), @1); } ; @@ -2873,7 +3138,7 @@ copy_opt_item: opt_binary: BINARY { - $$ = makeDefElem("format", (Node *)makeString("binary")); + $$ = makeDefElem("format", (Node *)makeString("binary"), @1); } | /*EMPTY*/ { $$ = NULL; } ; @@ -2881,7 +3146,7 @@ opt_binary: opt_oids: WITH OIDS { - $$ = makeDefElem("oids", (Node *)makeInteger(TRUE)); + $$ = makeDefElem("oids", (Node *)makeInteger(TRUE), @1); } | /*EMPTY*/ { $$ = NULL; } ; @@ -2889,7 +3154,7 @@ opt_oids: copy_delimiter: opt_using DELIMITERS Sconst { - $$ = makeDefElem("delimiter", (Node *)makeString($3)); + $$ = makeDefElem("delimiter", (Node *)makeString($3), @2); } | /*EMPTY*/ { $$ = NULL; } ; @@ -2914,7 +3179,7 @@ copy_generic_opt_list: copy_generic_opt_elem: ColLabel copy_generic_opt_arg { - $$ = makeDefElem($1, $2); + $$ = makeDefElem($1, $2, @1); } ; @@ -2958,7 +3223,7 @@ copy_generic_opt_arg_list_item: *****************************************************************************/ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' - OptInherit OptWith OnCommitOption OptTableSpace + OptInherit OptPartitionSpec OptWith OnCommitOption OptTableSpace /* PGXC_BEGIN */ OptDistributeBy OptSubCluster /* PGXC_END */ @@ -2968,11 +3233,12 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' n->relation = $4; n->tableElts = $6; n->inhRelations = $8; + n->partspec = $9; n->ofTypename = NULL; n->constraints = NIL; - n->options = $9; - n->oncommit = $10; - n->tablespacename = $11; + n->options = $10; + n->oncommit = $11; + n->tablespacename = $12; n->if_not_exists = false; /* PGXC_BEGIN */ if ($2 == RELPERSISTENCE_LOCAL_TEMP) @@ -2981,14 +3247,14 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' n->islocal = true; } n->relkind = RELKIND_RELATION; - n->distributeby = $12; - n->subcluster = $13; + n->distributeby = $13; + n->subcluster = $14; /* PGXC_END */ $$ = (Node *)n; } | CREATE OptTemp TABLE IF_P NOT EXISTS qualified_name '(' - OptTableElementList ')' OptInherit OptWith OnCommitOption - OptTableSpace + OptTableElementList ')' OptInherit OptPartitionSpec OptWith + OnCommitOption OptTableSpace /* PGXC_BEGIN */ OptDistributeBy OptSubCluster /* PGXC_END */ @@ -2998,11 +3264,12 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' n->relation = $7; n->tableElts = $9; n->inhRelations = $11; + n->partspec = $12; n->ofTypename = NULL; n->constraints = NIL; - n->options = $12; - n->oncommit = $13; - n->tablespacename = $14; + n->options = $13; + n->oncommit = $14; + n->tablespacename = $15; n->if_not_exists = true; /* PGXC_BEGIN */ if ($2 == RELPERSISTENCE_LOCAL_TEMP) @@ -3011,8 +3278,8 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' n->islocal = true; } n->relkind = RELKIND_RELATION; - n->distributeby = $15; - n->subcluster = $16; + n->distributeby = $16; + n->subcluster = $17; if (n->inhRelations != NULL && n->distributeby != NULL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), @@ -3022,7 +3289,8 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' $$ = (Node *)n; } | CREATE OptTemp TABLE qualified_name OF any_name - OptTypedTableElementList OptWith OnCommitOption OptTableSpace + OptTypedTableElementList OptPartitionSpec OptWith OnCommitOption + OptTableSpace /* PGXC_BEGIN */ OptDistributeBy OptSubCluster /* PGXC_END */ @@ -3032,12 +3300,13 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' n->relation = $4; n->tableElts = $7; n->inhRelations = NIL; + n->partspec = $8; n->ofTypename = makeTypeNameFromNameList($6); n->ofTypename->location = @6; n->constraints = NIL; - n->options = $8; - n->oncommit = $9; - n->tablespacename = $10; + n->options = $9; + n->oncommit = $10; + n->tablespacename = $11; n->if_not_exists = false; /* PGXC_BEGIN */ if ($2 == RELPERSISTENCE_LOCAL_TEMP) @@ -3046,8 +3315,8 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' n->islocal = true; } n->relkind = RELKIND_RELATION; - n->distributeby = $11; - n->subcluster = $12; + n->distributeby = $12; + n->subcluster = $13; if (n->inhRelations != NULL && n->distributeby != NULL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), @@ -3057,7 +3326,8 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' $$ = (Node *)n; } | CREATE OptTemp TABLE IF_P NOT EXISTS qualified_name OF any_name - OptTypedTableElementList OptWith OnCommitOption OptTableSpace + OptTypedTableElementList OptPartitionSpec OptWith OnCommitOption + OptTableSpace /* PGXC_BEGIN */ OptDistributeBy OptSubCluster /* PGXC_END */ @@ -3067,12 +3337,13 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' n->relation = $7; n->tableElts = $10; n->inhRelations = NIL; + n->partspec = $11; n->ofTypename = makeTypeNameFromNameList($9); n->ofTypename->location = @9; n->constraints = NIL; - n->options = $11; - n->oncommit = $12; - n->tablespacename = $13; + n->options = $12; + n->oncommit = $13; + n->tablespacename = $14; n->if_not_exists = true; /* PGXC_BEGIN */ if ($2 == RELPERSISTENCE_LOCAL_TEMP) @@ -3081,8 +3352,8 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' n->islocal = true; } n->relkind = RELKIND_RELATION; - n->distributeby = $14; - n->subcluster = $15; + n->distributeby = $15; + n->subcluster = $16; if (n->inhRelations != NULL && n->distributeby != NULL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), @@ -3091,6 +3362,44 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' /* PGXC_END */ $$ = (Node *)n; } + | CREATE OptTemp TABLE qualified_name PARTITION OF qualified_name + OptTypedTableElementList ForValues OptPartitionSpec OptWith + OnCommitOption OptTableSpace + { + CreateStmt *n = makeNode(CreateStmt); + $4->relpersistence = $2; + n->relation = $4; + n->tableElts = $8; + n->inhRelations = list_make1($7); + n->partbound = $9; + n->partspec = $10; + n->ofTypename = NULL; + n->constraints = NIL; + n->options = $11; + n->oncommit = $12; + n->tablespacename = $13; + n->if_not_exists = false; + $$ = (Node *)n; + } + | CREATE OptTemp TABLE IF_P NOT EXISTS qualified_name PARTITION OF + qualified_name OptTypedTableElementList ForValues OptPartitionSpec + OptWith OnCommitOption OptTableSpace + { + CreateStmt *n = makeNode(CreateStmt); + $7->relpersistence = $2; + n->relation = $7; + n->tableElts = $11; + n->inhRelations = list_make1($10); + n->partbound = $12; + n->partspec = $13; + n->ofTypename = NULL; + n->constraints = NIL; + n->options = $14; + n->oncommit = $15; + n->tablespacename = $16; + n->if_not_exists = true; + $$ = (Node *)n; + } ; /* @@ -3178,6 +3487,7 @@ columnDef: ColId Typename create_generic_options ColQualList n->is_local = true; n->is_not_null = false; n->is_from_type = false; + n->is_from_parent = false; n->storage = 0; n->raw_default = NULL; n->cooked_default = NULL; @@ -3190,7 +3500,26 @@ columnDef: ColId Typename create_generic_options ColQualList } ; -columnOptions: ColId WITH OPTIONS ColQualList +columnOptions: ColId ColQualList + { + ColumnDef *n = makeNode(ColumnDef); + n->colname = $1; + n->typeName = NULL; + n->inhcount = 0; + n->is_local = true; + n->is_not_null = false; + n->is_from_type = false; + n->is_from_parent = false; + n->storage = 0; + n->raw_default = NULL; + n->cooked_default = NULL; + n->collOid = InvalidOid; + SplitColQualList($2, &n->constraints, &n->collClause, + yyscanner); + n->location = @1; + $$ = (Node *)n; + } + | ColId WITH OPTIONS ColQualList { ColumnDef *n = makeNode(ColumnDef); n->colname = $1; @@ -3199,6 +3528,7 @@ columnOptions: ColId WITH OPTIONS ColQualList n->is_local = true; n->is_not_null = false; n->is_from_type = false; + n->is_from_parent = false; n->storage = 0; n->raw_default = NULL; n->cooked_default = NULL; @@ -3218,8 +3548,7 @@ ColQualList: ColConstraint: CONSTRAINT name ColConstraintElem { - Constraint *n = (Constraint *) $3; - Assert(IsA(n, Constraint)); + Constraint *n = castNode(Constraint, $3); n->conname = $2; n->location = @1; $$ = (Node *) n; @@ -3314,6 +3643,15 @@ ColConstraintElem: n->cooked_expr = NULL; $$ = (Node *)n; } + | GENERATED generated_when AS IDENTITY_P OptParenthesizedSeqOptList + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_IDENTITY; + n->generated_when = $2; + n->options = $5; + n->location = @1; + $$ = (Node *)n; + } | REFERENCES qualified_name opt_column_list key_match key_actions { Constraint *n = makeNode(Constraint); @@ -3331,6 +3669,11 @@ ColConstraintElem: } ; +generated_when: + ALWAYS { $$ = ATTRIBUTE_IDENTITY_ALWAYS; } + | BY DEFAULT { $$ = ATTRIBUTE_IDENTITY_BY_DEFAULT; } + ; + /* * ConstraintAttr represents constraint attributes, which we parse as if * they were independent constraint clauses, in order to avoid shift/reduce @@ -3397,6 +3740,7 @@ TableLikeOptionList: TableLikeOption: DEFAULTS { $$ = CREATE_TABLE_LIKE_DEFAULTS; } | CONSTRAINTS { $$ = CREATE_TABLE_LIKE_CONSTRAINTS; } + | IDENTITY_P { $$ = CREATE_TABLE_LIKE_IDENTITY; } | INDEXES { $$ = CREATE_TABLE_LIKE_INDEXES; } | STORAGE { $$ = CREATE_TABLE_LIKE_STORAGE; } | COMMENTS { $$ = CREATE_TABLE_LIKE_COMMENTS; } @@ -3411,8 +3755,7 @@ TableLikeOption: TableConstraint: CONSTRAINT name ConstraintElem { - Constraint *n = (Constraint *) $3; - Assert(IsA(n, Constraint)); + Constraint *n = castNode(Constraint, $3); n->conname = $2; n->location = @1; $$ = (Node *) n; @@ -3632,11 +3975,70 @@ OptInherit: INHERITS '(' qualified_name_list ')' { $$ = $3; } | /*EMPTY*/ { $$ = NIL; } ; +/* Optional partition key specification */ +OptPartitionSpec: PartitionSpec { $$ = $1; } + | /*EMPTY*/ { $$ = NULL; } + ; + +PartitionSpec: PARTITION BY part_strategy '(' part_params ')' + { + PartitionSpec *n = makeNode(PartitionSpec); + + n->strategy = $3; + n->partParams = $5; + n->location = @1; + + $$ = n; + } + ; + +part_strategy: IDENT { $$ = $1; } + | unreserved_keyword { $$ = pstrdup($1); } + ; + +part_params: part_elem { $$ = list_make1($1); } + | part_params ',' part_elem { $$ = lappend($1, $3); } + ; + +part_elem: ColId opt_collate opt_class + { + PartitionElem *n = makeNode(PartitionElem); + + n->name = $1; + n->expr = NULL; + n->collation = $2; + n->opclass = $3; + n->location = @1; + $$ = n; + } + | func_expr_windowless opt_collate opt_class + { + PartitionElem *n = makeNode(PartitionElem); + + n->name = NULL; + n->expr = $1; + n->collation = $2; + n->opclass = $3; + n->location = @1; + $$ = n; + } + | '(' a_expr ')' opt_collate opt_class + { + PartitionElem *n = makeNode(PartitionElem); + + n->name = NULL; + n->expr = $2; + n->collation = $4; + n->opclass = $5; + n->location = @1; + $$ = n; + } + ; /* WITH (options) is preferred, WITH OIDS and WITHOUT OIDS are legacy forms */ OptWith: WITH reloptions { $$ = $2; } - | WITH OIDS { $$ = list_make1(defWithOids(true)); } - | WITHOUT OIDS { $$ = list_make1(defWithOids(false)); } + | WITH OIDS { $$ = list_make1(makeDefElem("oids", (Node *) makeInteger(true), @1)); } + | WITHOUT OIDS { $$ = list_make1(makeDefElem("oids", (Node *) makeInteger(false), @1)); } | /*EMPTY*/ { $$ = NIL; } ; @@ -3780,6 +4182,33 @@ OptConsTableSpace: USING INDEX TABLESPACE name { $$ = $4; } ExistingIndex: USING INDEX index_name { $$ = $3; } ; +/***************************************************************************** + * + * QUERY : + * CREATE STATISTICS stats_name [(stat types)] + * ON expression-list FROM from_list + * + * Note: the expectation here is that the clauses after ON are a subset of + * SELECT syntax, allowing for expressions and joined tables, and probably + * someday a WHERE clause. Much less than that is currently implemented, + * but the grammar accepts it and then we'll throw FEATURE_NOT_SUPPORTED + * errors as necessary at execution. + * + *****************************************************************************/ + +CreateStatsStmt: + CREATE opt_if_not_exists STATISTICS any_name + opt_name_list ON expr_list FROM from_list + { + CreateStatsStmt *n = makeNode(CreateStatsStmt); + n->defnames = $4; + n->stat_types = $5; + n->exprs = $7; + n->relations = $9; + n->if_not_exists = $2; + $$ = (Node *)n; + } + ; /***************************************************************************** * @@ -3992,57 +4421,70 @@ OptSeqOptList: SeqOptList { $$ = $1; } | /*EMPTY*/ { $$ = NIL; } ; +OptParenthesizedSeqOptList: '(' SeqOptList ')' { $$ = $2; } + | /*EMPTY*/ { $$ = NIL; } + ; + SeqOptList: SeqOptElem { $$ = list_make1($1); } | SeqOptList SeqOptElem { $$ = lappend($1, $2); } ; -SeqOptElem: CACHE NumericOnly +SeqOptElem: AS SimpleTypename + { + $$ = makeDefElem("as", (Node *)$2, @1); + } + | CACHE NumericOnly { - $$ = makeDefElem("cache", (Node *)$2); + $$ = makeDefElem("cache", (Node *)$2, @1); } | CYCLE { - $$ = makeDefElem("cycle", (Node *)makeInteger(TRUE)); + $$ = makeDefElem("cycle", (Node *)makeInteger(TRUE), @1); } | NO CYCLE { - $$ = makeDefElem("cycle", (Node *)makeInteger(FALSE)); + $$ = makeDefElem("cycle", (Node *)makeInteger(FALSE), @1); } | INCREMENT opt_by NumericOnly { - $$ = makeDefElem("increment", (Node *)$3); + $$ = makeDefElem("increment", (Node *)$3, @1); } | MAXVALUE NumericOnly { - $$ = makeDefElem("maxvalue", (Node *)$2); + $$ = makeDefElem("maxvalue", (Node *)$2, @1); } | MINVALUE NumericOnly { - $$ = makeDefElem("minvalue", (Node *)$2); + $$ = makeDefElem("minvalue", (Node *)$2, @1); } | NO MAXVALUE { - $$ = makeDefElem("maxvalue", NULL); + $$ = makeDefElem("maxvalue", NULL, @1); } | NO MINVALUE { - $$ = makeDefElem("minvalue", NULL); + $$ = makeDefElem("minvalue", NULL, @1); } | OWNED BY any_name { - $$ = makeDefElem("owned_by", (Node *)$3); + $$ = makeDefElem("owned_by", (Node *)$3, @1); + } + | SEQUENCE NAME_P any_name + { + /* not documented, only used by pg_dump */ + $$ = makeDefElem("sequence_name", (Node *)$3, @1); } | START opt_with NumericOnly { - $$ = makeDefElem("start", (Node *)$3); + $$ = makeDefElem("start", (Node *)$3, @1); } | RESTART { - $$ = makeDefElem("restart", NULL); + $$ = makeDefElem("restart", NULL, @1); } | RESTART opt_with NumericOnly { - $$ = makeDefElem("restart", (Node *)$3); + $$ = makeDefElem("restart", (Node *)$3, @1); } ; @@ -4052,6 +4494,7 @@ opt_by: BY {} NumericOnly: FCONST { $$ = makeFloat($1); } + | '+' FCONST { $$ = makeFloat($2); } | '-' FCONST { $$ = makeFloat($2); @@ -4133,8 +4576,7 @@ DropPLangStmt: { DropStmt *n = makeNode(DropStmt); n->removeType = OBJECT_LANGUAGE; - n->objects = list_make1(list_make1(makeString($4))); - n->arguments = NIL; + n->objects = list_make1(makeString($4)); n->behavior = $5; n->missing_ok = false; n->concurrent = false; @@ -4144,7 +4586,7 @@ DropPLangStmt: { DropStmt *n = makeNode(DropStmt); n->removeType = OBJECT_LANGUAGE; - n->objects = list_make1(list_make1(makeString($6))); + n->objects = list_make1(makeString($6)); n->behavior = $7; n->missing_ok = true; n->concurrent = false; @@ -4241,19 +4683,19 @@ create_extension_opt_list: create_extension_opt_item: SCHEMA name { - $$ = makeDefElem("schema", (Node *)makeString($2)); + $$ = makeDefElem("schema", (Node *)makeString($2), @1); } | VERSION_P NonReservedWord_or_Sconst { - $$ = makeDefElem("new_version", (Node *)makeString($2)); + $$ = makeDefElem("new_version", (Node *)makeString($2), @1); } | FROM NonReservedWord_or_Sconst { - $$ = makeDefElem("old_version", (Node *)makeString($2)); + $$ = makeDefElem("old_version", (Node *)makeString($2), @1); } | CASCADE { - $$ = makeDefElem("cascade", (Node *)makeInteger(TRUE)); + $$ = makeDefElem("cascade", (Node *)makeInteger(TRUE), @1); } ; @@ -4282,7 +4724,7 @@ alter_extension_opt_list: alter_extension_opt_item: TO NonReservedWord_or_Sconst { - $$ = makeDefElem("new_version", (Node *)makeString($2)); + $$ = makeDefElem("new_version", (Node *)makeString($2), @1); } ; @@ -4293,14 +4735,22 @@ alter_extension_opt_item: *****************************************************************************/ AlterExtensionContentsStmt: - ALTER EXTENSION name add_drop AGGREGATE func_name aggr_args + ALTER EXTENSION name add_drop ACCESS METHOD name + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_ACCESS_METHOD; + n->object = (Node *) makeString($7); + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop AGGREGATE aggregate_with_argtypes { AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); n->extname = $3; n->action = $4; n->objtype = OBJECT_AGGREGATE; - n->objname = $6; - n->objargs = extractAggrArgTypes($7); + n->object = (Node *) $6; $$ = (Node *)n; } | ALTER EXTENSION name add_drop CAST '(' Typename AS Typename ')' @@ -4309,8 +4759,7 @@ AlterExtensionContentsStmt: n->extname = $3; n->action = $4; n->objtype = OBJECT_CAST; - n->objname = list_make1($7); - n->objargs = list_make1($9); + n->object = (Node *) list_make2($7, $9); $$ = (Node *) n; } | ALTER EXTENSION name add_drop COLLATION any_name @@ -4319,7 +4768,7 @@ AlterExtensionContentsStmt: n->extname = $3; n->action = $4; n->objtype = OBJECT_COLLATION; - n->objname = $6; + n->object = (Node *) $6; $$ = (Node *)n; } | ALTER EXTENSION name add_drop CONVERSION_P any_name @@ -4328,7 +4777,7 @@ AlterExtensionContentsStmt: n->extname = $3; n->action = $4; n->objtype = OBJECT_CONVERSION; - n->objname = $6; + n->object = (Node *) $6; $$ = (Node *)n; } | ALTER EXTENSION name add_drop DOMAIN_P Typename @@ -4337,7 +4786,7 @@ AlterExtensionContentsStmt: n->extname = $3; n->action = $4; n->objtype = OBJECT_DOMAIN; - n->objname = list_make1($6); + n->object = (Node *) $6; $$ = (Node *)n; } | ALTER EXTENSION name add_drop FUNCTION function_with_argtypes @@ -4346,8 +4795,7 @@ AlterExtensionContentsStmt: n->extname = $3; n->action = $4; n->objtype = OBJECT_FUNCTION; - n->objname = $6->funcname; - n->objargs = $6->funcargs; + n->object = (Node *) $6; $$ = (Node *)n; } | ALTER EXTENSION name add_drop opt_procedural LANGUAGE name @@ -4356,17 +4804,16 @@ AlterExtensionContentsStmt: n->extname = $3; n->action = $4; n->objtype = OBJECT_LANGUAGE; - n->objname = list_make1(makeString($7)); + n->object = (Node *) makeString($7); $$ = (Node *)n; } - | ALTER EXTENSION name add_drop OPERATOR any_operator oper_argtypes + | ALTER EXTENSION name add_drop OPERATOR operator_with_argtypes { AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); n->extname = $3; n->action = $4; n->objtype = OBJECT_OPERATOR; - n->objname = $6; - n->objargs = $7; + n->object = (Node *) $6; $$ = (Node *)n; } | ALTER EXTENSION name add_drop OPERATOR CLASS any_name USING access_method @@ -4375,7 +4822,7 @@ AlterExtensionContentsStmt: n->extname = $3; n->action = $4; n->objtype = OBJECT_OPCLASS; - n->objname = lcons(makeString($9), $7); + n->object = (Node *) lcons(makeString($9), $7); $$ = (Node *)n; } | ALTER EXTENSION name add_drop OPERATOR FAMILY any_name USING access_method @@ -4384,7 +4831,7 @@ AlterExtensionContentsStmt: n->extname = $3; n->action = $4; n->objtype = OBJECT_OPFAMILY; - n->objname = lcons(makeString($9), $7); + n->object = (Node *) lcons(makeString($9), $7); $$ = (Node *)n; } | ALTER EXTENSION name add_drop SCHEMA name @@ -4393,7 +4840,7 @@ AlterExtensionContentsStmt: n->extname = $3; n->action = $4; n->objtype = OBJECT_SCHEMA; - n->objname = list_make1(makeString($6)); + n->object = (Node *) makeString($6); $$ = (Node *)n; } | ALTER EXTENSION name add_drop EVENT TRIGGER name @@ -4402,7 +4849,7 @@ AlterExtensionContentsStmt: n->extname = $3; n->action = $4; n->objtype = OBJECT_EVENT_TRIGGER; - n->objname = list_make1(makeString($7)); + n->object = (Node *) makeString($7); $$ = (Node *)n; } | ALTER EXTENSION name add_drop TABLE any_name @@ -4411,7 +4858,7 @@ AlterExtensionContentsStmt: n->extname = $3; n->action = $4; n->objtype = OBJECT_TABLE; - n->objname = $6; + n->object = (Node *) $6; $$ = (Node *)n; } | ALTER EXTENSION name add_drop TEXT_P SEARCH PARSER any_name @@ -4420,7 +4867,7 @@ AlterExtensionContentsStmt: n->extname = $3; n->action = $4; n->objtype = OBJECT_TSPARSER; - n->objname = $8; + n->object = (Node *) $8; $$ = (Node *)n; } | ALTER EXTENSION name add_drop TEXT_P SEARCH DICTIONARY any_name @@ -4429,7 +4876,7 @@ AlterExtensionContentsStmt: n->extname = $3; n->action = $4; n->objtype = OBJECT_TSDICTIONARY; - n->objname = $8; + n->object = (Node *) $8; $$ = (Node *)n; } | ALTER EXTENSION name add_drop TEXT_P SEARCH TEMPLATE any_name @@ -4438,7 +4885,7 @@ AlterExtensionContentsStmt: n->extname = $3; n->action = $4; n->objtype = OBJECT_TSTEMPLATE; - n->objname = $8; + n->object = (Node *) $8; $$ = (Node *)n; } | ALTER EXTENSION name add_drop TEXT_P SEARCH CONFIGURATION any_name @@ -4447,7 +4894,7 @@ AlterExtensionContentsStmt: n->extname = $3; n->action = $4; n->objtype = OBJECT_TSCONFIGURATION; - n->objname = $8; + n->object = (Node *) $8; $$ = (Node *)n; } | ALTER EXTENSION name add_drop SEQUENCE any_name @@ -4456,7 +4903,7 @@ AlterExtensionContentsStmt: n->extname = $3; n->action = $4; n->objtype = OBJECT_SEQUENCE; - n->objname = $6; + n->object = (Node *) $6; $$ = (Node *)n; } | ALTER EXTENSION name add_drop VIEW any_name @@ -4465,7 +4912,7 @@ AlterExtensionContentsStmt: n->extname = $3; n->action = $4; n->objtype = OBJECT_VIEW; - n->objname = $6; + n->object = (Node *) $6; $$ = (Node *)n; } | ALTER EXTENSION name add_drop MATERIALIZED VIEW any_name @@ -4474,7 +4921,7 @@ AlterExtensionContentsStmt: n->extname = $3; n->action = $4; n->objtype = OBJECT_MATVIEW; - n->objname = $7; + n->object = (Node *) $7; $$ = (Node *)n; } | ALTER EXTENSION name add_drop FOREIGN TABLE any_name @@ -4483,7 +4930,7 @@ AlterExtensionContentsStmt: n->extname = $3; n->action = $4; n->objtype = OBJECT_FOREIGN_TABLE; - n->objname = $7; + n->object = (Node *) $7; $$ = (Node *)n; } | ALTER EXTENSION name add_drop FOREIGN DATA_P WRAPPER name @@ -4492,7 +4939,7 @@ AlterExtensionContentsStmt: n->extname = $3; n->action = $4; n->objtype = OBJECT_FDW; - n->objname = list_make1(makeString($8)); + n->object = (Node *) makeString($8); $$ = (Node *)n; } | ALTER EXTENSION name add_drop SERVER name @@ -4501,7 +4948,7 @@ AlterExtensionContentsStmt: n->extname = $3; n->action = $4; n->objtype = OBJECT_FOREIGN_SERVER; - n->objname = list_make1(makeString($6)); + n->object = (Node *) makeString($6); $$ = (Node *)n; } | ALTER EXTENSION name add_drop TRANSFORM FOR Typename LANGUAGE name @@ -4510,8 +4957,7 @@ AlterExtensionContentsStmt: n->extname = $3; n->action = $4; n->objtype = OBJECT_TRANSFORM; - n->objname = list_make1($7); - n->objargs = list_make1(makeString($9)); + n->object = (Node *) list_make2($7, makeString($9)); $$ = (Node *)n; } | ALTER EXTENSION name add_drop TYPE_P Typename @@ -4520,7 +4966,7 @@ AlterExtensionContentsStmt: n->extname = $3; n->action = $4; n->objtype = OBJECT_TYPE; - n->objname = list_make1($6); + n->object = (Node *) $6; $$ = (Node *)n; } ; @@ -4543,10 +4989,10 @@ CreateFdwStmt: CREATE FOREIGN DATA_P WRAPPER name opt_fdw_options create_generic ; fdw_option: - HANDLER handler_name { $$ = makeDefElem("handler", (Node *)$2); } - | NO HANDLER { $$ = makeDefElem("handler", NULL); } - | VALIDATOR handler_name { $$ = makeDefElem("validator", (Node *)$2); } - | NO VALIDATOR { $$ = makeDefElem("validator", NULL); } + HANDLER handler_name { $$ = makeDefElem("handler", (Node *)$2, @1); } + | NO HANDLER { $$ = makeDefElem("handler", NULL, @1); } + | VALIDATOR handler_name { $$ = makeDefElem("validator", (Node *)$2, @1); } + | NO VALIDATOR { $$ = makeDefElem("validator", NULL, @1); } ; fdw_options: @@ -4562,37 +5008,6 @@ opt_fdw_options: /***************************************************************************** * * QUERY : - * DROP FOREIGN DATA WRAPPER name - * - ****************************************************************************/ - -DropFdwStmt: DROP FOREIGN DATA_P WRAPPER name opt_drop_behavior - { - DropStmt *n = makeNode(DropStmt); - n->removeType = OBJECT_FDW; - n->objects = list_make1(list_make1(makeString($5))); - n->arguments = NIL; - n->missing_ok = false; - n->behavior = $6; - n->concurrent = false; - $$ = (Node *) n; - } - | DROP FOREIGN DATA_P WRAPPER IF_P EXISTS name opt_drop_behavior - { - DropStmt *n = makeNode(DropStmt); - n->removeType = OBJECT_FDW; - n->objects = list_make1(list_make1(makeString($7))); - n->arguments = NIL; - n->missing_ok = true; - n->behavior = $8; - n->concurrent = false; - $$ = (Node *) n; - } - ; - -/***************************************************************************** - * - * QUERY : * ALTER FOREIGN DATA WRAPPER name options * ****************************************************************************/ @@ -4665,14 +5080,14 @@ alter_generic_option_elem: } | DROP generic_option_name { - $$ = makeDefElemExtended(NULL, $2, NULL, DEFELEM_DROP); + $$ = makeDefElemExtended(NULL, $2, NULL, DEFELEM_DROP, @2); } ; generic_option_elem: generic_option_name generic_option_arg { - $$ = makeDefElem($1, $2); + $$ = makeDefElem($1, $2, @1); } ; @@ -4701,6 +5116,19 @@ CreateForeignServerStmt: CREATE SERVER name opt_type opt_foreign_server_version n->version = $5; n->fdwname = $9; n->options = $10; + n->if_not_exists = false; + $$ = (Node *) n; + } + | CREATE SERVER IF_P NOT EXISTS name opt_type opt_foreign_server_version + FOREIGN DATA_P WRAPPER name create_generic_options + { + CreateForeignServerStmt *n = makeNode(CreateForeignServerStmt); + n->servername = $6; + n->servertype = $7; + n->version = $8; + n->fdwname = $12; + n->options = $13; + n->if_not_exists = true; $$ = (Node *) n; } ; @@ -4724,37 +5152,6 @@ opt_foreign_server_version: /***************************************************************************** * * QUERY : - * DROP SERVER name - * - ****************************************************************************/ - -DropForeignServerStmt: DROP SERVER name opt_drop_behavior - { - DropStmt *n = makeNode(DropStmt); - n->removeType = OBJECT_FOREIGN_SERVER; - n->objects = list_make1(list_make1(makeString($3))); - n->arguments = NIL; - n->missing_ok = false; - n->behavior = $4; - n->concurrent = false; - $$ = (Node *) n; - } - | DROP SERVER IF_P EXISTS name opt_drop_behavior - { - DropStmt *n = makeNode(DropStmt); - n->removeType = OBJECT_FOREIGN_SERVER; - n->objects = list_make1(list_make1(makeString($5))); - n->arguments = NIL; - n->missing_ok = true; - n->behavior = $6; - n->concurrent = false; - $$ = (Node *) n; - } - ; - -/***************************************************************************** - * - * QUERY : * ALTER SERVER name [VERSION] [OPTIONS] * ****************************************************************************/ @@ -4833,6 +5230,48 @@ CreateForeignTableStmt: n->options = $14; $$ = (Node *) n; } + | CREATE FOREIGN TABLE qualified_name + PARTITION OF qualified_name OptTypedTableElementList ForValues + SERVER name create_generic_options + { + CreateForeignTableStmt *n = makeNode(CreateForeignTableStmt); + $4->relpersistence = RELPERSISTENCE_PERMANENT; + n->base.relation = $4; + n->base.inhRelations = list_make1($7); + n->base.tableElts = $8; + n->base.partbound = $9; + n->base.ofTypename = NULL; + n->base.constraints = NIL; + n->base.options = NIL; + n->base.oncommit = ONCOMMIT_NOOP; + n->base.tablespacename = NULL; + n->base.if_not_exists = false; + /* FDW-specific data */ + n->servername = $11; + n->options = $12; + $$ = (Node *) n; + } + | CREATE FOREIGN TABLE IF_P NOT EXISTS qualified_name + PARTITION OF qualified_name OptTypedTableElementList ForValues + SERVER name create_generic_options + { + CreateForeignTableStmt *n = makeNode(CreateForeignTableStmt); + $7->relpersistence = RELPERSISTENCE_PERMANENT; + n->base.relation = $7; + n->base.inhRelations = list_make1($10); + n->base.tableElts = $11; + n->base.partbound = $12; + n->base.ofTypename = NULL; + n->base.constraints = NIL; + n->base.options = NIL; + n->base.oncommit = ONCOMMIT_NOOP; + n->base.tablespacename = NULL; + n->base.if_not_exists = true; + /* FDW-specific data */ + n->servername = $14; + n->options = $15; + $$ = (Node *) n; + } ; /***************************************************************************** @@ -4922,6 +5361,16 @@ CreateUserMappingStmt: CREATE USER MAPPING FOR auth_ident SERVER name create_gen n->user = $5; n->servername = $7; n->options = $8; + n->if_not_exists = false; + $$ = (Node *) n; + } + | CREATE USER MAPPING IF_P NOT EXISTS FOR auth_ident SERVER name create_generic_options + { + CreateUserMappingStmt *n = makeNode(CreateUserMappingStmt); + n->user = $8; + n->servername = $10; + n->options = $11; + n->if_not_exists = true; $$ = (Node *) n; } ; @@ -4978,26 +5427,29 @@ AlterUserMappingStmt: ALTER USER MAPPING FOR auth_ident SERVER name alter_generi /***************************************************************************** * * QUERIES: - * CREATE POLICY name ON table [FOR cmd] [TO role, ...] - * [USING (qual)] [WITH CHECK (with_check)] + * CREATE POLICY name ON table + * [AS { PERMISSIVE | RESTRICTIVE } ] + * [FOR { SELECT | INSERT | UPDATE | DELETE } ] + * [TO role, ...] + * [USING (qual)] [WITH CHECK (with check qual)] * ALTER POLICY name ON table [TO role, ...] - * [USING (qual)] [WITH CHECK (with_check)] - * DROP POLICY name ON table + * [USING (qual)] [WITH CHECK (with check qual)] * *****************************************************************************/ CreatePolicyStmt: - CREATE POLICY name ON qualified_name RowSecurityDefaultForCmd - RowSecurityDefaultToRole RowSecurityOptionalExpr - RowSecurityOptionalWithCheck + CREATE POLICY name ON qualified_name RowSecurityDefaultPermissive + RowSecurityDefaultForCmd RowSecurityDefaultToRole + RowSecurityOptionalExpr RowSecurityOptionalWithCheck { CreatePolicyStmt *n = makeNode(CreatePolicyStmt); n->policy_name = $3; n->table = $5; - n->cmd_name = $6; - n->roles = $7; - n->qual = $8; - n->with_check = $9; + n->permissive = $6; + n->cmd_name = $7; + n->roles = $8; + n->qual = $9; + n->with_check = $10; $$ = (Node *) n; } ; @@ -5016,31 +5468,6 @@ AlterPolicyStmt: } ; -DropPolicyStmt: - DROP POLICY name ON any_name opt_drop_behavior - { - DropStmt *n = makeNode(DropStmt); - n->removeType = OBJECT_POLICY; - n->objects = list_make1(lappend($5, makeString($3))); - n->arguments = NIL; - n->behavior = $6; - n->missing_ok = false; - n->concurrent = false; - $$ = (Node *) n; - } - | DROP POLICY IF_P EXISTS name ON any_name opt_drop_behavior - { - DropStmt *n = makeNode(DropStmt); - n->removeType = OBJECT_POLICY; - n->objects = list_make1(lappend($7, makeString($5))); - n->arguments = NIL; - n->behavior = $8; - n->missing_ok = true; - n->concurrent = false; - $$ = (Node *) n; - } - ; - RowSecurityOptionalExpr: USING '(' a_expr ')' { $$ = $3; } | /* EMPTY */ { $$ = NULL; } @@ -5061,6 +5488,24 @@ RowSecurityOptionalToRole: | /* EMPTY */ { $$ = NULL; } ; +RowSecurityDefaultPermissive: + AS IDENT + { + if (strcmp($2, "permissive") == 0) + $$ = true; + else if (strcmp($2, "restrictive") == 0) + $$ = false; + else + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("unrecognized row security option \"%s\"", $2), + errhint("Only PERMISSIVE or RESTRICTIVE policies are supported currently."), + parser_errposition(@2))); + + } + | /* EMPTY */ { $$ = true; } + ; + RowSecurityDefaultForCmd: FOR row_security_cmd { $$ = $2; } | /* EMPTY */ { $$ = "all"; } @@ -5095,25 +5540,25 @@ CreateAmStmt: CREATE ACCESS METHOD name TYPE_P INDEX HANDLER handler_name * * QUERIES : * CREATE TRIGGER ... - * DROP TRIGGER ... * *****************************************************************************/ CreateTrigStmt: CREATE TRIGGER name TriggerActionTime TriggerEvents ON - qualified_name TriggerForSpec TriggerWhen + qualified_name TriggerReferencing TriggerForSpec TriggerWhen EXECUTE PROCEDURE func_name '(' TriggerFuncArgs ')' { CreateTrigStmt *n = makeNode(CreateTrigStmt); n->trigname = $3; n->relation = $7; - n->funcname = $12; - n->args = $14; - n->row = $8; + n->funcname = $13; + n->args = $15; + n->row = $9; n->timing = $4; n->events = intVal(linitial($5)); n->columns = (List *) lsecond($5); - n->whenClause = $9; + n->whenClause = $10; + n->transitionRels = $8; n->isconstraint = FALSE; n->deferrable = FALSE; n->initdeferred = FALSE; @@ -5135,6 +5580,7 @@ CreateTrigStmt: n->events = intVal(linitial($6)); n->columns = (List *) lsecond($6); n->whenClause = $14; + n->transitionRels = NIL; n->isconstraint = TRUE; processCASbits($10, @10, "TRIGGER", &n->deferrable, &n->initdeferred, NULL, @@ -5187,6 +5633,49 @@ TriggerOneEvent: { $$ = list_make2(makeInteger(TRIGGER_TYPE_TRUNCATE), NIL); } ; +TriggerReferencing: + REFERENCING TriggerTransitions { $$ = $2; } + | /*EMPTY*/ { $$ = NIL; } + ; + +TriggerTransitions: + TriggerTransition { $$ = list_make1($1); } + | TriggerTransitions TriggerTransition { $$ = lappend($1, $2); } + ; + +TriggerTransition: + TransitionOldOrNew TransitionRowOrTable opt_as TransitionRelName + { + TriggerTransition *n = makeNode(TriggerTransition); + n->name = $4; + n->isNew = $1; + n->isTable = $2; + $$ = (Node *)n; + } + ; + +TransitionOldOrNew: + NEW { $$ = TRUE; } + | OLD { $$ = FALSE; } + ; + +TransitionRowOrTable: + TABLE { $$ = TRUE; } + /* + * According to the standard, lack of a keyword here implies ROW. + * Support for that would require prohibiting ROW entirely here, + * reserving the keyword ROW, and/or requiring AS (instead of + * allowing it to be optional, as the standard specifies) as the + * next token. Requiring ROW seems cleanest and easiest to + * explain. + */ + | ROW { $$ = FALSE; } + ; + +TransitionRelName: + ColId { $$ = $1; } + ; + TriggerForSpec: FOR TriggerForOptEach TriggerForType { @@ -5277,32 +5766,6 @@ ConstraintAttributeElem: ; -DropTrigStmt: - DROP TRIGGER name ON any_name opt_drop_behavior - { - DropStmt *n = makeNode(DropStmt); - n->removeType = OBJECT_TRIGGER; - n->objects = list_make1(lappend($5, makeString($3))); - n->arguments = NIL; - n->behavior = $6; - n->missing_ok = false; - n->concurrent = false; - $$ = (Node *) n; - } - | DROP TRIGGER IF_P EXISTS name ON any_name opt_drop_behavior - { - DropStmt *n = makeNode(DropStmt); - n->removeType = OBJECT_TRIGGER; - n->objects = list_make1(lappend($7, makeString($5))); - n->arguments = NIL; - n->behavior = $8; - n->missing_ok = true; - n->concurrent = false; - $$ = (Node *) n; - } - ; - - /***************************************************************************** * * QUERIES : @@ -5344,7 +5807,7 @@ event_trigger_when_list: event_trigger_when_item: ColId IN_P '(' event_trigger_value_list ')' - { $$ = makeDefElem($1, (Node *) $4); } + { $$ = makeDefElem($1, (Node *) $4, @1); } ; event_trigger_value_list: @@ -5404,7 +5867,6 @@ DropAssertStmt: { DropStmt *n = makeNode(DropStmt); n->objects = NIL; - n->arguments = NIL; n->behavior = $4; n->removeType = OBJECT_TRIGGER; /* XXX */ ereport(ERROR, @@ -5543,13 +6005,33 @@ DefineStmt: n->definition = $4; $$ = (Node *)n; } + | CREATE COLLATION IF_P NOT EXISTS any_name definition + { + DefineStmt *n = makeNode(DefineStmt); + n->kind = OBJECT_COLLATION; + n->args = NIL; + n->defnames = $6; + n->definition = $7; + n->if_not_exists = true; + $$ = (Node *)n; + } | CREATE COLLATION any_name FROM any_name { DefineStmt *n = makeNode(DefineStmt); n->kind = OBJECT_COLLATION; n->args = NIL; n->defnames = $3; - n->definition = list_make1(makeDefElem("from", (Node *) $5)); + n->definition = list_make1(makeDefElem("from", (Node *) $5, @5)); + $$ = (Node *)n; + } + | CREATE COLLATION IF_P NOT EXISTS any_name FROM any_name + { + DefineStmt *n = makeNode(DefineStmt); + n->kind = OBJECT_COLLATION; + n->args = NIL; + n->defnames = $6; + n->definition = list_make1(makeDefElem("from", (Node *) $8, @8)); + n->if_not_exists = true; $$ = (Node *)n; } ; @@ -5563,11 +6045,11 @@ def_list: def_elem { $$ = list_make1($1); } def_elem: ColLabel '=' def_arg { - $$ = makeDefElem($1, (Node *) $3); + $$ = makeDefElem($1, (Node *) $3, @1); } | ColLabel { - $$ = makeDefElem($1, NULL); + $$ = makeDefElem($1, NULL, @1); } ; @@ -5577,6 +6059,7 @@ def_arg: func_type { $$ = (Node *)$1; } | qual_all_Op { $$ = (Node *)$1; } | NumericOnly { $$ = (Node *)$1; } | Sconst { $$ = (Node *)makeString($1); } + | NONE { $$ = (Node *)makeString(pstrdup($1)); } ; old_aggr_definition: '(' old_aggr_list ')' { $$ = $2; } @@ -5593,7 +6076,7 @@ old_aggr_list: old_aggr_elem { $$ = list_make1($1); } */ old_aggr_elem: IDENT '=' def_arg { - $$ = makeDefElem($1, (Node *)$3); + $$ = makeDefElem($1, (Node *)$3, @1); } ; @@ -5619,30 +6102,44 @@ AlterEnumStmt: { AlterEnumStmt *n = makeNode(AlterEnumStmt); n->typeName = $3; + n->oldVal = NULL; n->newVal = $7; n->newValNeighbor = NULL; n->newValIsAfter = true; - n->skipIfExists = $6; + n->skipIfNewValExists = $6; $$ = (Node *) n; } | ALTER TYPE_P any_name ADD_P VALUE_P opt_if_not_exists Sconst BEFORE Sconst { AlterEnumStmt *n = makeNode(AlterEnumStmt); n->typeName = $3; + n->oldVal = NULL; n->newVal = $7; n->newValNeighbor = $9; n->newValIsAfter = false; - n->skipIfExists = $6; + n->skipIfNewValExists = $6; $$ = (Node *) n; } | ALTER TYPE_P any_name ADD_P VALUE_P opt_if_not_exists Sconst AFTER Sconst { AlterEnumStmt *n = makeNode(AlterEnumStmt); n->typeName = $3; + n->oldVal = NULL; n->newVal = $7; n->newValNeighbor = $9; n->newValIsAfter = true; - n->skipIfExists = $6; + n->skipIfNewValExists = $6; + $$ = (Node *) n; + } + | ALTER TYPE_P any_name RENAME VALUE_P Sconst TO Sconst + { + AlterEnumStmt *n = makeNode(AlterEnumStmt); + n->typeName = $3; + n->oldVal = $6; + n->newVal = $8; + n->newValNeighbor = NULL; + n->newValIsAfter = false; + n->skipIfNewValExists = false; $$ = (Node *) n; } ; @@ -5687,39 +6184,38 @@ opclass_item: OPERATOR Iconst any_operator opclass_purpose opt_recheck { CreateOpClassItem *n = makeNode(CreateOpClassItem); + ObjectWithArgs *owa = makeNode(ObjectWithArgs); + owa->objname = $3; + owa->objargs = NIL; n->itemtype = OPCLASS_ITEM_OPERATOR; - n->name = $3; - n->args = NIL; + n->name = owa; n->number = $2; n->order_family = $4; $$ = (Node *) n; } - | OPERATOR Iconst any_operator oper_argtypes opclass_purpose + | OPERATOR Iconst operator_with_argtypes opclass_purpose opt_recheck { CreateOpClassItem *n = makeNode(CreateOpClassItem); n->itemtype = OPCLASS_ITEM_OPERATOR; n->name = $3; - n->args = $4; n->number = $2; - n->order_family = $5; + n->order_family = $4; $$ = (Node *) n; } - | FUNCTION Iconst func_name func_args + | FUNCTION Iconst function_with_argtypes { CreateOpClassItem *n = makeNode(CreateOpClassItem); n->itemtype = OPCLASS_ITEM_FUNCTION; n->name = $3; - n->args = extractArgTypes($4); n->number = $2; $$ = (Node *) n; } - | FUNCTION Iconst '(' type_list ')' func_name func_args + | FUNCTION Iconst '(' type_list ')' function_with_argtypes { CreateOpClassItem *n = makeNode(CreateOpClassItem); n->itemtype = OPCLASS_ITEM_FUNCTION; n->name = $6; - n->args = extractArgTypes($7); n->number = $2; n->class_args = $4; $$ = (Node *) n; @@ -5806,7 +6302,7 @@ opclass_drop: CreateOpClassItem *n = makeNode(CreateOpClassItem); n->itemtype = OPCLASS_ITEM_OPERATOR; n->number = $2; - n->args = $4; + n->class_args = $4; $$ = (Node *) n; } | FUNCTION Iconst '(' type_list ')' @@ -5814,7 +6310,7 @@ opclass_drop: CreateOpClassItem *n = makeNode(CreateOpClassItem); n->itemtype = OPCLASS_ITEM_FUNCTION; n->number = $2; - n->args = $4; + n->class_args = $4; $$ = (Node *) n; } ; @@ -5904,28 +6400,66 @@ ReassignOwnedStmt: * *****************************************************************************/ -DropStmt: DROP drop_type IF_P EXISTS any_name_list opt_drop_behavior +DropStmt: DROP drop_type_any_name IF_P EXISTS any_name_list opt_drop_behavior { DropStmt *n = makeNode(DropStmt); n->removeType = $2; n->missing_ok = TRUE; n->objects = $5; - n->arguments = NIL; n->behavior = $6; n->concurrent = false; $$ = (Node *)n; } - | DROP drop_type any_name_list opt_drop_behavior + | DROP drop_type_any_name any_name_list opt_drop_behavior { DropStmt *n = makeNode(DropStmt); n->removeType = $2; n->missing_ok = FALSE; n->objects = $3; - n->arguments = NIL; n->behavior = $4; n->concurrent = false; $$ = (Node *)n; } + | DROP drop_type_name IF_P EXISTS name_list opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = $2; + n->missing_ok = TRUE; + n->objects = $5; + n->behavior = $6; + n->concurrent = false; + $$ = (Node *)n; + } + | DROP drop_type_name name_list opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = $2; + n->missing_ok = FALSE; + n->objects = $3; + n->behavior = $4; + n->concurrent = false; + $$ = (Node *)n; + } + | DROP drop_type_name_on_any_name name ON any_name opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = $2; + n->objects = list_make1(lappend($5, makeString($3))); + n->behavior = $6; + n->missing_ok = false; + n->concurrent = false; + $$ = (Node *) n; + } + | DROP drop_type_name_on_any_name IF_P EXISTS name ON any_name opt_drop_behavior + { + DropStmt *n = makeNode(DropStmt); + n->removeType = $2; + n->objects = list_make1(lappend($7, makeString($5))); + n->behavior = $8; + n->missing_ok = true; + n->concurrent = false; + $$ = (Node *) n; + } | DROP TYPE_P type_name_list opt_drop_behavior { DropStmt *n = makeNode(DropStmt); @@ -5972,7 +6506,6 @@ DropStmt: DROP drop_type IF_P EXISTS any_name_list opt_drop_behavior n->removeType = OBJECT_INDEX; n->missing_ok = FALSE; n->objects = $4; - n->arguments = NIL; n->behavior = $5; n->concurrent = true; $$ = (Node *)n; @@ -5983,32 +6516,47 @@ DropStmt: DROP drop_type IF_P EXISTS any_name_list opt_drop_behavior n->removeType = OBJECT_INDEX; n->missing_ok = TRUE; n->objects = $6; - n->arguments = NIL; n->behavior = $7; n->concurrent = true; $$ = (Node *)n; } ; - -drop_type: TABLE { $$ = OBJECT_TABLE; } +/* object types taking any_name_list */ +drop_type_any_name: + TABLE { $$ = OBJECT_TABLE; } | SEQUENCE { $$ = OBJECT_SEQUENCE; } | VIEW { $$ = OBJECT_VIEW; } | MATERIALIZED VIEW { $$ = OBJECT_MATVIEW; } | INDEX { $$ = OBJECT_INDEX; } | FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; } - | ACCESS METHOD { $$ = OBJECT_ACCESS_METHOD; } - | EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; } | COLLATION { $$ = OBJECT_COLLATION; } | CONVERSION_P { $$ = OBJECT_CONVERSION; } - | SCHEMA { $$ = OBJECT_SCHEMA; } - | EXTENSION { $$ = OBJECT_EXTENSION; } + | STATISTICS { $$ = OBJECT_STATISTIC_EXT; } | TEXT_P SEARCH PARSER { $$ = OBJECT_TSPARSER; } | TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; } | TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; } | TEXT_P SEARCH CONFIGURATION { $$ = OBJECT_TSCONFIGURATION; } ; +/* object types taking name_list */ +drop_type_name: + ACCESS METHOD { $$ = OBJECT_ACCESS_METHOD; } + | EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; } + | EXTENSION { $$ = OBJECT_EXTENSION; } + | FOREIGN DATA_P WRAPPER { $$ = OBJECT_FDW; } + | PUBLICATION { $$ = OBJECT_PUBLICATION; } + | SCHEMA { $$ = OBJECT_SCHEMA; } + | SERVER { $$ = OBJECT_FOREIGN_SERVER; } + ; + +/* object types attached to a table */ +drop_type_name_on_any_name: + POLICY { $$ = OBJECT_POLICY; } + | RULE { $$ = OBJECT_RULE; } + | TRIGGER { $$ = OBJECT_TRIGGER; } + ; + any_name_list: any_name { $$ = list_make1($1); } | any_name_list ',' any_name { $$ = lappend($1, $3); } @@ -6025,8 +6573,8 @@ attrs: '.' attr_name ; type_name_list: - Typename { $$ = list_make1(list_make1($1)); } - | type_name_list ',' Typename { $$ = lappend($1, list_make1($3)); } + Typename { $$ = list_make1($1); } + | type_name_list ',' Typename { $$ = lappend($1, $3); } /***************************************************************************** * @@ -6062,7 +6610,7 @@ opt_restart_seqs: * EXTENSION | EVENT TRIGGER | FOREIGN DATA WRAPPER | * FOREIGN TABLE | INDEX | [PROCEDURAL] LANGUAGE | * MATERIALIZED VIEW | POLICY | ROLE | SCHEMA | SEQUENCE | - * SERVER | TABLE | TABLESPACE | + * SERVER | STATISTICS | TABLE | TABLESPACE | * TEXT SEARCH CONFIGURATION | TEXT SEARCH DICTIONARY | * TEXT SEARCH PARSER | TEXT SEARCH TEMPLATE | TYPE | * VIEW] <objname> | @@ -6083,12 +6631,19 @@ opt_restart_seqs: *****************************************************************************/ CommentStmt: - COMMENT ON comment_type any_name IS comment_text + COMMENT ON comment_type_any_name any_name IS comment_text + { + CommentStmt *n = makeNode(CommentStmt); + n->objtype = $3; + n->object = (Node *) $4; + n->comment = $6; + $$ = (Node *) n; + } + | COMMENT ON comment_type_name name IS comment_text { CommentStmt *n = makeNode(CommentStmt); n->objtype = $3; - n->objname = $4; - n->objargs = NIL; + n->object = (Node *) makeString($4); n->comment = $6; $$ = (Node *) n; } @@ -6096,8 +6651,7 @@ CommentStmt: { CommentStmt *n = makeNode(CommentStmt); n->objtype = OBJECT_TYPE; - n->objname = list_make1($4); - n->objargs = NIL; + n->object = (Node *) $4; n->comment = $6; $$ = (Node *) n; } @@ -6105,44 +6659,39 @@ CommentStmt: { CommentStmt *n = makeNode(CommentStmt); n->objtype = OBJECT_DOMAIN; - n->objname = list_make1($4); - n->objargs = NIL; + n->object = (Node *) $4; n->comment = $6; $$ = (Node *) n; } - | COMMENT ON AGGREGATE func_name aggr_args IS comment_text + | COMMENT ON AGGREGATE aggregate_with_argtypes IS comment_text { CommentStmt *n = makeNode(CommentStmt); n->objtype = OBJECT_AGGREGATE; - n->objname = $4; - n->objargs = extractAggrArgTypes($5); - n->comment = $7; + n->object = (Node *) $4; + n->comment = $6; $$ = (Node *) n; } - | COMMENT ON FUNCTION func_name func_args IS comment_text + | COMMENT ON FUNCTION function_with_argtypes IS comment_text { CommentStmt *n = makeNode(CommentStmt); n->objtype = OBJECT_FUNCTION; - n->objname = $4; - n->objargs = extractArgTypes($5); - n->comment = $7; + n->object = (Node *) $4; + n->comment = $6; $$ = (Node *) n; } - | COMMENT ON OPERATOR any_operator oper_argtypes IS comment_text + | COMMENT ON OPERATOR operator_with_argtypes IS comment_text { CommentStmt *n = makeNode(CommentStmt); n->objtype = OBJECT_OPERATOR; - n->objname = $4; - n->objargs = $5; - n->comment = $7; + n->object = (Node *) $4; + n->comment = $6; $$ = (Node *) n; } | COMMENT ON CONSTRAINT name ON any_name IS comment_text { CommentStmt *n = makeNode(CommentStmt); n->objtype = OBJECT_TABCONSTRAINT; - n->objname = lappend($6, makeString($4)); - n->objargs = NIL; + n->object = (Node *) lappend($6, makeString($4)); n->comment = $8; $$ = (Node *) n; } @@ -6155,8 +6704,7 @@ CommentStmt: * there's a shift/reduce conflict if we do that, so fix it * up here. */ - n->objname = list_make1(makeTypeNameFromNameList($7)); - n->objargs = list_make1(makeString($4)); + n->object = (Node *) list_make2(makeTypeNameFromNameList($7), makeString($4)); n->comment = $9; $$ = (Node *) n; } @@ -6164,8 +6712,7 @@ CommentStmt: { CommentStmt *n = makeNode(CommentStmt); n->objtype = OBJECT_POLICY; - n->objname = lappend($6, makeString($4)); - n->objargs = NIL; + n->object = (Node *) lappend($6, makeString($4)); n->comment = $8; $$ = (Node *) n; } @@ -6173,27 +6720,15 @@ CommentStmt: { CommentStmt *n = makeNode(CommentStmt); n->objtype = OBJECT_RULE; - n->objname = lappend($6, makeString($4)); - n->objargs = NIL; + n->object = (Node *) lappend($6, makeString($4)); n->comment = $8; $$ = (Node *) n; } - | COMMENT ON RULE name IS comment_text - { - /* Obsolete syntax supported for awhile for compatibility */ - CommentStmt *n = makeNode(CommentStmt); - n->objtype = OBJECT_RULE; - n->objname = list_make1(makeString($4)); - n->objargs = NIL; - n->comment = $6; - $$ = (Node *) n; - } | COMMENT ON TRANSFORM FOR Typename LANGUAGE name IS comment_text { CommentStmt *n = makeNode(CommentStmt); n->objtype = OBJECT_TRANSFORM; - n->objname = list_make1($5); - n->objargs = list_make1(makeString($7)); + n->object = (Node *) list_make2($5, makeString($7)); n->comment = $9; $$ = (Node *) n; } @@ -6201,8 +6736,7 @@ CommentStmt: { CommentStmt *n = makeNode(CommentStmt); n->objtype = OBJECT_TRIGGER; - n->objname = lappend($6, makeString($4)); - n->objargs = NIL; + n->object = (Node *) lappend($6, makeString($4)); n->comment = $8; $$ = (Node *) n; } @@ -6210,7 +6744,7 @@ CommentStmt: { CommentStmt *n = makeNode(CommentStmt); n->objtype = OBJECT_OPCLASS; - n->objname = lcons(makeString($7), $5); + n->object = (Node *) lcons(makeString($7), $5); n->comment = $9; $$ = (Node *) n; } @@ -6218,8 +6752,7 @@ CommentStmt: { CommentStmt *n = makeNode(CommentStmt); n->objtype = OBJECT_OPFAMILY; - n->objname = lcons(makeString($7), $5); - n->objargs = NIL; + n->object = (Node *) lcons(makeString($7), $5); n->comment = $9; $$ = (Node *) n; } @@ -6227,8 +6760,7 @@ CommentStmt: { CommentStmt *n = makeNode(CommentStmt); n->objtype = OBJECT_LARGEOBJECT; - n->objname = list_make1($5); - n->objargs = NIL; + n->object = (Node *) $5; n->comment = $7; $$ = (Node *) n; } @@ -6236,47 +6768,46 @@ CommentStmt: { CommentStmt *n = makeNode(CommentStmt); n->objtype = OBJECT_CAST; - n->objname = list_make1($5); - n->objargs = list_make1($7); + n->object = (Node *) list_make2($5, $7); n->comment = $10; $$ = (Node *) n; } - | COMMENT ON opt_procedural LANGUAGE any_name IS comment_text - { - CommentStmt *n = makeNode(CommentStmt); - n->objtype = OBJECT_LANGUAGE; - n->objname = $5; - n->objargs = NIL; - n->comment = $7; - $$ = (Node *) n; - } ; -comment_type: - ACCESS METHOD { $$ = OBJECT_ACCESS_METHOD; } - | COLUMN { $$ = OBJECT_COLUMN; } - | DATABASE { $$ = OBJECT_DATABASE; } - | SCHEMA { $$ = OBJECT_SCHEMA; } +/* object types taking any_name */ +comment_type_any_name: + COLUMN { $$ = OBJECT_COLUMN; } | INDEX { $$ = OBJECT_INDEX; } | SEQUENCE { $$ = OBJECT_SEQUENCE; } + | STATISTICS { $$ = OBJECT_STATISTIC_EXT; } | TABLE { $$ = OBJECT_TABLE; } | VIEW { $$ = OBJECT_VIEW; } | MATERIALIZED VIEW { $$ = OBJECT_MATVIEW; } | COLLATION { $$ = OBJECT_COLLATION; } | CONVERSION_P { $$ = OBJECT_CONVERSION; } - | TABLESPACE { $$ = OBJECT_TABLESPACE; } - | EXTENSION { $$ = OBJECT_EXTENSION; } - | ROLE { $$ = OBJECT_ROLE; } | FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; } - | SERVER { $$ = OBJECT_FOREIGN_SERVER; } - | FOREIGN DATA_P WRAPPER { $$ = OBJECT_FDW; } - | EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; } | TEXT_P SEARCH CONFIGURATION { $$ = OBJECT_TSCONFIGURATION; } | TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; } | TEXT_P SEARCH PARSER { $$ = OBJECT_TSPARSER; } | TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; } ; +/* object types taking name */ +comment_type_name: + ACCESS METHOD { $$ = OBJECT_ACCESS_METHOD; } + | DATABASE { $$ = OBJECT_DATABASE; } + | EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; } + | EXTENSION { $$ = OBJECT_EXTENSION; } + | FOREIGN DATA_P WRAPPER { $$ = OBJECT_FDW; } + | opt_procedural LANGUAGE { $$ = OBJECT_LANGUAGE; } + | PUBLICATION { $$ = OBJECT_PUBLICATION; } + | ROLE { $$ = OBJECT_ROLE; } + | SCHEMA { $$ = OBJECT_SCHEMA; } + | SERVER { $$ = OBJECT_FOREIGN_SERVER; } + | SUBSCRIPTION { $$ = OBJECT_SUBSCRIPTION; } + | TABLESPACE { $$ = OBJECT_TABLESPACE; } + ; + comment_text: Sconst { $$ = $1; } | NULL_P { $$ = NULL; } @@ -6293,14 +6824,23 @@ comment_text: *****************************************************************************/ SecLabelStmt: - SECURITY LABEL opt_provider ON security_label_type any_name + SECURITY LABEL opt_provider ON security_label_type_any_name any_name IS security_label { SecLabelStmt *n = makeNode(SecLabelStmt); n->provider = $3; n->objtype = $5; - n->objname = $6; - n->objargs = NIL; + n->object = (Node *) $6; + n->label = $8; + $$ = (Node *) n; + } + | SECURITY LABEL opt_provider ON security_label_type_name name + IS security_label + { + SecLabelStmt *n = makeNode(SecLabelStmt); + n->provider = $3; + n->objtype = $5; + n->object = (Node *) makeString($6); n->label = $8; $$ = (Node *) n; } @@ -6310,8 +6850,7 @@ SecLabelStmt: SecLabelStmt *n = makeNode(SecLabelStmt); n->provider = $3; n->objtype = OBJECT_TYPE; - n->objname = list_make1($6); - n->objargs = NIL; + n->object = (Node *) $6; n->label = $8; $$ = (Node *) n; } @@ -6321,31 +6860,28 @@ SecLabelStmt: SecLabelStmt *n = makeNode(SecLabelStmt); n->provider = $3; n->objtype = OBJECT_TYPE; - n->objname = list_make1($6); - n->objargs = NIL; + n->object = (Node *) $6; n->label = $8; $$ = (Node *) n; } - | SECURITY LABEL opt_provider ON AGGREGATE func_name aggr_args + | SECURITY LABEL opt_provider ON AGGREGATE aggregate_with_argtypes IS security_label { SecLabelStmt *n = makeNode(SecLabelStmt); n->provider = $3; n->objtype = OBJECT_AGGREGATE; - n->objname = $6; - n->objargs = extractAggrArgTypes($7); - n->label = $9; + n->object = (Node *) $6; + n->label = $8; $$ = (Node *) n; } - | SECURITY LABEL opt_provider ON FUNCTION func_name func_args + | SECURITY LABEL opt_provider ON FUNCTION function_with_argtypes IS security_label { SecLabelStmt *n = makeNode(SecLabelStmt); n->provider = $3; n->objtype = OBJECT_FUNCTION; - n->objname = $6; - n->objargs = extractArgTypes($7); - n->label = $9; + n->object = (Node *) $6; + n->label = $8; $$ = (Node *) n; } | SECURITY LABEL opt_provider ON LARGE_P OBJECT_P NumericOnly @@ -6354,19 +6890,7 @@ SecLabelStmt: SecLabelStmt *n = makeNode(SecLabelStmt); n->provider = $3; n->objtype = OBJECT_LARGEOBJECT; - n->objname = list_make1($7); - n->objargs = NIL; - n->label = $9; - $$ = (Node *) n; - } - | SECURITY LABEL opt_provider ON opt_procedural LANGUAGE any_name - IS security_label - { - SecLabelStmt *n = makeNode(SecLabelStmt); - n->provider = $3; - n->objtype = OBJECT_LANGUAGE; - n->objname = $7; - n->objargs = NIL; + n->object = (Node *) $7; n->label = $9; $$ = (Node *) n; } @@ -6376,20 +6900,28 @@ opt_provider: FOR NonReservedWord_or_Sconst { $$ = $2; } | /* empty */ { $$ = NULL; } ; -security_label_type: +/* object types taking any_name */ +security_label_type_any_name: COLUMN { $$ = OBJECT_COLUMN; } - | DATABASE { $$ = OBJECT_DATABASE; } - | EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; } | FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; } - | SCHEMA { $$ = OBJECT_SCHEMA; } | SEQUENCE { $$ = OBJECT_SEQUENCE; } | TABLE { $$ = OBJECT_TABLE; } - | ROLE { $$ = OBJECT_ROLE; } - | TABLESPACE { $$ = OBJECT_TABLESPACE; } | VIEW { $$ = OBJECT_VIEW; } | MATERIALIZED VIEW { $$ = OBJECT_MATVIEW; } ; +/* object types taking name */ +security_label_type_name: + DATABASE { $$ = OBJECT_DATABASE; } + | EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; } + | opt_procedural LANGUAGE { $$ = OBJECT_LANGUAGE; } + | PUBLICATION { $$ = OBJECT_PUBLICATION; } + | ROLE { $$ = OBJECT_ROLE; } + | SCHEMA { $$ = OBJECT_SCHEMA; } + | SUBSCRIPTION { $$ = OBJECT_SUBSCRIPTION; } + | TABLESPACE { $$ = OBJECT_TABLESPACE; } + ; + security_label: Sconst { $$ = $1; } | NULL_P { $$ = NULL; } ; @@ -6824,22 +7356,6 @@ opt_grant_grant_option: | /*EMPTY*/ { $$ = FALSE; } ; -function_with_argtypes_list: - function_with_argtypes { $$ = list_make1($1); } - | function_with_argtypes_list ',' function_with_argtypes - { $$ = lappend($1, $3); } - ; - -function_with_argtypes: - func_name func_args - { - FuncWithArgs *n = makeNode(FuncWithArgs); - n->funcname = $1; - n->funcargs = extractArgTypes($2); - $$ = n; - } - ; - /***************************************************************************** * * GRANT and REVOKE ROLE statements @@ -6914,15 +7430,15 @@ DefACLOptionList: DefACLOption: IN_P SCHEMA name_list { - $$ = makeDefElem("schemas", (Node *)$3); + $$ = makeDefElem("schemas", (Node *)$3, @1); } | FOR ROLE role_list { - $$ = makeDefElem("roles", (Node *)$3); + $$ = makeDefElem("roles", (Node *)$3, @1); } | FOR USER role_list { - $$ = makeDefElem("roles", (Node *)$3); + $$ = makeDefElem("roles", (Node *)$3, @1); } ; @@ -6979,6 +7495,7 @@ defacl_privilege_target: | FUNCTIONS { $$ = ACL_OBJECT_FUNCTION; } | SEQUENCES { $$ = ACL_OBJECT_SEQUENCE; } | TYPES_P { $$ = ACL_OBJECT_TYPE; } + | SCHEMAS { $$ = ACL_OBJECT_NAMESPACE; } ; @@ -7192,6 +7709,49 @@ func_args_list: | func_args_list ',' func_arg { $$ = lappend($1, $3); } ; +function_with_argtypes_list: + function_with_argtypes { $$ = list_make1($1); } + | function_with_argtypes_list ',' function_with_argtypes + { $$ = lappend($1, $3); } + ; + +function_with_argtypes: + func_name func_args + { + ObjectWithArgs *n = makeNode(ObjectWithArgs); + n->objname = $1; + n->objargs = extractArgTypes($2); + $$ = n; + } + /* + * Because of reduce/reduce conflicts, we can't use func_name + * below, but we can write it out the long way, which actually + * allows more cases. + */ + | type_func_name_keyword + { + ObjectWithArgs *n = makeNode(ObjectWithArgs); + n->objname = list_make1(makeString(pstrdup($1))); + n->args_unspecified = true; + $$ = n; + } + | ColId + { + ObjectWithArgs *n = makeNode(ObjectWithArgs); + n->objname = list_make1(makeString($1)); + n->args_unspecified = true; + $$ = n; + } + | ColId indirection + { + ObjectWithArgs *n = makeNode(ObjectWithArgs); + n->objname = check_func_name(lcons(makeString($1), $2), + yyscanner); + n->args_unspecified = true; + $$ = n; + } + ; + /* * func_args_with_defaults is separate because we only want to accept * defaults in CREATE FUNCTION, not in ALTER etc. @@ -7394,6 +7954,22 @@ aggr_args_list: | aggr_args_list ',' aggr_arg { $$ = lappend($1, $3); } ; +aggregate_with_argtypes: + func_name aggr_args + { + ObjectWithArgs *n = makeNode(ObjectWithArgs); + n->objname = $1; + n->objargs = extractAggrArgTypes($2); + $$ = n; + } + ; + +aggregate_with_argtypes_list: + aggregate_with_argtypes { $$ = list_make1($1); } + | aggregate_with_argtypes_list ',' aggregate_with_argtypes + { $$ = lappend($1, $3); } + ; + createfunc_opt_list: /* Must be at least one to prevent conflict */ createfunc_opt_item { $$ = list_make1($1); } @@ -7406,87 +7982,87 @@ createfunc_opt_list: common_func_opt_item: CALLED ON NULL_P INPUT_P { - $$ = makeDefElem("strict", (Node *)makeInteger(FALSE)); + $$ = makeDefElem("strict", (Node *)makeInteger(FALSE), @1); } | RETURNS NULL_P ON NULL_P INPUT_P { - $$ = makeDefElem("strict", (Node *)makeInteger(TRUE)); + $$ = makeDefElem("strict", (Node *)makeInteger(TRUE), @1); } | STRICT_P { - $$ = makeDefElem("strict", (Node *)makeInteger(TRUE)); + $$ = makeDefElem("strict", (Node *)makeInteger(TRUE), @1); } | IMMUTABLE { - $$ = makeDefElem("volatility", (Node *)makeString("immutable")); + $$ = makeDefElem("volatility", (Node *)makeString("immutable"), @1); } | STABLE { - $$ = makeDefElem("volatility", (Node *)makeString("stable")); + $$ = makeDefElem("volatility", (Node *)makeString("stable"), @1); } | VOLATILE { - $$ = makeDefElem("volatility", (Node *)makeString("volatile")); + $$ = makeDefElem("volatility", (Node *)makeString("volatile"), @1); } | EXTERNAL SECURITY DEFINER { - $$ = makeDefElem("security", (Node *)makeInteger(TRUE)); + $$ = makeDefElem("security", (Node *)makeInteger(TRUE), @1); } | EXTERNAL SECURITY INVOKER { - $$ = makeDefElem("security", (Node *)makeInteger(FALSE)); + $$ = makeDefElem("security", (Node *)makeInteger(FALSE), @1); } | SECURITY DEFINER { - $$ = makeDefElem("security", (Node *)makeInteger(TRUE)); + $$ = makeDefElem("security", (Node *)makeInteger(TRUE), @1); } | SECURITY INVOKER { - $$ = makeDefElem("security", (Node *)makeInteger(FALSE)); + $$ = makeDefElem("security", (Node *)makeInteger(FALSE), @1); } | LEAKPROOF { - $$ = makeDefElem("leakproof", (Node *)makeInteger(TRUE)); + $$ = makeDefElem("leakproof", (Node *)makeInteger(TRUE), @1); } | NOT LEAKPROOF { - $$ = makeDefElem("leakproof", (Node *)makeInteger(FALSE)); + $$ = makeDefElem("leakproof", (Node *)makeInteger(FALSE), @1); } | COST NumericOnly { - $$ = makeDefElem("cost", (Node *)$2); + $$ = makeDefElem("cost", (Node *)$2, @1); } | ROWS NumericOnly { - $$ = makeDefElem("rows", (Node *)$2); + $$ = makeDefElem("rows", (Node *)$2, @1); } | FunctionSetResetClause { /* we abuse the normal content of a DefElem here */ - $$ = makeDefElem("set", (Node *)$1); + $$ = makeDefElem("set", (Node *)$1, @1); } | PARALLEL ColId { - $$ = makeDefElem("parallel", (Node *)makeString($2)); + $$ = makeDefElem("parallel", (Node *)makeString($2), @1); } ; createfunc_opt_item: AS func_as { - $$ = makeDefElem("as", (Node *)$2); + $$ = makeDefElem("as", (Node *)$2, @1); } | LANGUAGE NonReservedWord_or_Sconst { - $$ = makeDefElem("language", (Node *)makeString($2)); + $$ = makeDefElem("language", (Node *)makeString($2), @1); } | TRANSFORM transform_type_list { - $$ = makeDefElem("transform", (Node *)$2); + $$ = makeDefElem("transform", (Node *)$2, @1); } | WINDOW { - $$ = makeDefElem("window", (Node *)makeInteger(TRUE)); + $$ = makeDefElem("window", (Node *)makeInteger(TRUE), @1); } | common_func_opt_item { @@ -7575,24 +8151,22 @@ opt_restrict: *****************************************************************************/ RemoveFuncStmt: - DROP FUNCTION func_name func_args opt_drop_behavior + DROP FUNCTION function_with_argtypes_list opt_drop_behavior { DropStmt *n = makeNode(DropStmt); n->removeType = OBJECT_FUNCTION; - n->objects = list_make1($3); - n->arguments = list_make1(extractArgTypes($4)); - n->behavior = $5; + n->objects = $3; + n->behavior = $4; n->missing_ok = false; n->concurrent = false; $$ = (Node *)n; } - | DROP FUNCTION IF_P EXISTS func_name func_args opt_drop_behavior + | DROP FUNCTION IF_P EXISTS function_with_argtypes_list opt_drop_behavior { DropStmt *n = makeNode(DropStmt); n->removeType = OBJECT_FUNCTION; - n->objects = list_make1($5); - n->arguments = list_make1(extractArgTypes($6)); - n->behavior = $7; + n->objects = $5; + n->behavior = $6; n->missing_ok = true; n->concurrent = false; $$ = (Node *)n; @@ -7600,24 +8174,22 @@ RemoveFuncStmt: ; RemoveAggrStmt: - DROP AGGREGATE func_name aggr_args opt_drop_behavior + DROP AGGREGATE aggregate_with_argtypes_list opt_drop_behavior { DropStmt *n = makeNode(DropStmt); n->removeType = OBJECT_AGGREGATE; - n->objects = list_make1($3); - n->arguments = list_make1(extractAggrArgTypes($4)); - n->behavior = $5; + n->objects = $3; + n->behavior = $4; n->missing_ok = false; n->concurrent = false; $$ = (Node *)n; } - | DROP AGGREGATE IF_P EXISTS func_name aggr_args opt_drop_behavior + | DROP AGGREGATE IF_P EXISTS aggregate_with_argtypes_list opt_drop_behavior { DropStmt *n = makeNode(DropStmt); n->removeType = OBJECT_AGGREGATE; - n->objects = list_make1($5); - n->arguments = list_make1(extractAggrArgTypes($6)); - n->behavior = $7; + n->objects = $5; + n->behavior = $6; n->missing_ok = true; n->concurrent = false; $$ = (Node *)n; @@ -7625,24 +8197,22 @@ RemoveAggrStmt: ; RemoveOperStmt: - DROP OPERATOR any_operator oper_argtypes opt_drop_behavior + DROP OPERATOR operator_with_argtypes_list opt_drop_behavior { DropStmt *n = makeNode(DropStmt); n->removeType = OBJECT_OPERATOR; - n->objects = list_make1($3); - n->arguments = list_make1($4); - n->behavior = $5; + n->objects = $3; + n->behavior = $4; n->missing_ok = false; n->concurrent = false; $$ = (Node *)n; } - | DROP OPERATOR IF_P EXISTS any_operator oper_argtypes opt_drop_behavior + | DROP OPERATOR IF_P EXISTS operator_with_argtypes_list opt_drop_behavior { DropStmt *n = makeNode(DropStmt); n->removeType = OBJECT_OPERATOR; - n->objects = list_make1($5); - n->arguments = list_make1($6); - n->behavior = $7; + n->objects = $5; + n->behavior = $6; n->missing_ok = true; n->concurrent = false; $$ = (Node *)n; @@ -7673,6 +8243,22 @@ any_operator: { $$ = lcons(makeString($1), $3); } ; +operator_with_argtypes_list: + operator_with_argtypes { $$ = list_make1($1); } + | operator_with_argtypes_list ',' operator_with_argtypes + { $$ = lappend($1, $3); } + ; + +operator_with_argtypes: + any_operator oper_argtypes + { + ObjectWithArgs *n = makeNode(ObjectWithArgs); + n->objname = $1; + n->objargs = $2; + $$ = n; + } + ; + /***************************************************************************** * * DO <anonymous code block> [ LANGUAGE language ] @@ -7698,11 +8284,11 @@ dostmt_opt_list: dostmt_opt_item: Sconst { - $$ = makeDefElem("as", (Node *)makeString($1)); + $$ = makeDefElem("as", (Node *)makeString($1), @1); } | LANGUAGE NonReservedWord_or_Sconst { - $$ = makeDefElem("language", (Node *)makeString($2)); + $$ = makeDefElem("language", (Node *)makeString($2), @1); } ; @@ -7757,8 +8343,7 @@ DropCastStmt: DROP CAST opt_if_exists '(' Typename AS Typename ')' opt_drop_beha { DropStmt *n = makeNode(DropStmt); n->removeType = OBJECT_CAST; - n->objects = list_make1(list_make1($5)); - n->arguments = list_make1(list_make1($7)); + n->objects = list_make1(list_make2($5, $7)); n->behavior = $9; n->missing_ok = $3; n->concurrent = false; @@ -7812,8 +8397,7 @@ DropTransformStmt: DROP TRANSFORM opt_if_exists FOR Typename LANGUAGE name opt_d { DropStmt *n = makeNode(DropStmt); n->removeType = OBJECT_TRANSFORM; - n->objects = list_make1(list_make1($5)); - n->arguments = list_make1(list_make1(makeString($7))); + n->objects = list_make1(list_make2($5, makeString($7))); n->behavior = $8; n->missing_ok = $3; $$ = (Node *)n; @@ -7916,13 +8500,12 @@ AlterTblSpcStmt: * *****************************************************************************/ -RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name +RenameStmt: ALTER AGGREGATE aggregate_with_argtypes RENAME TO name { RenameStmt *n = makeNode(RenameStmt); n->renameType = OBJECT_AGGREGATE; - n->object = $3; - n->objarg = extractAggrArgTypes($4); - n->newname = $7; + n->object = (Node *) $3; + n->newname = $6; n->missing_ok = false; $$ = (Node *)n; } @@ -7930,7 +8513,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name { RenameStmt *n = makeNode(RenameStmt); n->renameType = OBJECT_COLLATION; - n->object = $3; + n->object = (Node *) $3; n->newname = $6; n->missing_ok = false; $$ = (Node *)n; @@ -7939,7 +8522,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name { RenameStmt *n = makeNode(RenameStmt); n->renameType = OBJECT_CONVERSION; - n->object = $3; + n->object = (Node *) $3; n->newname = $6; n->missing_ok = false; $$ = (Node *)n; @@ -7957,7 +8540,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name { RenameStmt *n = makeNode(RenameStmt); n->renameType = OBJECT_DOMAIN; - n->object = $3; + n->object = (Node *) $3; n->newname = $6; n->missing_ok = false; $$ = (Node *)n; @@ -7966,7 +8549,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name { RenameStmt *n = makeNode(RenameStmt); n->renameType = OBJECT_DOMCONSTRAINT; - n->object = $3; + n->object = (Node *) $3; n->subname = $6; n->newname = $8; $$ = (Node *)n; @@ -7975,7 +8558,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name { RenameStmt *n = makeNode(RenameStmt); n->renameType = OBJECT_FDW; - n->object = list_make1(makeString($5)); + n->object = (Node *) makeString($5); n->newname = $8; n->missing_ok = false; $$ = (Node *)n; @@ -7984,8 +8567,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name { RenameStmt *n = makeNode(RenameStmt); n->renameType = OBJECT_FUNCTION; - n->object = $3->funcname; - n->objarg = $3->funcargs; + n->object = (Node *) $3; n->newname = $6; n->missing_ok = false; $$ = (Node *)n; @@ -8003,7 +8585,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name { RenameStmt *n = makeNode(RenameStmt); n->renameType = OBJECT_LANGUAGE; - n->object = list_make1(makeString($4)); + n->object = (Node *) makeString($4); n->newname = $7; n->missing_ok = false; $$ = (Node *)n; @@ -8012,7 +8594,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name { RenameStmt *n = makeNode(RenameStmt); n->renameType = OBJECT_OPCLASS; - n->object = lcons(makeString($6), $4); + n->object = (Node *) lcons(makeString($6), $4); n->newname = $9; n->missing_ok = false; $$ = (Node *)n; @@ -8021,7 +8603,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name { RenameStmt *n = makeNode(RenameStmt); n->renameType = OBJECT_OPFAMILY; - n->object = lcons(makeString($6), $4); + n->object = (Node *) lcons(makeString($6), $4); n->newname = $9; n->missing_ok = false; $$ = (Node *)n; @@ -8046,6 +8628,15 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name n->missing_ok = true; $$ = (Node *)n; } + | ALTER PUBLICATION name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_PUBLICATION; + n->object = (Node *) makeString($3); + n->newname = $6; + n->missing_ok = false; + $$ = (Node *)n; + } | ALTER SCHEMA name RENAME TO name { RenameStmt *n = makeNode(RenameStmt); @@ -8059,7 +8650,16 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name { RenameStmt *n = makeNode(RenameStmt); n->renameType = OBJECT_FOREIGN_SERVER; - n->object = list_make1(makeString($3)); + n->object = (Node *) makeString($3); + n->newname = $6; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_SUBSCRIPTION; + n->object = (Node *) makeString($3); n->newname = $6; n->missing_ok = false; $$ = (Node *)n; @@ -8294,7 +8894,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name { RenameStmt *n = makeNode(RenameStmt); n->renameType = OBJECT_EVENT_TRIGGER; - n->object = list_make1(makeString($4)); + n->object = (Node *) makeString($4); n->newname = $7; $$ = (Node *)n; } @@ -8325,11 +8925,20 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name n->missing_ok = false; $$ = (Node *)n; } + | ALTER STATISTICS any_name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_STATISTIC_EXT; + n->object = (Node *) $3; + n->newname = $6; + n->missing_ok = false; + $$ = (Node *)n; + } | ALTER TEXT_P SEARCH PARSER any_name RENAME TO name { RenameStmt *n = makeNode(RenameStmt); n->renameType = OBJECT_TSPARSER; - n->object = $5; + n->object = (Node *) $5; n->newname = $8; n->missing_ok = false; $$ = (Node *)n; @@ -8338,7 +8947,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name { RenameStmt *n = makeNode(RenameStmt); n->renameType = OBJECT_TSDICTIONARY; - n->object = $5; + n->object = (Node *) $5; n->newname = $8; n->missing_ok = false; $$ = (Node *)n; @@ -8347,7 +8956,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name { RenameStmt *n = makeNode(RenameStmt); n->renameType = OBJECT_TSTEMPLATE; - n->object = $5; + n->object = (Node *) $5; n->newname = $8; n->missing_ok = false; $$ = (Node *)n; @@ -8356,7 +8965,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name { RenameStmt *n = makeNode(RenameStmt); n->renameType = OBJECT_TSCONFIGURATION; - n->object = $5; + n->object = (Node *) $5; n->newname = $8; n->missing_ok = false; $$ = (Node *)n; @@ -8365,7 +8974,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name { RenameStmt *n = makeNode(RenameStmt); n->renameType = OBJECT_TYPE; - n->object = $3; + n->object = (Node *) $3; n->newname = $6; n->missing_ok = false; $$ = (Node *)n; @@ -8403,9 +9012,7 @@ AlterObjectDependsStmt: { AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt); n->objectType = OBJECT_FUNCTION; - n->relation = NULL; - n->objname = $3->funcname; - n->objargs = $3->funcargs; + n->object = (Node *) $3; n->extname = makeString($7); $$ = (Node *)n; } @@ -8414,8 +9021,7 @@ AlterObjectDependsStmt: AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt); n->objectType = OBJECT_TRIGGER; n->relation = $5; - n->objname = list_make1(makeString($3)); - n->objargs = NIL; + n->object = (Node *) list_make1(makeString($3)); n->extname = makeString($9); $$ = (Node *)n; } @@ -8424,8 +9030,6 @@ AlterObjectDependsStmt: AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt); n->objectType = OBJECT_MATVIEW; n->relation = $4; - n->objname = NIL; - n->objargs = NIL; n->extname = makeString($8); $$ = (Node *)n; } @@ -8434,8 +9038,6 @@ AlterObjectDependsStmt: AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt); n->objectType = OBJECT_INDEX; n->relation = $3; - n->objname = NIL; - n->objargs = NIL; n->extname = makeString($7); $$ = (Node *)n; } @@ -8448,13 +9050,12 @@ AlterObjectDependsStmt: *****************************************************************************/ AlterObjectSchemaStmt: - ALTER AGGREGATE func_name aggr_args SET SCHEMA name + ALTER AGGREGATE aggregate_with_argtypes SET SCHEMA name { AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); n->objectType = OBJECT_AGGREGATE; - n->object = $3; - n->objarg = extractAggrArgTypes($4); - n->newschema = $7; + n->object = (Node *) $3; + n->newschema = $6; n->missing_ok = false; $$ = (Node *)n; } @@ -8462,7 +9063,7 @@ AlterObjectSchemaStmt: { AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); n->objectType = OBJECT_COLLATION; - n->object = $3; + n->object = (Node *) $3; n->newschema = $6; n->missing_ok = false; $$ = (Node *)n; @@ -8471,7 +9072,7 @@ AlterObjectSchemaStmt: { AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); n->objectType = OBJECT_CONVERSION; - n->object = $3; + n->object = (Node *) $3; n->newschema = $6; n->missing_ok = false; $$ = (Node *)n; @@ -8480,16 +9081,16 @@ AlterObjectSchemaStmt: { AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); n->objectType = OBJECT_DOMAIN; - n->object = $3; + n->object = (Node *) $3; n->newschema = $6; n->missing_ok = false; $$ = (Node *)n; } - | ALTER EXTENSION any_name SET SCHEMA name + | ALTER EXTENSION name SET SCHEMA name { AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); n->objectType = OBJECT_EXTENSION; - n->object = $3; + n->object = (Node *) makeString($3); n->newschema = $6; n->missing_ok = false; $$ = (Node *)n; @@ -8498,19 +9099,17 @@ AlterObjectSchemaStmt: { AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); n->objectType = OBJECT_FUNCTION; - n->object = $3->funcname; - n->objarg = $3->funcargs; + n->object = (Node *) $3; n->newschema = $6; n->missing_ok = false; $$ = (Node *)n; } - | ALTER OPERATOR any_operator oper_argtypes SET SCHEMA name + | ALTER OPERATOR operator_with_argtypes SET SCHEMA name { AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); n->objectType = OBJECT_OPERATOR; - n->object = $3; - n->objarg = $4; - n->newschema = $7; + n->object = (Node *) $3; + n->newschema = $6; n->missing_ok = false; $$ = (Node *)n; } @@ -8518,7 +9117,7 @@ AlterObjectSchemaStmt: { AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); n->objectType = OBJECT_OPCLASS; - n->object = lcons(makeString($6), $4); + n->object = (Node *) lcons(makeString($6), $4); n->newschema = $9; n->missing_ok = false; $$ = (Node *)n; @@ -8527,7 +9126,7 @@ AlterObjectSchemaStmt: { AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); n->objectType = OBJECT_OPFAMILY; - n->object = lcons(makeString($6), $4); + n->object = (Node *) lcons(makeString($6), $4); n->newschema = $9; n->missing_ok = false; $$ = (Node *)n; @@ -8550,11 +9149,20 @@ AlterObjectSchemaStmt: n->missing_ok = true; $$ = (Node *)n; } + | ALTER STATISTICS any_name SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_STATISTIC_EXT; + n->object = (Node *) $3; + n->newschema = $6; + n->missing_ok = false; + $$ = (Node *)n; + } | ALTER TEXT_P SEARCH PARSER any_name SET SCHEMA name { AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); n->objectType = OBJECT_TSPARSER; - n->object = $5; + n->object = (Node *) $5; n->newschema = $8; n->missing_ok = false; $$ = (Node *)n; @@ -8563,7 +9171,7 @@ AlterObjectSchemaStmt: { AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); n->objectType = OBJECT_TSDICTIONARY; - n->object = $5; + n->object = (Node *) $5; n->newschema = $8; n->missing_ok = false; $$ = (Node *)n; @@ -8572,7 +9180,7 @@ AlterObjectSchemaStmt: { AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); n->objectType = OBJECT_TSTEMPLATE; - n->object = $5; + n->object = (Node *) $5; n->newschema = $8; n->missing_ok = false; $$ = (Node *)n; @@ -8581,7 +9189,7 @@ AlterObjectSchemaStmt: { AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); n->objectType = OBJECT_TSCONFIGURATION; - n->object = $5; + n->object = (Node *) $5; n->newschema = $8; n->missing_ok = false; $$ = (Node *)n; @@ -8662,7 +9270,7 @@ AlterObjectSchemaStmt: { AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); n->objectType = OBJECT_TYPE; - n->object = $3; + n->object = (Node *) $3; n->newschema = $6; n->missing_ok = false; $$ = (Node *)n; @@ -8676,12 +9284,11 @@ AlterObjectSchemaStmt: *****************************************************************************/ AlterOperatorStmt: - ALTER OPERATOR any_operator oper_argtypes SET '(' operator_def_list ')' + ALTER OPERATOR operator_with_argtypes SET '(' operator_def_list ')' { AlterOperatorStmt *n = makeNode(AlterOperatorStmt); n->opername = $3; - n->operargs = $4; - n->options = $7; + n->options = $6; $$ = (Node *)n; } ; @@ -8691,9 +9298,18 @@ operator_def_list: operator_def_elem { $$ = list_make1($1); } ; operator_def_elem: ColLabel '=' NONE - { $$ = makeDefElem($1, NULL); } - | ColLabel '=' def_arg - { $$ = makeDefElem($1, (Node *) $3); } + { $$ = makeDefElem($1, NULL, @1); } + | ColLabel '=' operator_def_arg + { $$ = makeDefElem($1, (Node *) $3, @1); } + ; + +/* must be similar enough to def_arg to avoid reduce/reduce conflicts */ +operator_def_arg: + func_type { $$ = (Node *)$1; } + | reserved_keyword { $$ = (Node *)makeString(pstrdup($1)); } + | qual_all_Op { $$ = (Node *)$1; } + | NumericOnly { $$ = (Node *)$1; } + | Sconst { $$ = (Node *)makeString($1); } ; /***************************************************************************** @@ -8702,20 +9318,19 @@ operator_def_elem: ColLabel '=' NONE * *****************************************************************************/ -AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleSpec +AlterOwnerStmt: ALTER AGGREGATE aggregate_with_argtypes OWNER TO RoleSpec { AlterOwnerStmt *n = makeNode(AlterOwnerStmt); n->objectType = OBJECT_AGGREGATE; - n->object = $3; - n->objarg = extractAggrArgTypes($4); - n->newowner = $7; + n->object = (Node *) $3; + n->newowner = $6; $$ = (Node *)n; } | ALTER COLLATION any_name OWNER TO RoleSpec { AlterOwnerStmt *n = makeNode(AlterOwnerStmt); n->objectType = OBJECT_COLLATION; - n->object = $3; + n->object = (Node *) $3; n->newowner = $6; $$ = (Node *)n; } @@ -8723,7 +9338,7 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleSpec { AlterOwnerStmt *n = makeNode(AlterOwnerStmt); n->objectType = OBJECT_CONVERSION; - n->object = $3; + n->object = (Node *) $3; n->newowner = $6; $$ = (Node *)n; } @@ -8731,7 +9346,7 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleSpec { AlterOwnerStmt *n = makeNode(AlterOwnerStmt); n->objectType = OBJECT_DATABASE; - n->object = list_make1(makeString($3)); + n->object = (Node *) makeString($3); n->newowner = $6; $$ = (Node *)n; } @@ -8739,7 +9354,7 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleSpec { AlterOwnerStmt *n = makeNode(AlterOwnerStmt); n->objectType = OBJECT_DOMAIN; - n->object = $3; + n->object = (Node *) $3; n->newowner = $6; $$ = (Node *)n; } @@ -8747,8 +9362,7 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleSpec { AlterOwnerStmt *n = makeNode(AlterOwnerStmt); n->objectType = OBJECT_FUNCTION; - n->object = $3->funcname; - n->objarg = $3->funcargs; + n->object = (Node *) $3; n->newowner = $6; $$ = (Node *)n; } @@ -8756,7 +9370,7 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleSpec { AlterOwnerStmt *n = makeNode(AlterOwnerStmt); n->objectType = OBJECT_LANGUAGE; - n->object = list_make1(makeString($4)); + n->object = (Node *) makeString($4); n->newowner = $7; $$ = (Node *)n; } @@ -8764,24 +9378,23 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleSpec { AlterOwnerStmt *n = makeNode(AlterOwnerStmt); n->objectType = OBJECT_LARGEOBJECT; - n->object = list_make1($4); + n->object = (Node *) $4; n->newowner = $7; $$ = (Node *)n; } - | ALTER OPERATOR any_operator oper_argtypes OWNER TO RoleSpec + | ALTER OPERATOR operator_with_argtypes OWNER TO RoleSpec { AlterOwnerStmt *n = makeNode(AlterOwnerStmt); n->objectType = OBJECT_OPERATOR; - n->object = $3; - n->objarg = $4; - n->newowner = $7; + n->object = (Node *) $3; + n->newowner = $6; $$ = (Node *)n; } | ALTER OPERATOR CLASS any_name USING access_method OWNER TO RoleSpec { AlterOwnerStmt *n = makeNode(AlterOwnerStmt); n->objectType = OBJECT_OPCLASS; - n->object = lcons(makeString($6), $4); + n->object = (Node *) lcons(makeString($6), $4); n->newowner = $9; $$ = (Node *)n; } @@ -8789,7 +9402,7 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleSpec { AlterOwnerStmt *n = makeNode(AlterOwnerStmt); n->objectType = OBJECT_OPFAMILY; - n->object = lcons(makeString($6), $4); + n->object = (Node *) lcons(makeString($6), $4); n->newowner = $9; $$ = (Node *)n; } @@ -8797,7 +9410,7 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleSpec { AlterOwnerStmt *n = makeNode(AlterOwnerStmt); n->objectType = OBJECT_SCHEMA; - n->object = list_make1(makeString($3)); + n->object = (Node *) makeString($3); n->newowner = $6; $$ = (Node *)n; } @@ -8805,7 +9418,7 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleSpec { AlterOwnerStmt *n = makeNode(AlterOwnerStmt); n->objectType = OBJECT_TYPE; - n->object = $3; + n->object = (Node *) $3; n->newowner = $6; $$ = (Node *)n; } @@ -8813,7 +9426,15 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleSpec { AlterOwnerStmt *n = makeNode(AlterOwnerStmt); n->objectType = OBJECT_TABLESPACE; - n->object = list_make1(makeString($3)); + n->object = (Node *) makeString($3); + n->newowner = $6; + $$ = (Node *)n; + } + | ALTER STATISTICS any_name OWNER TO RoleSpec + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_STATISTIC_EXT; + n->object = (Node *) $3; n->newowner = $6; $$ = (Node *)n; } @@ -8821,7 +9442,7 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleSpec { AlterOwnerStmt *n = makeNode(AlterOwnerStmt); n->objectType = OBJECT_TSDICTIONARY; - n->object = $5; + n->object = (Node *) $5; n->newowner = $8; $$ = (Node *)n; } @@ -8829,7 +9450,7 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleSpec { AlterOwnerStmt *n = makeNode(AlterOwnerStmt); n->objectType = OBJECT_TSCONFIGURATION; - n->object = $5; + n->object = (Node *) $5; n->newowner = $8; $$ = (Node *)n; } @@ -8837,7 +9458,7 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleSpec { AlterOwnerStmt *n = makeNode(AlterOwnerStmt); n->objectType = OBJECT_FDW; - n->object = list_make1(makeString($5)); + n->object = (Node *) makeString($5); n->newowner = $8; $$ = (Node *)n; } @@ -8845,7 +9466,7 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleSpec { AlterOwnerStmt *n = makeNode(AlterOwnerStmt); n->objectType = OBJECT_FOREIGN_SERVER; - n->object = list_make1(makeString($3)); + n->object = (Node *) makeString($3); n->newowner = $6; $$ = (Node *)n; } @@ -8853,15 +9474,252 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleSpec { AlterOwnerStmt *n = makeNode(AlterOwnerStmt); n->objectType = OBJECT_EVENT_TRIGGER; - n->object = list_make1(makeString($4)); + n->object = (Node *) makeString($4); n->newowner = $7; $$ = (Node *)n; } + | ALTER PUBLICATION name OWNER TO RoleSpec + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_PUBLICATION; + n->object = (Node *) makeString($3); + n->newowner = $6; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name OWNER TO RoleSpec + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_SUBSCRIPTION; + n->object = (Node *) makeString($3); + n->newowner = $6; + $$ = (Node *)n; + } ; /***************************************************************************** * + * CREATE PUBLICATION name [ FOR TABLE ] [ WITH options ] + * + *****************************************************************************/ + +CreatePublicationStmt: + CREATE PUBLICATION name opt_publication_for_tables opt_definition + { + CreatePublicationStmt *n = makeNode(CreatePublicationStmt); + n->pubname = $3; + n->options = $5; + if ($4 != NULL) + { + /* FOR TABLE */ + if (IsA($4, List)) + n->tables = (List *)$4; + /* FOR ALL TABLES */ + else + n->for_all_tables = TRUE; + } + $$ = (Node *)n; + } + ; + +opt_publication_for_tables: + publication_for_tables { $$ = $1; } + | /* EMPTY */ { $$ = NULL; } + ; + +publication_for_tables: + FOR TABLE relation_expr_list + { + $$ = (Node *) $3; + } + | FOR ALL TABLES + { + $$ = (Node *) makeInteger(TRUE); + } + ; + + +/***************************************************************************** + * + * ALTER PUBLICATION name SET ( options ) + * + * ALTER PUBLICATION name ADD TABLE table [, table2] + * + * ALTER PUBLICATION name DROP TABLE table [, table2] + * + * ALTER PUBLICATION name SET TABLE table [, table2] + * + *****************************************************************************/ + +AlterPublicationStmt: + ALTER PUBLICATION name SET definition + { + AlterPublicationStmt *n = makeNode(AlterPublicationStmt); + n->pubname = $3; + n->options = $5; + $$ = (Node *)n; + } + | ALTER PUBLICATION name ADD_P TABLE relation_expr_list + { + AlterPublicationStmt *n = makeNode(AlterPublicationStmt); + n->pubname = $3; + n->tables = $6; + n->tableAction = DEFELEM_ADD; + $$ = (Node *)n; + } + | ALTER PUBLICATION name SET TABLE relation_expr_list + { + AlterPublicationStmt *n = makeNode(AlterPublicationStmt); + n->pubname = $3; + n->tables = $6; + n->tableAction = DEFELEM_SET; + $$ = (Node *)n; + } + | ALTER PUBLICATION name DROP TABLE relation_expr_list + { + AlterPublicationStmt *n = makeNode(AlterPublicationStmt); + n->pubname = $3; + n->tables = $6; + n->tableAction = DEFELEM_DROP; + $$ = (Node *)n; + } + ; + +/***************************************************************************** + * + * CREATE SUBSCRIPTION name ... + * + *****************************************************************************/ + +CreateSubscriptionStmt: + CREATE SUBSCRIPTION name CONNECTION Sconst PUBLICATION publication_name_list opt_definition + { + CreateSubscriptionStmt *n = + makeNode(CreateSubscriptionStmt); + n->subname = $3; + n->conninfo = $5; + n->publication = $7; + n->options = $8; + $$ = (Node *)n; + } + ; + +publication_name_list: + publication_name_item + { + $$ = list_make1($1); + } + | publication_name_list ',' publication_name_item + { + $$ = lappend($1, $3); + } + ; + +publication_name_item: + ColLabel { $$ = makeString($1); }; + +/***************************************************************************** + * + * ALTER SUBSCRIPTION name ... + * + *****************************************************************************/ + +AlterSubscriptionStmt: + ALTER SUBSCRIPTION name SET definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_OPTIONS; + n->subname = $3; + n->options = $5; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name CONNECTION Sconst + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_CONNECTION; + n->subname = $3; + n->conninfo = $5; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name REFRESH PUBLICATION opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_REFRESH; + n->subname = $3; + n->options = $6; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name SET PUBLICATION publication_name_list REFRESH opt_definition + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_PUBLICATION_REFRESH; + n->subname = $3; + n->publication = $6; + n->options = $8; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name SET PUBLICATION publication_name_list SKIP REFRESH + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_PUBLICATION; + n->subname = $3; + n->publication = $6; + n->options = NIL; + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name ENABLE_P + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ENABLED; + n->subname = $3; + n->options = list_make1(makeDefElem("enabled", + (Node *)makeInteger(TRUE), @1)); + $$ = (Node *)n; + } + | ALTER SUBSCRIPTION name DISABLE_P + { + AlterSubscriptionStmt *n = + makeNode(AlterSubscriptionStmt); + n->kind = ALTER_SUBSCRIPTION_ENABLED; + n->subname = $3; + n->options = list_make1(makeDefElem("enabled", + (Node *)makeInteger(FALSE), @1)); + $$ = (Node *)n; + } + ; + +/***************************************************************************** + * + * DROP SUBSCRIPTION [ IF EXISTS ] name + * + *****************************************************************************/ + +DropSubscriptionStmt: DROP SUBSCRIPTION name opt_drop_behavior + { + DropSubscriptionStmt *n = makeNode(DropSubscriptionStmt); + n->subname = $3; + n->missing_ok = false; + n->behavior = $4; + $$ = (Node *) n; + } + | DROP SUBSCRIPTION IF_P EXISTS name opt_drop_behavior + { + DropSubscriptionStmt *n = makeNode(DropSubscriptionStmt); + n->subname = $5; + n->missing_ok = true; + n->behavior = $6; + $$ = (Node *) n; + } + ; + +/***************************************************************************** + * * QUERY: Define Rewrite Rule * *****************************************************************************/ @@ -8930,32 +9788,6 @@ opt_instead: ; -DropRuleStmt: - DROP RULE name ON any_name opt_drop_behavior - { - DropStmt *n = makeNode(DropStmt); - n->removeType = OBJECT_RULE; - n->objects = list_make1(lappend($5, makeString($3))); - n->arguments = NIL; - n->behavior = $6; - n->missing_ok = false; - n->concurrent = false; - $$ = (Node *) n; - } - | DROP RULE IF_P EXISTS name ON any_name opt_drop_behavior - { - DropStmt *n = makeNode(DropStmt); - n->removeType = OBJECT_RULE; - n->objects = list_make1(lappend($7, makeString($5))); - n->arguments = NIL; - n->behavior = $8; - n->missing_ok = true; - n->concurrent = false; - $$ = (Node *) n; - } - ; - - /***************************************************************************** * * QUERY: @@ -9059,7 +9891,7 @@ TransactionStmt: TransactionStmt *n = makeNode(TransactionStmt); n->kind = TRANS_STMT_SAVEPOINT; n->options = list_make1(makeDefElem("savepoint_name", - (Node *)makeString($2))); + (Node *)makeString($2), @1)); $$ = (Node *)n; } | RELEASE SAVEPOINT ColId @@ -9067,7 +9899,7 @@ TransactionStmt: TransactionStmt *n = makeNode(TransactionStmt); n->kind = TRANS_STMT_RELEASE; n->options = list_make1(makeDefElem("savepoint_name", - (Node *)makeString($3))); + (Node *)makeString($3), @1)); $$ = (Node *)n; } | RELEASE ColId @@ -9075,7 +9907,7 @@ TransactionStmt: TransactionStmt *n = makeNode(TransactionStmt); n->kind = TRANS_STMT_RELEASE; n->options = list_make1(makeDefElem("savepoint_name", - (Node *)makeString($2))); + (Node *)makeString($2), @1)); $$ = (Node *)n; } | ROLLBACK opt_transaction TO SAVEPOINT ColId @@ -9083,7 +9915,7 @@ TransactionStmt: TransactionStmt *n = makeNode(TransactionStmt); n->kind = TRANS_STMT_ROLLBACK_TO; n->options = list_make1(makeDefElem("savepoint_name", - (Node *)makeString($5))); + (Node *)makeString($5), @1)); $$ = (Node *)n; } | ROLLBACK opt_transaction TO ColId @@ -9091,7 +9923,7 @@ TransactionStmt: TransactionStmt *n = makeNode(TransactionStmt); n->kind = TRANS_STMT_ROLLBACK_TO; n->options = list_make1(makeDefElem("savepoint_name", - (Node *)makeString($4))); + (Node *)makeString($4), @1)); $$ = (Node *)n; } | PREPARE TRANSACTION Sconst @@ -9125,19 +9957,19 @@ opt_transaction: WORK {} transaction_mode_item: ISOLATION LEVEL iso_level { $$ = makeDefElem("transaction_isolation", - makeStringConst($3, @3)); } + makeStringConst($3, @3), @1); } | READ ONLY { $$ = makeDefElem("transaction_read_only", - makeIntConst(TRUE, @1)); } + makeIntConst(TRUE, @1), @1); } | READ WRITE { $$ = makeDefElem("transaction_read_only", - makeIntConst(FALSE, @1)); } + makeIntConst(FALSE, @1), @1); } | DEFERRABLE { $$ = makeDefElem("transaction_deferrable", - makeIntConst(TRUE, @1)); } + makeIntConst(TRUE, @1), @1); } | NOT DEFERRABLE { $$ = makeDefElem("transaction_deferrable", - makeIntConst(FALSE, @1)); } + makeIntConst(FALSE, @1), @1); } ; /* Syntax with commas is SQL-spec, without commas is Postgres historical */ @@ -9281,15 +10113,15 @@ createdb_opt_items: createdb_opt_item: createdb_opt_name opt_equal SignedIconst { - $$ = makeDefElem($1, (Node *)makeInteger($3)); + $$ = makeDefElem($1, (Node *)makeInteger($3), @1); } | createdb_opt_name opt_equal opt_boolean_or_string { - $$ = makeDefElem($1, (Node *)makeString($3)); + $$ = makeDefElem($1, (Node *)makeString($3), @1); } | createdb_opt_name opt_equal DEFAULT { - $$ = makeDefElem($1, NULL); + $$ = makeDefElem($1, NULL, @1); } ; @@ -9349,7 +10181,7 @@ AlterDatabaseStmt: AlterDatabaseStmt *n = makeNode(AlterDatabaseStmt); n->dbname = $3; n->options = list_make1(makeDefElem("tablespace", - (Node *)makeString($6))); + (Node *)makeString($6), @6)); $$ = (Node *)n; } ; @@ -9391,6 +10223,21 @@ DropdbStmt: DROP DATABASE database_name /***************************************************************************** * + * ALTER COLLATION + * + *****************************************************************************/ + +AlterCollationStmt: ALTER COLLATION any_name REFRESH VERSION_P + { + AlterCollationStmt *n = makeNode(AlterCollationStmt); + n->collname = $3; + $$ = (Node *)n; + } + ; + + +/***************************************************************************** + * * ALTER SYSTEM * * This is used to change configuration parameters persistently. @@ -9992,17 +10839,17 @@ ExplainStmt: { ExplainStmt *n = makeNode(ExplainStmt); n->query = $4; - n->options = list_make1(makeDefElem("analyze", NULL)); + n->options = list_make1(makeDefElem("analyze", NULL, @2)); if ($3) n->options = lappend(n->options, - makeDefElem("verbose", NULL)); + makeDefElem("verbose", NULL, @3)); $$ = (Node *) n; } | EXPLAIN VERBOSE ExplainableStmt { ExplainStmt *n = makeNode(ExplainStmt); n->query = $3; - n->options = list_make1(makeDefElem("verbose", NULL)); + n->options = list_make1(makeDefElem("verbose", NULL, @2)); $$ = (Node *) n; } | EXPLAIN '(' explain_option_list ')' ExplainableStmt @@ -10040,7 +10887,7 @@ explain_option_list: explain_option_elem: explain_option_name explain_option_arg { - $$ = makeDefElem($1, $2); + $$ = makeDefElem($1, $2, @1); } ; @@ -10284,12 +11131,26 @@ insert_rest: $$->cols = NIL; $$->selectStmt = $1; } + | OVERRIDING override_kind VALUE_P SelectStmt + { + $$ = makeNode(InsertStmt); + $$->cols = NIL; + $$->override = $2; + $$->selectStmt = $4; + } | '(' insert_column_list ')' SelectStmt { $$ = makeNode(InsertStmt); $$->cols = $2; $$->selectStmt = $4; } + | '(' insert_column_list ')' OVERRIDING override_kind VALUE_P SelectStmt + { + $$ = makeNode(InsertStmt); + $$->cols = $2; + $$->override = $5; + $$->selectStmt = $7; + } | DEFAULT VALUES { $$ = makeNode(InsertStmt); @@ -10298,6 +11159,11 @@ insert_rest: } ; +override_kind: + USER { $$ = OVERRIDING_USER_VALUE; } + | SYSTEM_P { $$ = OVERRIDING_SYSTEM_VALUE; } + ; + insert_column_list: insert_column_item { $$ = list_make1($1); } @@ -10471,75 +11337,24 @@ set_clause_list: ; set_clause: - single_set_clause { $$ = list_make1($1); } - | multiple_set_clause { $$ = $1; } - ; - -single_set_clause: - set_target '=' ctext_expr - { - $$ = $1; - $$->val = (Node *) $3; - } - ; - -/* - * Ideally, we'd accept any row-valued a_expr as RHS of a multiple_set_clause. - * However, per SQL spec the row-constructor case must allow DEFAULT as a row - * member, and it's pretty unclear how to do that (unless perhaps we allow - * DEFAULT in any a_expr and let parse analysis sort it out later?). For the - * moment, the planner/executor only support a subquery as a multiassignment - * source anyhow, so we need only accept ctext_row and subqueries here. - */ -multiple_set_clause: - '(' set_target_list ')' '=' ctext_row + set_target '=' a_expr { - ListCell *col_cell; - ListCell *val_cell; - - /* - * Break the ctext_row apart, merge individual expressions - * into the destination ResTargets. This is semantically - * equivalent to, and much cheaper to process than, the - * general case. - */ - if (list_length($2) != list_length($5)) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("number of columns does not match number of values"), - parser_errposition(@5))); - forboth(col_cell, $2, val_cell, $5) - { - ResTarget *res_col = (ResTarget *) lfirst(col_cell); - Node *res_val = (Node *) lfirst(val_cell); - - res_col->val = res_val; - } - - $$ = $2; + $1->val = (Node *) $3; + $$ = list_make1($1); } - | '(' set_target_list ')' '=' select_with_parens + | '(' set_target_list ')' '=' a_expr { - SubLink *sl = makeNode(SubLink); int ncolumns = list_length($2); int i = 1; ListCell *col_cell; - /* First, convert bare SelectStmt into a SubLink */ - sl->subLinkType = MULTIEXPR_SUBLINK; - sl->subLinkId = 0; /* will be assigned later */ - sl->testexpr = NULL; - sl->operName = NIL; - sl->subselect = $5; - sl->location = @5; - /* Create a MultiAssignRef source for each target */ foreach(col_cell, $2) { ResTarget *res_col = (ResTarget *) lfirst(col_cell); MultiAssignRef *r = makeNode(MultiAssignRef); - r->source = (Node *) sl; + r->source = (Node *) $5; r->colno = i; r->ncolumns = ncolumns; res_col->val = (Node *) r; @@ -11198,17 +12013,22 @@ locked_rels_list: ; +/* + * We should allow ROW '(' expr_list ')' too, but that seems to require + * making VALUES a fully reserved word, which will probably break more apps + * than allowing the noise-word is worth. + */ values_clause: - VALUES ctext_row + VALUES '(' expr_list ')' { SelectStmt *n = makeNode(SelectStmt); - n->valuesLists = list_make1($2); + n->valuesLists = list_make1($3); $$ = (Node *) n; } - | values_clause ',' ctext_row + | values_clause ',' '(' expr_list ')' { SelectStmt *n = (SelectStmt *) $1; - n->valuesLists = lappend(n->valuesLists, $3); + n->valuesLists = lappend(n->valuesLists, $4); $$ = (Node *) n; } ; @@ -11263,6 +12083,19 @@ table_ref: relation_expr opt_alias_clause n->coldeflist = lsecond($3); $$ = (Node *) n; } + | xmltable opt_alias_clause + { + RangeTableFunc *n = (RangeTableFunc *) $1; + n->alias = $2; + $$ = (Node *) n; + } + | LATERAL_P xmltable opt_alias_clause + { + RangeTableFunc *n = (RangeTableFunc *) $2; + n->lateral = true; + n->alias = $3; + $$ = (Node *) n; + } | select_with_parens opt_alias_clause { RangeSubselect *n = makeNode(RangeSubselect); @@ -11304,7 +12137,7 @@ table_ref: relation_expr opt_alias_clause n->lateral = true; n->subquery = $2; n->alias = $3; - /* same coment as above */ + /* same comment as above */ if ($3 == NULL) { if (IsA($2, SelectStmt) && @@ -11509,30 +12342,30 @@ join_qual: USING '(' name_list ')' { $$ = (Node *) $3; } relation_expr: qualified_name { - /* default inheritance */ + /* inheritance query, implicitly */ $$ = $1; - $$->inhOpt = INH_DEFAULT; + $$->inh = true; $$->alias = NULL; } | qualified_name '*' { - /* inheritance query */ + /* inheritance query, explicitly */ $$ = $1; - $$->inhOpt = INH_YES; + $$->inh = true; $$->alias = NULL; } | ONLY qualified_name { /* no inheritance */ $$ = $2; - $$->inhOpt = INH_NO; + $$->inh = false; $$->alias = NULL; } | ONLY '(' qualified_name ')' { /* no inheritance, SQL99-style syntax */ $$ = $3; - $$->inhOpt = INH_NO; + $$->inh = false; $$->alias = NULL; } ; @@ -11691,6 +12524,7 @@ TableFuncElement: ColId Typename opt_collate_clause n->is_local = true; n->is_not_null = false; n->is_from_type = false; + n->is_from_parent = false; n->storage = 0; n->raw_default = NULL; n->cooked_default = NULL; @@ -11702,6 +12536,166 @@ TableFuncElement: ColId Typename opt_collate_clause } ; +/* + * XMLTABLE + */ +xmltable: + XMLTABLE '(' c_expr xmlexists_argument COLUMNS xmltable_column_list ')' + { + RangeTableFunc *n = makeNode(RangeTableFunc); + n->rowexpr = $3; + n->docexpr = $4; + n->columns = $6; + n->namespaces = NIL; + n->location = @1; + $$ = (Node *)n; + } + | XMLTABLE '(' XMLNAMESPACES '(' xml_namespace_list ')' ',' + c_expr xmlexists_argument COLUMNS xmltable_column_list ')' + { + RangeTableFunc *n = makeNode(RangeTableFunc); + n->rowexpr = $8; + n->docexpr = $9; + n->columns = $11; + n->namespaces = $5; + n->location = @1; + $$ = (Node *)n; + } + ; + +xmltable_column_list: xmltable_column_el { $$ = list_make1($1); } + | xmltable_column_list ',' xmltable_column_el { $$ = lappend($1, $3); } + ; + +xmltable_column_el: + ColId Typename + { + RangeTableFuncCol *fc = makeNode(RangeTableFuncCol); + + fc->colname = $1; + fc->for_ordinality = false; + fc->typeName = $2; + fc->is_not_null = false; + fc->colexpr = NULL; + fc->coldefexpr = NULL; + fc->location = @1; + + $$ = (Node *) fc; + } + | ColId Typename xmltable_column_option_list + { + RangeTableFuncCol *fc = makeNode(RangeTableFuncCol); + ListCell *option; + bool nullability_seen = false; + + fc->colname = $1; + fc->typeName = $2; + fc->for_ordinality = false; + fc->is_not_null = false; + fc->colexpr = NULL; + fc->coldefexpr = NULL; + fc->location = @1; + + foreach(option, $3) + { + DefElem *defel = (DefElem *) lfirst(option); + + if (strcmp(defel->defname, "default") == 0) + { + if (fc->coldefexpr != NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("only one DEFAULT value is allowed"), + parser_errposition(defel->location))); + fc->coldefexpr = defel->arg; + } + else if (strcmp(defel->defname, "path") == 0) + { + if (fc->colexpr != NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("only one PATH value per column is allowed"), + parser_errposition(defel->location))); + fc->colexpr = defel->arg; + } + else if (strcmp(defel->defname, "is_not_null") == 0) + { + if (nullability_seen) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant NULL / NOT NULL declarations for column \"%s\"", fc->colname), + parser_errposition(defel->location))); + fc->is_not_null = intVal(defel->arg); + nullability_seen = true; + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("unrecognized column option \"%s\"", + defel->defname), + parser_errposition(defel->location))); + } + } + $$ = (Node *) fc; + } + | ColId FOR ORDINALITY + { + RangeTableFuncCol *fc = makeNode(RangeTableFuncCol); + + fc->colname = $1; + fc->for_ordinality = true; + /* other fields are ignored, initialized by makeNode */ + fc->location = @1; + + $$ = (Node *) fc; + } + ; + +xmltable_column_option_list: + xmltable_column_option_el + { $$ = list_make1($1); } + | xmltable_column_option_list xmltable_column_option_el + { $$ = lappend($1, $2); } + ; + +xmltable_column_option_el: + IDENT b_expr + { $$ = makeDefElem($1, $2, @1); } + | DEFAULT b_expr + { $$ = makeDefElem("default", $2, @1); } + | NOT NULL_P + { $$ = makeDefElem("is_not_null", (Node *) makeInteger(true), @1); } + | NULL_P + { $$ = makeDefElem("is_not_null", (Node *) makeInteger(false), @1); } + ; + +xml_namespace_list: + xml_namespace_el + { $$ = list_make1($1); } + | xml_namespace_list ',' xml_namespace_el + { $$ = lappend($1, $3); } + ; + +xml_namespace_el: + b_expr AS ColLabel + { + $$ = makeNode(ResTarget); + $$->name = $3; + $$->indirection = NIL; + $$->val = $1; + $$->location = @1; + } + | DEFAULT b_expr + { + $$ = makeNode(ResTarget); + $$->name = NULL; + $$->indirection = NIL; + $$->val = $2; + $$->location = @1; + } + ; + /***************************************************************************** * * Type syntax @@ -11998,28 +12992,20 @@ ConstCharacter: CharacterWithLength } ; -CharacterWithLength: character '(' Iconst ')' opt_charset +CharacterWithLength: character '(' Iconst ')' { - if (($5 != NULL) && (strcmp($5, "sql_text") != 0)) - $1 = psprintf("%s_%s", $1, $5); - $$ = SystemTypeName($1); $$->typmods = list_make1(makeIntConst($3, @3)); $$->location = @1; } ; -CharacterWithoutLength: character opt_charset +CharacterWithoutLength: character { - if (($2 != NULL) && (strcmp($2, "sql_text") != 0)) - $1 = psprintf("%s_%s", $1, $2); - $$ = SystemTypeName($1); - /* char defaults to char(1), varchar to no limit */ if (strcmp($1, "bpchar") == 0) $$->typmods = list_make1(makeIntConst(1, -1)); - $$->location = @1; } ; @@ -12043,11 +13029,6 @@ opt_varying: | /*EMPTY*/ { $$ = FALSE; } ; -opt_charset: - CHARACTER SET ColId { $$ = $3; } - | /*EMPTY*/ { $$ = NULL; } - ; - /* * SQL date/time types */ @@ -12599,6 +13580,20 @@ a_expr: c_expr { $$ = $1; } list_make1($1), @2), @2); } + | DEFAULT + { + /* + * The SQL spec only allows DEFAULT in "contextually typed + * expressions", but for us, it's easier to allow it in + * any a_expr and then throw error during parse analysis + * if it's in an inappropriate context. This way also + * lets us say something smarter than "syntax error". + */ + SetToDefault *n = makeNode(SetToDefault); + /* parse analysis will fill in the rest */ + n->location = @1; + $$ = (Node *)n; + } ; /* @@ -12718,7 +13713,10 @@ c_expr: columnref { $$ = $1; } * AEXPR_PAREN nodes wrapping all explicitly * parenthesized subexpressions; this prevents bogus * warnings from being issued when the ordering has - * been forced by parentheses. + * been forced by parentheses. Take care that an + * AEXPR_PAREN node has the same exprLocation as its + * child, so as not to cause surprising changes in + * error cursor positioning. * * In principle we should not be relying on a GUC to * decide whether to insert AEXPR_PAREN nodes. @@ -12727,7 +13725,8 @@ c_expr: columnref { $$ = $1; } * we'd just as soon not waste cycles on dummy parse * nodes if we don't have to. */ - $$ = (Node *) makeA_Expr(AEXPR_PAREN, NIL, $2, NULL, @1); + $$ = (Node *) makeA_Expr(AEXPR_PAREN, NIL, $2, NULL, + exprLocation($2)); } else $$ = $2; @@ -12795,8 +13794,7 @@ c_expr: columnref { $$ = $1; } } | ARRAY array_expr { - A_ArrayExpr *n = (A_ArrayExpr *) $2; - Assert(IsA(n, A_ArrayExpr)); + A_ArrayExpr *n = castNode(A_ArrayExpr, $2); /* point outermost A_ArrayExpr to the ARRAY keyword */ n->location = @1; $$ = (Node *)n; @@ -12961,143 +13959,63 @@ func_expr_common_subexpr: } | CURRENT_DATE { - /* - * Translate as "'now'::text::date". - * - * We cannot use "'now'::date" because coerce_type() will - * immediately reduce that to a constant representing - * today's date. We need to delay the conversion until - * runtime, else the wrong things will happen when - * CURRENT_DATE is used in a column default value or rule. - * - * This could be simplified if we had a way to generate - * an expression tree representing runtime application - * of type-input conversion functions. (As of PG 7.3 - * that is actually possible, but not clear that we want - * to rely on it.) - * - * The token location is attached to the run-time - * typecast, not to the Const, for the convenience of - * pg_stat_statements (which doesn't want these constructs - * to appear to be replaceable constants). - */ - Node *n; - n = makeStringConstCast("now", -1, SystemTypeName("text")); - $$ = makeTypeCast(n, SystemTypeName("date"), @1); + $$ = makeSQLValueFunction(SVFOP_CURRENT_DATE, -1, @1); } | CURRENT_TIME { - /* - * Translate as "'now'::text::timetz". - * See comments for CURRENT_DATE. - */ - Node *n; - n = makeStringConstCast("now", -1, SystemTypeName("text")); - $$ = makeTypeCast(n, SystemTypeName("timetz"), @1); + $$ = makeSQLValueFunction(SVFOP_CURRENT_TIME, -1, @1); } | CURRENT_TIME '(' Iconst ')' { - /* - * Translate as "'now'::text::timetz(n)". - * See comments for CURRENT_DATE. - */ - Node *n; - TypeName *d; - n = makeStringConstCast("now", -1, SystemTypeName("text")); - d = SystemTypeName("timetz"); - d->typmods = list_make1(makeIntConst($3, @3)); - $$ = makeTypeCast(n, d, @1); + $$ = makeSQLValueFunction(SVFOP_CURRENT_TIME_N, $3, @1); } | CURRENT_TIMESTAMP { - /* - * Translate as "now()", since we have a function that - * does exactly what is needed. - */ - $$ = (Node *) makeFuncCall(SystemFuncName("now"), NIL, @1); + $$ = makeSQLValueFunction(SVFOP_CURRENT_TIMESTAMP, -1, @1); } | CURRENT_TIMESTAMP '(' Iconst ')' { - /* - * Translate as "'now'::text::timestamptz(n)". - * See comments for CURRENT_DATE. - */ - Node *n; - TypeName *d; - n = makeStringConstCast("now", -1, SystemTypeName("text")); - d = SystemTypeName("timestamptz"); - d->typmods = list_make1(makeIntConst($3, @3)); - $$ = makeTypeCast(n, d, @1); + $$ = makeSQLValueFunction(SVFOP_CURRENT_TIMESTAMP_N, $3, @1); } | LOCALTIME { - /* - * Translate as "'now'::text::time". - * See comments for CURRENT_DATE. - */ - Node *n; - n = makeStringConstCast("now", -1, SystemTypeName("text")); - $$ = makeTypeCast((Node *)n, SystemTypeName("time"), @1); + $$ = makeSQLValueFunction(SVFOP_LOCALTIME, -1, @1); } | LOCALTIME '(' Iconst ')' { - /* - * Translate as "'now'::text::time(n)". - * See comments for CURRENT_DATE. - */ - Node *n; - TypeName *d; - n = makeStringConstCast("now", -1, SystemTypeName("text")); - d = SystemTypeName("time"); - d->typmods = list_make1(makeIntConst($3, @3)); - $$ = makeTypeCast((Node *)n, d, @1); + $$ = makeSQLValueFunction(SVFOP_LOCALTIME_N, $3, @1); } | LOCALTIMESTAMP { - /* - * Translate as "'now'::text::timestamp". - * See comments for CURRENT_DATE. - */ - Node *n; - n = makeStringConstCast("now", -1, SystemTypeName("text")); - $$ = makeTypeCast(n, SystemTypeName("timestamp"), @1); + $$ = makeSQLValueFunction(SVFOP_LOCALTIMESTAMP, -1, @1); } | LOCALTIMESTAMP '(' Iconst ')' { - /* - * Translate as "'now'::text::timestamp(n)". - * See comments for CURRENT_DATE. - */ - Node *n; - TypeName *d; - n = makeStringConstCast("now", -1, SystemTypeName("text")); - d = SystemTypeName("timestamp"); - d->typmods = list_make1(makeIntConst($3, @3)); - $$ = makeTypeCast(n, d, @1); + $$ = makeSQLValueFunction(SVFOP_LOCALTIMESTAMP_N, $3, @1); } | CURRENT_ROLE { - $$ = (Node *) makeFuncCall(SystemFuncName("current_user"), NIL, @1); + $$ = makeSQLValueFunction(SVFOP_CURRENT_ROLE, -1, @1); } | CURRENT_USER { - $$ = (Node *) makeFuncCall(SystemFuncName("current_user"), NIL, @1); + $$ = makeSQLValueFunction(SVFOP_CURRENT_USER, -1, @1); } | SESSION_USER { - $$ = (Node *) makeFuncCall(SystemFuncName("session_user"), NIL, @1); + $$ = makeSQLValueFunction(SVFOP_SESSION_USER, -1, @1); } | USER { - $$ = (Node *) makeFuncCall(SystemFuncName("current_user"), NIL, @1); + $$ = makeSQLValueFunction(SVFOP_USER, -1, @1); } | CURRENT_CATALOG { - $$ = (Node *) makeFuncCall(SystemFuncName("current_database"), NIL, @1); + $$ = makeSQLValueFunction(SVFOP_CURRENT_CATALOG, -1, @1); } | CURRENT_SCHEMA { - $$ = (Node *) makeFuncCall(SystemFuncName("current_schema"), NIL, @1); + $$ = makeSQLValueFunction(SVFOP_CURRENT_SCHEMA, -1, @1); } | CAST '(' a_expr AS Typename ')' { $$ = makeTypeCast($3, $5, @1); } @@ -13934,36 +14852,6 @@ opt_asymmetric: ASYMMETRIC | /*EMPTY*/ ; -/* - * The SQL spec defines "contextually typed value expressions" and - * "contextually typed row value constructors", which for our purposes - * are the same as "a_expr" and "row" except that DEFAULT can appear at - * the top level. - */ - -ctext_expr: - a_expr { $$ = (Node *) $1; } - | DEFAULT - { - SetToDefault *n = makeNode(SetToDefault); - n->location = @1; - $$ = (Node *) n; - } - ; - -ctext_expr_list: - ctext_expr { $$ = list_make1($1); } - | ctext_expr_list ',' ctext_expr { $$ = lappend($1, $3); } - ; - -/* - * We should allow ROW '(' ctext_expr_list ')' too, but that seems to require - * making VALUES a fully reserved word, which will probably break more apps - * than allowing the noise-word is worth. - */ -ctext_row: '(' ctext_expr_list ')' { $$ = $2; } - ; - /***************************************************************************** * @@ -14275,10 +15163,10 @@ RoleSpec: NonReservedWord } else { - n = (RoleSpec *) makeRoleSpec(ROLESPEC_CSTRING, @1); + n = makeRoleSpec(ROLESPEC_CSTRING, @1); n->rolename = pstrdup($1); } - $$ = (Node *) n; + $$ = n; } | CURRENT_USER { @@ -14371,6 +15259,7 @@ unreserved_keyword: | ASSERTION | ASSIGNMENT | AT + | ATTACH | ATTRIBUTE | BACKWARD /* PGXC_BEGIN */ @@ -14391,6 +15280,7 @@ unreserved_keyword: | CLEAN | CLOSE | CLUSTER + | COLUMNS | COMMENT | COMMENTS | COMMIT @@ -14422,6 +15312,7 @@ unreserved_keyword: | DELIMITER | DELIMITERS | DEPENDS + | DETACH | DICTIONARY | DIRECT | DISABLE_P @@ -14458,6 +15349,7 @@ unreserved_keyword: | FORWARD | FUNCTION | FUNCTIONS + | GENERATED | GLOBAL | GRANTED | HANDLER @@ -14509,6 +15401,7 @@ unreserved_keyword: | MOVE | NAME_P | NAMES + | NEW | NEXT | NO | NODE @@ -14520,11 +15413,13 @@ unreserved_keyword: | OF | OFF | OIDS + | OLD | OPERATOR | OPTION | OPTIONS | ORDINALITY | OVER + | OVERRIDING | OWNED | OWNER | PARALLEL @@ -14550,6 +15445,7 @@ unreserved_keyword: | PROCEDURAL | PROCEDURE | PROGRAM + | PUBLICATION | QUOTE /* PGXC_BEGIN */ | RANDOMLY @@ -14560,6 +15456,7 @@ unreserved_keyword: | RECHECK | RECURSIVE | REF + | REFERENCING | REFRESH | REINDEX | RELATIVE_P @@ -14580,6 +15477,7 @@ unreserved_keyword: | RULE | SAVEPOINT | SCHEMA + | SCHEMAS | SCROLL | SEARCH | SECOND_P @@ -14607,6 +15505,7 @@ unreserved_keyword: | STORAGE | STRICT_P | STRIP_P + | SUBSCRIPTION | SYSID | SYSTEM_P | TABLES @@ -14710,10 +15609,12 @@ col_name_keyword: | XMLELEMENT | XMLEXISTS | XMLFOREST + | XMLNAMESPACES | XMLPARSE | XMLPI | XMLROOT | XMLSERIALIZE + | XMLTABLE ; /* Type/function identifier --- keywords that can be type or function names. @@ -14851,6 +15752,33 @@ base_yyerror(YYLTYPE *yylloc, core_yyscan_t yyscanner, const char *msg) parser_yyerror(msg); } +static RawStmt * +makeRawStmt(Node *stmt, int stmt_location) +{ + RawStmt *rs = makeNode(RawStmt); + + rs->stmt = stmt; + rs->stmt_location = stmt_location; + rs->stmt_len = 0; /* might get changed later */ + return rs; +} + +/* Adjust a RawStmt to reflect that it doesn't run to the end of the string */ +static void +updateRawStmtEnd(RawStmt *rs, int end_location) +{ + /* + * If we already set the length, don't change it. This is for situations + * like "select foo ;; select bar" where the same statement will be last + * in the string for more than one semicolon. + */ + if (rs->stmt_len > 0) + return; + + /* OK, update length of RawStmt */ + rs->stmt_len = end_location - rs->stmt_location; +} + static Node * makeColumnRef(char *colname, List *indirection, int location, core_yyscan_t yyscanner) @@ -15022,7 +15950,7 @@ makeBoolAConst(bool state, int location) /* makeRoleSpec * Create a RoleSpec with the given type */ -static Node * +static RoleSpec * makeRoleSpec(RoleSpecType type, int location) { RoleSpec *spec = makeNode(RoleSpec); @@ -15030,7 +15958,7 @@ makeRoleSpec(RoleSpecType type, int location) spec->roletype = type; spec->location = location; - return (Node *) spec; + return spec; } /* check_qualified_name --- check the result of qualified_name production @@ -15368,6 +16296,18 @@ makeAArrayExpr(List *elements, int location) } static Node * +makeSQLValueFunction(SQLValueFunctionOp op, int32 typmod, int location) +{ + SQLValueFunction *svf = makeNode(SQLValueFunction); + + svf->op = op; + /* svf->type will be filled during parse analysis */ + svf->typmod = typmod; + svf->location = location; + return (Node *) svf; +} + +static Node * makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args, int location) { @@ -15424,7 +16364,7 @@ TableFuncTypeName(List *columns) { FunctionParameter *p = (FunctionParameter *) linitial(columns); - result = (TypeName *) copyObject(p->argType); + result = copyObject(p->argType); } else result = SystemTypeName("record"); diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c index 6876f2a3d4..9fc0371cb3 100644 --- a/src/backend/parser/parse_agg.c +++ b/src/backend/parser/parse_agg.c @@ -4,7 +4,7 @@ * handle aggregates and window functions in parser * * Portions Copyright (c) 2012-2014, TransLattice, Inc. - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -162,8 +162,7 @@ transformAggregateCall(ParseState *pstate, Aggref *agg, tlist = lappend(tlist, tle); torder = addTargetToSortList(pstate, tle, - torder, tlist, sortby, - true /* fix unknowns */ ); + torder, tlist, sortby); } /* Never any DISTINCT in an ordered-set agg */ @@ -203,7 +202,6 @@ transformAggregateCall(ParseState *pstate, Aggref *agg, aggorder, &tlist, EXPR_KIND_ORDER_BY, - true /* fix unknowns */ , true /* force SQL99 rules */ ); /* @@ -454,6 +452,7 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr) errkind = true; break; case EXPR_KIND_VALUES: + case EXPR_KIND_VALUES_SINGLE: errkind = true; break; case EXPR_KIND_CHECK_CONSTRAINT: @@ -508,6 +507,13 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr) err = _("grouping operations are not allowed in trigger WHEN conditions"); break; + case EXPR_KIND_PARTITION_EXPRESSION: + if (isAgg) + err = _("aggregate functions are not allowed in partition key expression"); + else + err = _("grouping operations are not allowed in partition key expression"); + + break; /* * There is intentionally no default: case here, so that the @@ -840,6 +846,7 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc, errkind = true; break; case EXPR_KIND_VALUES: + case EXPR_KIND_VALUES_SINGLE: errkind = true; break; case EXPR_KIND_CHECK_CONSTRAINT: @@ -865,6 +872,9 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc, case EXPR_KIND_TRIGGER_WHEN: err = _("window functions are not allowed in trigger WHEN conditions"); break; + case EXPR_KIND_PARTITION_EXPRESSION: + err = _("window functions are not allowed in partition key expression"); + break; /* * There is intentionally no default: case here, so that the diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 751de4bddb..27dd49d301 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -3,7 +3,7 @@ * parse_clause.c * handle clauses in parser * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -22,6 +22,7 @@ #include "catalog/catalog.h" #include "catalog/heap.h" #include "catalog/pg_am.h" +#include "catalog/pg_collation.h" #include "catalog/pg_constraint_fn.h" #include "catalog/pg_type.h" #include "commands/defrem.h" @@ -58,13 +59,18 @@ static Node *transformJoinUsingClause(ParseState *pstate, List *leftVars, List *rightVars); static Node *transformJoinOnClause(ParseState *pstate, JoinExpr *j, List *namespace); +static RangeTblEntry *getRTEForSpecialRelationTypes(ParseState *pstate, + RangeVar *rv); static RangeTblEntry *transformTableEntry(ParseState *pstate, RangeVar *r); static RangeTblEntry *transformCTEReference(ParseState *pstate, RangeVar *r, CommonTableExpr *cte, Index levelsup); +static RangeTblEntry *transformENRReference(ParseState *pstate, RangeVar *r); static RangeTblEntry *transformRangeSubselect(ParseState *pstate, RangeSubselect *r); static RangeTblEntry *transformRangeFunction(ParseState *pstate, RangeFunction *r); +static RangeTblEntry *transformRangeTableFunc(ParseState *pstate, + RangeTableFunc *t); static TableSampleClause *transformRangeTableSample(ParseState *pstate, RangeTableSample *rts); static Node *transformFromClauseItem(ParseState *pstate, Node *n, @@ -89,8 +95,7 @@ static int get_matching_location(int sortgroupref, 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); + List *grouplist, List *targetlist, int location); static WindowClause *findWindowClause(List *wclist, const char *name); static Node *transformFrameOffset(ParseState *pstate, int frameOptions, Node *clause); @@ -179,6 +184,14 @@ setTargetTable(ParseState *pstate, RangeVar *relation, RangeTblEntry *rte; int rtindex; + /* So far special relations are immutable; so they cannot be targets. */ + rte = getRTEForSpecialRelationTypes(pstate, relation); + if (rte != NULL) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("relation \"%s\" cannot be the target of a modifying statement", + relation->relname))); + /* Close old target; this could only happen for multi-action rules */ if (pstate->p_target_relation != NULL) heap_close(pstate->p_target_relation, NoLock); @@ -229,30 +242,6 @@ setTargetTable(ParseState *pstate, RangeVar *relation, } /* - * Simplify InhOption (yes/no/default) into boolean yes/no. - * - * The reason we do things this way is that we don't want to examine the - * SQL_inheritance option flag until parse_analyze() is run. Otherwise, - * we'd do the wrong thing with query strings that intermix SET commands - * with queries. - */ -bool -interpretInhOption(InhOption inhOpt) -{ - switch (inhOpt) - { - case INH_NO: - return false; - case INH_YES: - return true; - case INH_DEFAULT: - return SQL_inheritance; - } - elog(ERROR, "bogus InhOption value: %d", inhOpt); - return false; /* keep compiler quiet */ -} - -/* * Given a relation-options list (of DefElems), return true iff the specified * table/result set should be created with OIDs. This needs to be done after * parsing the query string because the return value can depend upon the @@ -370,7 +359,7 @@ transformJoinUsingClause(ParseState *pstate, /* Now create the lvar = rvar join condition */ e = makeSimpleA_Expr(AEXPR_OP, "=", - copyObject(lvar), copyObject(rvar), + (Node *) copyObject(lvar), (Node *) copyObject(rvar), -1); /* Prepare to combine into an AND clause, if multiple join columns */ @@ -436,8 +425,7 @@ transformTableEntry(ParseState *pstate, RangeVar *r) RangeTblEntry *rte; /* We need only build a range table entry */ - rte = addRangeTableEntry(pstate, r, r->alias, - interpretInhOption(r->inhOpt), true); + rte = addRangeTableEntry(pstate, r, r->alias, r->inh, true); return rte; } @@ -458,6 +446,20 @@ transformCTEReference(ParseState *pstate, RangeVar *r, } /* + * transformENRReference --- transform a RangeVar that references an ephemeral + * named relation + */ +static RangeTblEntry * +transformENRReference(ParseState *pstate, RangeVar *r) +{ + RangeTblEntry *rte; + + rte = addRangeTableEntryForENR(pstate, r, true); + + return rte; +} + +/* * transformRangeSubselect --- transform a sub-SELECT appearing in FROM */ static RangeTblEntry * @@ -496,19 +498,19 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r) * Analyze and transform the subquery. */ query = parse_sub_analyze(r->subquery, pstate, NULL, - isLockedRefname(pstate, r->alias->aliasname)); + isLockedRefname(pstate, r->alias->aliasname), + true); /* Restore state */ pstate->p_lateral_active = false; pstate->p_expr_kind = EXPR_KIND_NONE; /* - * Check that we got something reasonable. Many of these conditions are - * impossible given restrictions of the grammar, but check 'em anyway. + * Check that we got a SELECT. Anything else should be impossible given + * restrictions of the grammar, but check anyway. */ if (!IsA(query, Query) || - query->commandType != CMD_SELECT || - query->utilityStmt != NULL) + query->commandType != CMD_SELECT) elog(ERROR, "unexpected non-SELECT command in subquery in FROM"); /* @@ -719,6 +721,228 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r) } /* + * transformRangeTableFunc - + * Transform a raw RangeTableFunc into TableFunc. + * + * Transform the namespace clauses, the document-generating expression, the + * row-generating expression, the column-generating expressions, and the + * default value expressions. + */ +static RangeTblEntry * +transformRangeTableFunc(ParseState *pstate, RangeTableFunc *rtf) +{ + TableFunc *tf = makeNode(TableFunc); + const char *constructName; + Oid docType; + RangeTblEntry *rte; + bool is_lateral; + ListCell *col; + char **names; + int colno; + + /* Currently only XMLTABLE is supported */ + constructName = "XMLTABLE"; + docType = XMLOID; + + /* + * We make lateral_only names of this level visible, whether or not the + * RangeTableFunc is explicitly marked LATERAL. This is needed for SQL + * spec compliance and seems useful on convenience grounds for all + * functions in FROM. + * + * (LATERAL can't nest within a single pstate level, so we don't need + * save/restore logic here.) + */ + Assert(!pstate->p_lateral_active); + pstate->p_lateral_active = true; + + /* Transform and apply typecast to the row-generating expression ... */ + Assert(rtf->rowexpr != NULL); + tf->rowexpr = coerce_to_specific_type(pstate, + transformExpr(pstate, rtf->rowexpr, EXPR_KIND_FROM_FUNCTION), + TEXTOID, + constructName); + assign_expr_collations(pstate, tf->rowexpr); + + /* ... and to the document itself */ + Assert(rtf->docexpr != NULL); + tf->docexpr = coerce_to_specific_type(pstate, + transformExpr(pstate, rtf->docexpr, EXPR_KIND_FROM_FUNCTION), + docType, + constructName); + assign_expr_collations(pstate, tf->docexpr); + + /* undef ordinality column number */ + tf->ordinalitycol = -1; + + + names = palloc(sizeof(char *) * list_length(rtf->columns)); + + colno = 0; + foreach(col, rtf->columns) + { + RangeTableFuncCol *rawc = (RangeTableFuncCol *) lfirst(col); + Oid typid; + int32 typmod; + Node *colexpr; + Node *coldefexpr; + int j; + + tf->colnames = lappend(tf->colnames, + makeString(pstrdup(rawc->colname))); + + /* + * Determine the type and typmod for the new column. FOR ORDINALITY + * columns are INTEGER per spec; the others are user-specified. + */ + if (rawc->for_ordinality) + { + if (tf->ordinalitycol != -1) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("only one FOR ORDINALITY column is allowed"), + parser_errposition(pstate, rawc->location))); + + typid = INT4OID; + typmod = -1; + tf->ordinalitycol = colno; + } + else + { + if (rawc->typeName->setof) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("column \"%s\" cannot be declared SETOF", + rawc->colname), + parser_errposition(pstate, rawc->location))); + + typenameTypeIdAndMod(pstate, rawc->typeName, + &typid, &typmod); + } + + tf->coltypes = lappend_oid(tf->coltypes, typid); + tf->coltypmods = lappend_int(tf->coltypmods, typmod); + tf->colcollations = lappend_oid(tf->colcollations, + type_is_collatable(typid) ? DEFAULT_COLLATION_OID : InvalidOid); + + /* Transform the PATH and DEFAULT expressions */ + if (rawc->colexpr) + { + colexpr = coerce_to_specific_type(pstate, + transformExpr(pstate, rawc->colexpr, + EXPR_KIND_FROM_FUNCTION), + TEXTOID, + constructName); + assign_expr_collations(pstate, colexpr); + } + else + colexpr = NULL; + + if (rawc->coldefexpr) + { + coldefexpr = coerce_to_specific_type_typmod(pstate, + transformExpr(pstate, rawc->coldefexpr, + EXPR_KIND_FROM_FUNCTION), + typid, typmod, + constructName); + assign_expr_collations(pstate, coldefexpr); + } + else + coldefexpr = NULL; + + tf->colexprs = lappend(tf->colexprs, colexpr); + tf->coldefexprs = lappend(tf->coldefexprs, coldefexpr); + + if (rawc->is_not_null) + tf->notnulls = bms_add_member(tf->notnulls, colno); + + /* make sure column names are unique */ + for (j = 0; j < colno; j++) + if (strcmp(names[j], rawc->colname) == 0) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("column name \"%s\" is not unique", + rawc->colname), + parser_errposition(pstate, rawc->location))); + names[colno] = rawc->colname; + + colno++; + } + pfree(names); + + /* Namespaces, if any, also need to be transformed */ + if (rtf->namespaces != NIL) + { + ListCell *ns; + ListCell *lc2; + List *ns_uris = NIL; + List *ns_names = NIL; + bool default_ns_seen = false; + + foreach(ns, rtf->namespaces) + { + ResTarget *r = (ResTarget *) lfirst(ns); + Node *ns_uri; + + Assert(IsA(r, ResTarget)); + ns_uri = transformExpr(pstate, r->val, EXPR_KIND_FROM_FUNCTION); + ns_uri = coerce_to_specific_type(pstate, ns_uri, + TEXTOID, constructName); + assign_expr_collations(pstate, ns_uri); + ns_uris = lappend(ns_uris, ns_uri); + + /* Verify consistency of name list: no dupes, only one DEFAULT */ + if (r->name != NULL) + { + foreach(lc2, ns_names) + { + char *name = strVal(lfirst(lc2)); + + if (name == NULL) + continue; + if (strcmp(name, r->name) == 0) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("namespace name \"%s\" is not unique", + name), + parser_errposition(pstate, r->location))); + } + } + else + { + if (default_ns_seen) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("only one default namespace is allowed"), + parser_errposition(pstate, r->location))); + default_ns_seen = true; + } + + /* Note the string may be NULL */ + ns_names = lappend(ns_names, makeString(r->name)); + } + + tf->ns_uris = ns_uris; + tf->ns_names = ns_names; + } + + tf->location = rtf->location; + + pstate->p_lateral_active = false; + + /* + * Mark the RTE as LATERAL if the user said LATERAL explicitly, or if + * there are any lateral cross-references in it. + */ + is_lateral = rtf->lateral || contain_vars_of_level((Node *) tf, 0); + + rte = addRangeTableEntryForTableFunc(pstate, + tf, rtf->alias, is_lateral, true); + + return rte; +} + +/* * transformRangeTableSample --- transform a TABLESAMPLE clause * * Caller has already transformed rts->relation, we just have to validate @@ -822,6 +1046,22 @@ transformRangeTableSample(ParseState *pstate, RangeTableSample *rts) } +static RangeTblEntry * +getRTEForSpecialRelationTypes(ParseState *pstate, RangeVar *rv) +{ + CommonTableExpr *cte; + Index levelsup; + RangeTblEntry *rte = NULL; + + cte = scanNameSpaceForCTE(pstate, rv->relname, &levelsup); + if (cte) + rte = transformCTEReference(pstate, rv, cte, levelsup); + if (!rte && scanNameSpaceForENR(pstate, rv->relname)) + rte = transformENRReference(pstate, rv); + + return rte; +} + /* * transformFromClauseItem - * Transform a FROM-clause item, adding any required entries to the @@ -856,18 +1096,14 @@ transformFromClauseItem(ParseState *pstate, Node *n, RangeTblEntry *rte = NULL; int rtindex; - /* if it is an unqualified name, it might be a CTE reference */ + /* + * if it is an unqualified name, it might be a CTE or tuplestore + * reference + */ if (!rv->schemaname) - { - CommonTableExpr *cte; - Index levelsup; - - cte = scanNameSpaceForCTE(pstate, rv->relname, &levelsup); - if (cte) - rte = transformCTEReference(pstate, rv, cte, levelsup); - } + rte = getRTEForSpecialRelationTypes(pstate, rv); - /* if not found as a CTE, must be a table reference */ + /* if not found above, must be a table reference */ if (!rte) rte = transformTableEntry(pstate, rv); @@ -917,6 +1153,24 @@ transformFromClauseItem(ParseState *pstate, Node *n, rtr->rtindex = rtindex; return (Node *) rtr; } + else if (IsA(n, RangeTableFunc)) + { + /* table function is like a plain relation */ + RangeTblRef *rtr; + RangeTblEntry *rte; + int rtindex; + + rte = transformRangeTableFunc(pstate, (RangeTableFunc *) n); + /* assume new rte is at end */ + rtindex = list_length(pstate->p_rtable); + Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); + *top_rte = rte; + *top_rti = rtindex; + *namespace = list_make1(makeDefaultNSItem(rte)); + rtr = makeNode(RangeTblRef); + rtr->rtindex = rtindex; + return (Node *) rtr; + } else if (IsA(n, RangeTableSample)) { /* TABLESAMPLE clause (wrapping some other valid FROM node) */ @@ -929,12 +1183,12 @@ transformFromClauseItem(ParseState *pstate, Node *n, rel = transformFromClauseItem(pstate, rts->relation, top_rte, top_rti, namespace); /* Currently, grammar could only return a RangeVar as contained rel */ - Assert(IsA(rel, RangeTblRef)); - rtr = (RangeTblRef *) rel; + rtr = castNode(RangeTblRef, rel); rte = rt_fetch(rtr->rtindex, pstate->p_rtable); /* We only support this on plain relations and matviews */ if (rte->relkind != RELKIND_RELATION && - rte->relkind != RELKIND_MATVIEW) + rte->relkind != RELKIND_MATVIEW && + rte->relkind != RELKIND_PARTITIONED_TABLE) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("TABLESAMPLE clause can only be applied to tables and materialized views"), @@ -2036,8 +2290,7 @@ transformGroupClauseExpr(List **flatresult, Bitmapset *seen_local, if (!found) *flatresult = addTargetToGroupList(pstate, tle, *flatresult, *targetlist, - exprLocation(gexpr), - true); + exprLocation(gexpr)); /* * _something_ must have assigned us a sortgroupref by now... @@ -2325,7 +2578,6 @@ transformSortClause(ParseState *pstate, List *orderlist, List **targetlist, ParseExprKind exprKind, - bool resolveUnknown, bool useSQL99) { List *sortlist = NIL; @@ -2344,8 +2596,7 @@ transformSortClause(ParseState *pstate, targetlist, exprKind); sortlist = addTargetToSortList(pstate, tle, - sortlist, *targetlist, sortby, - resolveUnknown); + sortlist, *targetlist, sortby); } return sortlist; @@ -2407,7 +2658,6 @@ transformWindowDefinitions(ParseState *pstate, windef->orderClause, targetlist, EXPR_KIND_WINDOW_ORDER, - true /* fix unknowns */ , true /* force SQL99 rules */ ); partitionClause = transformGroupClause(pstate, windef->partitionClause, @@ -2578,8 +2828,7 @@ transformDistinctClause(ParseState *pstate, continue; /* ignore junk */ result = addTargetToGroupList(pstate, tle, result, *targetlist, - exprLocation((Node *) tle->expr), - true); + exprLocation((Node *) tle->expr)); } /* @@ -2696,8 +2945,7 @@ transformDistinctOnClause(ParseState *pstate, List *distinctlist, parser_errposition(pstate, exprLocation(dexpr)))); result = addTargetToGroupList(pstate, tle, result, *targetlist, - exprLocation(dexpr), - true); + exprLocation(dexpr)); } /* @@ -2931,17 +3179,11 @@ transformOnConflictArbiter(ParseState *pstate, * list, add it to the end of the list, using the given sort ordering * info. * - * If resolveUnknown is TRUE, convert TLEs of type UNKNOWN to TEXT. If not, - * do nothing (which implies the search for a sort operator will fail). - * pstate should be provided if resolveUnknown is TRUE, but can be NULL - * otherwise. - * * Returns the updated SortGroupClause list. */ List * addTargetToSortList(ParseState *pstate, TargetEntry *tle, - List *sortlist, List *targetlist, SortBy *sortby, - bool resolveUnknown) + List *sortlist, List *targetlist, SortBy *sortby) { Oid restype = exprType((Node *) tle->expr); Oid sortop; @@ -2952,7 +3194,7 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle, ParseCallbackState pcbstate; /* if tlist item is an UNKNOWN literal, change it to TEXT */ - if (restype == UNKNOWNOID && resolveUnknown) + if (restype == UNKNOWNOID) { tle->expr = (Expr *) coerce_type(pstate, (Node *) tle->expr, restype, TEXTOID, -1, @@ -3080,22 +3322,16 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle, * to a SELECT item that matches the GROUP BY item; it'd be pretty confusing * to report such a location. * - * If resolveUnknown is TRUE, convert TLEs of type UNKNOWN to TEXT. If not, - * do nothing (which implies the search for an equality operator will fail). - * pstate should be provided if resolveUnknown is TRUE, but can be NULL - * otherwise. - * * Returns the updated SortGroupClause list. */ static List * addTargetToGroupList(ParseState *pstate, TargetEntry *tle, - List *grouplist, List *targetlist, int location, - bool resolveUnknown) + List *grouplist, List *targetlist, int location) { Oid restype = exprType((Node *) tle->expr); /* if tlist item is an UNKNOWN literal, change it to TEXT */ - if (restype == UNKNOWNOID && resolveUnknown) + if (restype == UNKNOWNOID) { tle->expr = (Expr *) coerce_type(pstate, (Node *) tle->expr, restype, TEXTOID, -1, diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 633697c8b9..cd05eac159 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -3,7 +3,7 @@ * parse_coerce.c * handle type coercions/conversions for parser * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -1084,8 +1084,9 @@ coerce_to_boolean(ParseState *pstate, Node *node, ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), /* translator: first %s is name of a SQL construct, eg WHERE */ - errmsg("argument of %s must be type boolean, not type %s", - constructName, format_type_be(inputTypeId)), + errmsg("argument of %s must be type %s, not type %s", + constructName, "boolean", + format_type_be(inputTypeId)), parser_errposition(pstate, exprLocation(node)))); node = newnode; } @@ -1102,9 +1103,9 @@ coerce_to_boolean(ParseState *pstate, Node *node, } /* - * coerce_to_specific_type() - * Coerce an argument of a construct that requires a specific data type. - * Also check that input is not a set. + * coerce_to_specific_type_typmod() + * Coerce an argument of a construct that requires a specific data type, + * with a specific typmod. Also check that input is not a set. * * Returns the possibly-transformed node tree. * @@ -1112,9 +1113,9 @@ coerce_to_boolean(ParseState *pstate, Node *node, * processing is wanted. */ Node * -coerce_to_specific_type(ParseState *pstate, Node *node, - Oid targetTypeId, - const char *constructName) +coerce_to_specific_type_typmod(ParseState *pstate, Node *node, + Oid targetTypeId, int32 targetTypmod, + const char *constructName) { Oid inputTypeId = exprType(node); @@ -1123,7 +1124,7 @@ coerce_to_specific_type(ParseState *pstate, Node *node, Node *newnode; newnode = coerce_to_target_type(pstate, node, inputTypeId, - targetTypeId, -1, + targetTypeId, targetTypmod, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST, -1); @@ -1150,6 +1151,25 @@ coerce_to_specific_type(ParseState *pstate, Node *node, return node; } +/* + * coerce_to_specific_type() + * Coerce an argument of a construct that requires a specific data type. + * Also check that input is not a set. + * + * Returns the possibly-transformed node tree. + * + * As with coerce_type, pstate may be NULL if no special unknown-Param + * processing is wanted. + */ +Node * +coerce_to_specific_type(ParseState *pstate, Node *node, + Oid targetTypeId, + const char *constructName) +{ + return coerce_to_specific_type_typmod(pstate, node, + targetTypeId, -1, + constructName); +} /* * parser_coercion_errposition - report coercion error location, if possible @@ -1702,8 +1722,8 @@ enforce_generic_type_consistency(Oid *actual_arg_types, if (!OidIsValid(array_typelem)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("argument declared \"anyarray\" is not an array but type %s", - format_type_be(array_typeid)))); + errmsg("argument declared %s is not an array but type %s", + "anyarray", format_type_be(array_typeid)))); } if (!OidIsValid(elem_typeid)) @@ -1718,7 +1738,8 @@ enforce_generic_type_consistency(Oid *actual_arg_types, /* otherwise, they better match */ ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("argument declared \"anyarray\" is not consistent with argument declared \"anyelement\""), + errmsg("argument declared %s is not consistent with argument declared %s", + "anyarray", "anyelement"), errdetail("%s versus %s", format_type_be(array_typeid), format_type_be(elem_typeid)))); @@ -1739,8 +1760,9 @@ enforce_generic_type_consistency(Oid *actual_arg_types, if (!OidIsValid(range_typelem)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("argument declared \"anyrange\" is not a range type but type %s", - format_type_be(range_typeid)))); + errmsg("argument declared %s is not a range type but type %s", + "anyrange", + format_type_be(range_typeid)))); } if (!OidIsValid(elem_typeid)) @@ -1755,7 +1777,8 @@ enforce_generic_type_consistency(Oid *actual_arg_types, /* otherwise, they better match */ ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("argument declared \"anyrange\" is not consistent with argument declared \"anyelement\""), + errmsg("argument declared %s is not consistent with argument declared %s", + "anyrange", "anyelement"), errdetail("%s versus %s", format_type_be(range_typeid), format_type_be(elem_typeid)))); @@ -1775,7 +1798,8 @@ enforce_generic_type_consistency(Oid *actual_arg_types, /* Only way to get here is if all the generic args are UNKNOWN */ ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("could not determine polymorphic type because input has type \"unknown\""))); + errmsg("could not determine polymorphic type because input has type %s", + "unknown"))); } } @@ -1913,8 +1937,8 @@ resolve_generic_type(Oid declared_type, if (!OidIsValid(array_typelem)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("argument declared \"anyarray\" is not an array but type %s", - format_type_be(context_base_type)))); + errmsg("argument declared %s is not an array but type %s", + "anyarray", format_type_be(context_base_type)))); return context_base_type; } else if (context_declared_type == ANYELEMENTOID || @@ -1947,8 +1971,8 @@ resolve_generic_type(Oid declared_type, if (!OidIsValid(array_typelem)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("argument declared \"anyarray\" is not an array but type %s", - format_type_be(context_base_type)))); + errmsg("argument declared %s is not an array but type %s", + "anyarray", format_type_be(context_base_type)))); return array_typelem; } else if (context_declared_type == ANYRANGEOID) @@ -1960,8 +1984,8 @@ resolve_generic_type(Oid declared_type, if (!OidIsValid(range_typelem)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("argument declared \"anyrange\" is not a range type but type %s", - format_type_be(context_base_type)))); + errmsg("argument declared %s is not a range type but type %s", + "anyrange", format_type_be(context_base_type)))); return range_typelem; } else if (context_declared_type == ANYELEMENTOID || diff --git a/src/backend/parser/parse_collate.c b/src/backend/parser/parse_collate.c index a19e8dd075..aa443f23ad 100644 --- a/src/backend/parser/parse_collate.c +++ b/src/backend/parser/parse_collate.c @@ -29,7 +29,7 @@ * at runtime. If we knew exactly which functions require collation * information, we could throw those errors at parse time instead. * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -514,8 +514,7 @@ assign_collations_walker(Node *node, assign_collations_context *context) if (qtree->targetList == NIL) return false; - tent = (TargetEntry *) linitial(qtree->targetList); - Assert(IsA(tent, TargetEntry)); + tent = linitial_node(TargetEntry, qtree->targetList); if (tent->resjunk) return false; @@ -650,9 +649,7 @@ assign_collations_walker(Node *node, assign_collations_context *context) foreach(lc, expr->args) { - CaseWhen *when = (CaseWhen *) lfirst(lc); - - Assert(IsA(when, CaseWhen)); + CaseWhen *when = lfirst_node(CaseWhen, lc); /* * The condition expressions mustn't affect @@ -805,7 +802,7 @@ merge_collation_state(Oid collation, else if (collation != DEFAULT_COLLATION_OID) { /* - * Ooops, we have a conflict. We cannot throw error + * Oops, we have a conflict. We cannot throw error * here, since the conflict could be resolved by a * later sibling CollateExpr, or the parent might not * care about collation anyway. Return enough info to @@ -824,7 +821,7 @@ merge_collation_state(Oid collation, if (collation != context->collation) { /* - * Ooops, we have a conflict of explicit COLLATE clauses. + * Oops, we have a conflict of explicit COLLATE clauses. * Here we choose to throw error immediately; that is what * the SQL standard says to do, and there's no good reason * to be less strict. @@ -868,9 +865,8 @@ assign_aggregate_collations(Aggref *aggref, /* Process aggregated args, holding resjunk ones at arm's length */ foreach(lc, aggref->args) { - TargetEntry *tle = (TargetEntry *) lfirst(lc); + TargetEntry *tle = lfirst_node(TargetEntry, lc); - Assert(IsA(tle, TargetEntry)); if (tle->resjunk) assign_expr_collations(loccontext->pstate, (Node *) tle); else @@ -913,9 +909,8 @@ assign_ordered_set_collations(Aggref *aggref, /* Process aggregated args appropriately */ foreach(lc, aggref->args) { - TargetEntry *tle = (TargetEntry *) lfirst(lc); + TargetEntry *tle = lfirst_node(TargetEntry, lc); - Assert(IsA(tle, TargetEntry)); if (merge_sort_collations) (void) assign_collations_walker((Node *) tle, loccontext); else diff --git a/src/backend/parser/parse_cte.c b/src/backend/parser/parse_cte.c index 5e8bcd40d5..dfbcaa2cdc 100644 --- a/src/backend/parser/parse_cte.c +++ b/src/backend/parser/parse_cte.c @@ -3,7 +3,7 @@ * parse_cte.c * handle CTEs (common table expressions) in parser * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -241,7 +241,7 @@ analyzeCTE(ParseState *pstate, CommonTableExpr *cte) /* Analysis not done already */ Assert(!IsA(cte->ctequery, Query)); - query = parse_sub_analyze(cte->ctequery, pstate, cte, false); + query = parse_sub_analyze(cte->ctequery, pstate, cte, false, true); cte->ctequery = (Node *) query; /* @@ -393,11 +393,10 @@ analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist) /* * If the CTE is recursive, force the exposed column type of any - * "unknown" column to "text". This corresponds to the fact that - * SELECT 'foo' UNION SELECT 'bar' will ultimately produce text. We - * might see "unknown" as a result of an untyped literal in the - * non-recursive term's select list, and if we don't convert to text - * then we'll have a mismatch against the UNION result. + * "unknown" column to "text". We must deal with this here because + * we're called on the non-recursive term before there's been any + * attempt to force unknown output columns to some other type. We + * have to resolve unknowns before looking at the recursive term. * * The column might contain 'foo' COLLATE "bar", so don't override * collation if it's already set. diff --git a/src/backend/parser/parse_enr.c b/src/backend/parser/parse_enr.c new file mode 100644 index 0000000000..1cfcf65a51 --- /dev/null +++ b/src/backend/parser/parse_enr.c @@ -0,0 +1,29 @@ +/*------------------------------------------------------------------------- + * + * parse_enr.c + * parser support routines dealing with ephemeral named relations + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/parser/parse_enr.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "parser/parse_enr.h" + +bool +name_matches_visible_ENR(ParseState *pstate, const char *refname) +{ + return (get_visible_ENR_metadata(pstate->p_queryEnv, refname) != NULL); +} + +EphemeralNamedRelationMetadata +get_visible_ENR(ParseState *pstate, const char *refname) +{ + return get_visible_ENR_metadata(pstate->p_queryEnv, refname); +} diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index e54738bbff..8e2ae0e11c 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -3,7 +3,7 @@ * parse_expr.c * handle expressions in parser * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -34,7 +34,9 @@ #include "parser/parse_type.h" #include "parser/parse_agg.h" #include "utils/builtins.h" +#include "utils/date.h" #include "utils/lsyscache.h" +#include "utils/timestamp.h" #include "utils/xml.h" @@ -104,9 +106,11 @@ static Node *transformCaseExpr(ParseState *pstate, CaseExpr *c); static Node *transformSubLink(ParseState *pstate, SubLink *sublink); static Node *transformArrayExpr(ParseState *pstate, A_ArrayExpr *a, Oid array_type, Oid element_type, int32 typmod); -static Node *transformRowExpr(ParseState *pstate, RowExpr *r); +static Node *transformRowExpr(ParseState *pstate, RowExpr *r, bool allowDefault); static Node *transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c); static Node *transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m); +static Node *transformSQLValueFunction(ParseState *pstate, + SQLValueFunction *svf); static Node *transformXmlExpr(ParseState *pstate, XmlExpr *x); static Node *transformXmlSerialize(ParseState *pstate, XmlSerialize *xs); static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b); @@ -295,7 +299,7 @@ transformExprRecurse(ParseState *pstate, Node *expr) break; case T_RowExpr: - result = transformRowExpr(pstate, (RowExpr *) expr); + result = transformRowExpr(pstate, (RowExpr *) expr, false); break; case T_CoalesceExpr: @@ -306,6 +310,11 @@ transformExprRecurse(ParseState *pstate, Node *expr) result = transformMinMaxExpr(pstate, (MinMaxExpr *) expr); break; + case T_SQLValueFunction: + result = transformSQLValueFunction(pstate, + (SQLValueFunction *) expr); + break; + case T_XmlExpr: result = transformXmlExpr(pstate, (XmlExpr *) expr); break; @@ -339,8 +348,20 @@ transformExprRecurse(ParseState *pstate, Node *expr) break; /* - * CaseTestExpr and SetToDefault don't require any processing; - * they are only injected into parse trees in fully-formed state. + * In all places where DEFAULT is legal, the caller should have + * processed it rather than passing it to transformExpr(). + */ + case T_SetToDefault: + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("DEFAULT is not allowed in this context"), + parser_errposition(pstate, + ((SetToDefault *) expr)->location))); + break; + + /* + * CaseTestExpr doesn't require any processing; it is only + * injected into parse trees in a fully-formed state. * * Ordinarily we should not see a Var here, but it is convenient * for transformJoinUsingClause() to create untransformed operator @@ -349,7 +370,6 @@ transformExprRecurse(ParseState *pstate, Node *expr) * references, which seems expensively pointless. So allow it. */ case T_CaseTestExpr: - case T_SetToDefault: case T_Var: { result = (Node *) expr; @@ -557,27 +577,6 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) /* * Not known as a column of any range-table entry. * - * Consider the possibility that it's VALUE in a domain - * check expression. (We handle VALUE as a name, not a - * keyword, to avoid breaking a lot of applications that - * have used VALUE as a column name in the past.) - */ - if (pstate->p_value_substitute != NULL && - strcmp(colname, "value") == 0) - { - node = (Node *) copyObject(pstate->p_value_substitute); - - /* - * Try to propagate location knowledge. This should - * be extended if p_value_substitute can ever take on - * other node types. - */ - if (IsA(node, CoerceToDomainValue)) - ((CoerceToDomainValue *) node)->location = cref->location; - break; - } - - /* * Try to find the name as a relation. Note that only * relations already entered into the rangetable will be * recognized. @@ -918,13 +917,11 @@ transformAExprOp(ParseState *pstate, A_Expr *a) /* ROW() op ROW() is handled specially */ lexpr = transformExprRecurse(pstate, lexpr); rexpr = transformExprRecurse(pstate, rexpr); - Assert(IsA(lexpr, RowExpr)); - Assert(IsA(rexpr, RowExpr)); result = make_row_comparison_op(pstate, a->name, - ((RowExpr *) lexpr)->args, - ((RowExpr *) rexpr)->args, + castNode(RowExpr, lexpr)->args, + castNode(RowExpr, rexpr)->args, a->location); } else @@ -1258,7 +1255,7 @@ transformAExprIn(ParseState *pstate, A_Expr *a) /* ROW() op ROW() is handled specially */ cmp = make_row_comparison_op(pstate, a->name, - (List *) copyObject(((RowExpr *) lexpr)->args), + copyObject(((RowExpr *) lexpr)->args), ((RowExpr *) rexpr)->args, a->location); } @@ -1297,8 +1294,7 @@ transformAExprBetween(ParseState *pstate, A_Expr *a) /* Deconstruct A_Expr into three subexprs */ aexpr = a->lexpr; - Assert(IsA(a->rexpr, List)); - args = (List *) a->rexpr; + args = castNode(List, a->rexpr); Assert(list_length(args) == 2); bexpr = (Node *) linitial(args); cexpr = (Node *) lsecond(args); @@ -1478,9 +1474,9 @@ static Node * transformMultiAssignRef(ParseState *pstate, MultiAssignRef *maref) { SubLink *sublink; + RowExpr *rexpr; Query *qtree; TargetEntry *tle; - Param *param; /* We should only see this in first-stage processing of UPDATE tlists */ Assert(pstate->p_expr_kind == EXPR_KIND_UPDATE_SOURCE); @@ -1488,64 +1484,137 @@ transformMultiAssignRef(ParseState *pstate, MultiAssignRef *maref) /* We only need to transform the source if this is the first column */ if (maref->colno == 1) { - sublink = (SubLink *) transformExprRecurse(pstate, maref->source); - /* Currently, the grammar only allows a SubLink as source */ - Assert(IsA(sublink, SubLink)); - Assert(sublink->subLinkType == MULTIEXPR_SUBLINK); - qtree = (Query *) sublink->subselect; - Assert(IsA(qtree, Query)); + /* + * For now, we only allow EXPR SubLinks and RowExprs as the source of + * an UPDATE multiassignment. This is sufficient to cover interesting + * cases; at worst, someone would have to write (SELECT * FROM expr) + * to expand a composite-returning expression of another form. + */ + if (IsA(maref->source, SubLink) && + ((SubLink *) maref->source)->subLinkType == EXPR_SUBLINK) + { + /* Relabel it as a MULTIEXPR_SUBLINK */ + sublink = (SubLink *) maref->source; + sublink->subLinkType = MULTIEXPR_SUBLINK; + /* And transform it */ + sublink = (SubLink *) transformExprRecurse(pstate, + (Node *) sublink); - /* Check subquery returns required number of columns */ - if (count_nonjunk_tlist_entries(qtree->targetList) != maref->ncolumns) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), + qtree = castNode(Query, sublink->subselect); + + /* Check subquery returns required number of columns */ + if (count_nonjunk_tlist_entries(qtree->targetList) != maref->ncolumns) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), errmsg("number of columns does not match number of values"), - parser_errposition(pstate, sublink->location))); + parser_errposition(pstate, sublink->location))); - /* - * Build a resjunk tlist item containing the MULTIEXPR SubLink, and - * add it to pstate->p_multiassign_exprs, whence it will later get - * appended to the completed targetlist. We needn't worry about - * selecting a resno for it; transformUpdateStmt will do that. - */ - tle = makeTargetEntry((Expr *) sublink, 0, NULL, true); - pstate->p_multiassign_exprs = lappend(pstate->p_multiassign_exprs, tle); + /* + * Build a resjunk tlist item containing the MULTIEXPR SubLink, + * and add it to pstate->p_multiassign_exprs, whence it will later + * get appended to the completed targetlist. We needn't worry + * about selecting a resno for it; transformUpdateStmt will do + * that. + */ + tle = makeTargetEntry((Expr *) sublink, 0, NULL, true); + pstate->p_multiassign_exprs = lappend(pstate->p_multiassign_exprs, + tle); - /* - * Assign a unique-within-this-targetlist ID to the MULTIEXPR SubLink. - * We can just use its position in the p_multiassign_exprs list. - */ - sublink->subLinkId = list_length(pstate->p_multiassign_exprs); + /* + * Assign a unique-within-this-targetlist ID to the MULTIEXPR + * SubLink. We can just use its position in the + * p_multiassign_exprs list. + */ + sublink->subLinkId = list_length(pstate->p_multiassign_exprs); + } + else if (IsA(maref->source, RowExpr)) + { + /* Transform the RowExpr, allowing SetToDefault items */ + rexpr = (RowExpr *) transformRowExpr(pstate, + (RowExpr *) maref->source, + true); + + /* Check it returns required number of columns */ + if (list_length(rexpr->args) != maref->ncolumns) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("number of columns does not match number of values"), + parser_errposition(pstate, rexpr->location))); + + /* + * Temporarily append it to p_multiassign_exprs, so we can get it + * back when we come back here for additional columns. + */ + tle = makeTargetEntry((Expr *) rexpr, 0, NULL, true); + pstate->p_multiassign_exprs = lappend(pstate->p_multiassign_exprs, + tle); + } + else + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("source for a multiple-column UPDATE item must be a sub-SELECT or ROW() expression"), + parser_errposition(pstate, exprLocation(maref->source)))); } else { /* * Second or later column in a multiassignment. Re-fetch the - * transformed query, which we assume is still the last entry in - * p_multiassign_exprs. + * transformed SubLink or RowExpr, which we assume is still the last + * entry in p_multiassign_exprs. */ Assert(pstate->p_multiassign_exprs != NIL); tle = (TargetEntry *) llast(pstate->p_multiassign_exprs); + } + + /* + * Emit the appropriate output expression for the current column + */ + if (IsA(tle->expr, SubLink)) + { + Param *param; + sublink = (SubLink *) tle->expr; - Assert(IsA(sublink, SubLink)); Assert(sublink->subLinkType == MULTIEXPR_SUBLINK); - qtree = (Query *) sublink->subselect; - Assert(IsA(qtree, Query)); + qtree = castNode(Query, sublink->subselect); + + /* Build a Param representing the current subquery output column */ + tle = (TargetEntry *) list_nth(qtree->targetList, maref->colno - 1); + Assert(!tle->resjunk); + + param = makeNode(Param); + param->paramkind = PARAM_MULTIEXPR; + param->paramid = (sublink->subLinkId << 16) | maref->colno; + param->paramtype = exprType((Node *) tle->expr); + param->paramtypmod = exprTypmod((Node *) tle->expr); + param->paramcollid = exprCollation((Node *) tle->expr); + param->location = exprLocation((Node *) tle->expr); + + return (Node *) param; } - /* Build a Param representing the appropriate subquery output column */ - tle = (TargetEntry *) list_nth(qtree->targetList, maref->colno - 1); - Assert(!tle->resjunk); + if (IsA(tle->expr, RowExpr)) + { + Node *result; + + rexpr = (RowExpr *) tle->expr; + + /* Just extract and return the next element of the RowExpr */ + result = (Node *) list_nth(rexpr->args, maref->colno - 1); + + /* + * If we're at the last column, delete the RowExpr from + * p_multiassign_exprs; we don't need it anymore, and don't want it in + * the finished UPDATE tlist. + */ + if (maref->colno == maref->ncolumns) + pstate->p_multiassign_exprs = + list_delete_ptr(pstate->p_multiassign_exprs, tle); - param = makeNode(Param); - param->paramkind = PARAM_MULTIEXPR; - param->paramid = (sublink->subLinkId << 16) | maref->colno; - param->paramtype = exprType((Node *) tle->expr); - param->paramtypmod = exprTypmod((Node *) tle->expr); - param->paramcollid = exprCollation((Node *) tle->expr); - param->location = exprLocation((Node *) tle->expr); + return result; + } - return (Node *) param; + elog(ERROR, "unexpected expr type in multiassign list"); + return NULL; /* keep compiler quiet */ } static Node * @@ -1601,12 +1670,10 @@ transformCaseExpr(ParseState *pstate, CaseExpr *c) resultexprs = NIL; foreach(l, c->args) { - CaseWhen *w = (CaseWhen *) lfirst(l); + CaseWhen *w = lfirst_node(CaseWhen, l); CaseWhen *neww = makeNode(CaseWhen); Node *warg; - Assert(IsA(w, CaseWhen)); - warg = (Node *) w->expr; if (placeholder) { @@ -1724,6 +1791,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink) case EXPR_KIND_OFFSET: case EXPR_KIND_RETURNING: case EXPR_KIND_VALUES: + case EXPR_KIND_VALUES_SINGLE: /* okay */ break; case EXPR_KIND_CHECK_CONSTRAINT: @@ -1749,6 +1817,9 @@ transformSubLink(ParseState *pstate, SubLink *sublink) case EXPR_KIND_TRIGGER_WHEN: err = _("cannot use subquery in trigger WHEN condition"); break; + case EXPR_KIND_PARTITION_EXPRESSION: + err = _("cannot use subquery in partition key expression"); + break; /* * There is intentionally no default: case here, so that the @@ -1769,15 +1840,14 @@ transformSubLink(ParseState *pstate, SubLink *sublink) /* * OK, let's transform the sub-SELECT. */ - qtree = parse_sub_analyze(sublink->subselect, pstate, NULL, false); + qtree = parse_sub_analyze(sublink->subselect, pstate, NULL, false, true); /* - * Check that we got something reasonable. Many of these conditions are - * impossible given restrictions of the grammar, but check 'em anyway. + * Check that we got a SELECT. Anything else should be impossible given + * restrictions of the grammar, but check anyway. */ if (!IsA(qtree, Query) || - qtree->commandType != CMD_SELECT || - qtree->utilityStmt != NULL) + qtree->commandType != CMD_SELECT) elog(ERROR, "unexpected non-SELECT command in SubLink"); sublink->subselect = (Node *) qtree; @@ -2073,7 +2143,7 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a, } static Node * -transformRowExpr(ParseState *pstate, RowExpr *r) +transformRowExpr(ParseState *pstate, RowExpr *r, bool allowDefault) { RowExpr *newr; char fname[16]; @@ -2083,7 +2153,8 @@ transformRowExpr(ParseState *pstate, RowExpr *r) newr = makeNode(RowExpr); /* Transform the field expressions */ - newr->args = transformExpressionList(pstate, r->args, pstate->p_expr_kind); + newr->args = transformExpressionList(pstate, r->args, + pstate->p_expr_kind, allowDefault); /* Barring later casting, we consider the type RECORD */ newr->row_typeid = RECORDOID; @@ -2180,6 +2251,59 @@ transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m) } static Node * +transformSQLValueFunction(ParseState *pstate, SQLValueFunction *svf) +{ + /* + * All we need to do is insert the correct result type and (where needed) + * validate the typmod, so we just modify the node in-place. + */ + switch (svf->op) + { + case SVFOP_CURRENT_DATE: + svf->type = DATEOID; + break; + case SVFOP_CURRENT_TIME: + svf->type = TIMETZOID; + break; + case SVFOP_CURRENT_TIME_N: + svf->type = TIMETZOID; + svf->typmod = anytime_typmod_check(true, svf->typmod); + break; + case SVFOP_CURRENT_TIMESTAMP: + svf->type = TIMESTAMPTZOID; + break; + case SVFOP_CURRENT_TIMESTAMP_N: + svf->type = TIMESTAMPTZOID; + svf->typmod = anytimestamp_typmod_check(true, svf->typmod); + break; + case SVFOP_LOCALTIME: + svf->type = TIMEOID; + break; + case SVFOP_LOCALTIME_N: + svf->type = TIMEOID; + svf->typmod = anytime_typmod_check(false, svf->typmod); + break; + case SVFOP_LOCALTIMESTAMP: + svf->type = TIMESTAMPOID; + break; + case SVFOP_LOCALTIMESTAMP_N: + svf->type = TIMESTAMPOID; + svf->typmod = anytimestamp_typmod_check(false, svf->typmod); + break; + case SVFOP_CURRENT_ROLE: + case SVFOP_CURRENT_USER: + case SVFOP_USER: + case SVFOP_SESSION_USER: + case SVFOP_CURRENT_CATALOG: + case SVFOP_CURRENT_SCHEMA: + svf->type = NAMEOID; + break; + } + + return (Node *) svf; +} + +static Node * transformXmlExpr(ParseState *pstate, XmlExpr *x) { XmlExpr *newx; @@ -2211,12 +2335,10 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x) foreach(lc, x->named_args) { - ResTarget *r = (ResTarget *) lfirst(lc); + ResTarget *r = lfirst_node(ResTarget, lc); Node *expr; char *argname; - Assert(IsA(r, ResTarget)); - expr = transformExprRecurse(pstate, r->val); if (r->name) @@ -2678,8 +2800,7 @@ make_row_comparison_op(ParseState *pstate, List *opname, Node *rarg = (Node *) lfirst(r); OpExpr *cmp; - cmp = (OpExpr *) make_op(pstate, opname, larg, rarg, location); - Assert(IsA(cmp, OpExpr)); + cmp = castNode(OpExpr, make_op(pstate, opname, larg, rarg, location)); /* * We don't use coerce_to_boolean here because we insist on the @@ -3287,6 +3408,7 @@ ParseExprKindName(ParseExprKind exprKind) case EXPR_KIND_RETURNING: return "RETURNING"; case EXPR_KIND_VALUES: + case EXPR_KIND_VALUES_SINGLE: return "VALUES"; case EXPR_KIND_CHECK_CONSTRAINT: case EXPR_KIND_DOMAIN_CHECK: @@ -3304,6 +3426,8 @@ ParseExprKindName(ParseExprKind exprKind) return "EXECUTE"; case EXPR_KIND_TRIGGER_WHEN: return "WHEN"; + case EXPR_KIND_PARTITION_EXPRESSION: + return "PARTITION BY"; /* * There is intentionally no default: case here, so that the diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 61af484fee..55853c20bb 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -3,7 +3,7 @@ * parse_func.c * handle function calls in parser * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -25,6 +25,7 @@ #include "parser/parse_agg.h" #include "parser/parse_clause.h" #include "parser/parse_coerce.h" +#include "parser/parse_expr.h" #include "parser/parse_func.h" #include "parser/parse_relation.h" #include "parser/parse_target.h" @@ -625,6 +626,10 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, exprLocation((Node *) llast(fargs))))); } + /* if it returns a set, check that's OK */ + if (retset) + check_srf_call_placement(pstate, location); + /* build the appropriate output structure */ if (fdresult == FUNCDETAIL_NORMAL) { @@ -1510,8 +1515,7 @@ func_get_detail(List *funcname, &isnull); Assert(!isnull); str = TextDatumGetCString(proargdefaults); - defaults = (List *) stringToNode(str); - Assert(IsA(defaults, List)); + defaults = castNode(List, stringToNode(str)); pfree(str); /* Delete any unused defaults from the returned list */ @@ -1891,8 +1895,10 @@ func_signature_string(List *funcname, int nargs, /* * LookupFuncName - * Given a possibly-qualified function name and a set of argument types, - * look up the function. + * + * Given a possibly-qualified function name and optionally a set of argument + * types, look up the function. Pass nargs == -1 to indicate that no argument + * types are specified. * * If the function name is not schema-qualified, it is sought in the current * namespace search path. @@ -1910,6 +1916,35 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError) clist = FuncnameGetCandidates(funcname, nargs, NIL, false, false, noError); + /* + * If no arguments were specified, the name must yield a unique candidate. + */ + if (nargs == -1) + { + if (clist) + { + if (clist->next) + { + if (!noError) + ereport(ERROR, + (errcode(ERRCODE_AMBIGUOUS_FUNCTION), + errmsg("function name \"%s\" is not unique", + NameListToString(funcname)), + errhint("Specify the argument list to select the function unambiguously."))); + } + else + return clist->oid; + } + else + { + if (!noError) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("could not find a function named \"%s\"", + NameListToString(funcname)))); + } + } + while (clist) { if (memcmp(argtypes, clist->args, nargs * sizeof(Oid)) == 0) @@ -1928,19 +1963,19 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError) } /* - * LookupFuncNameTypeNames + * LookupFuncWithArgs * Like LookupFuncName, but the argument types are specified by a - * list of TypeName nodes. + * ObjectWithArgs node. */ Oid -LookupFuncNameTypeNames(List *funcname, List *argtypes, bool noError) +LookupFuncWithArgs(ObjectWithArgs *func, bool noError) { Oid argoids[FUNC_MAX_ARGS]; int argcount; int i; ListCell *args_item; - argcount = list_length(argtypes); + argcount = list_length(func->objargs); if (argcount > FUNC_MAX_ARGS) ereport(ERROR, (errcode(ERRCODE_TOO_MANY_ARGUMENTS), @@ -1949,7 +1984,7 @@ LookupFuncNameTypeNames(List *funcname, List *argtypes, bool noError) FUNC_MAX_ARGS, FUNC_MAX_ARGS))); - args_item = list_head(argtypes); + args_item = list_head(func->objargs); for (i = 0; i < argcount; i++) { TypeName *t = (TypeName *) lfirst(args_item); @@ -1958,19 +1993,19 @@ LookupFuncNameTypeNames(List *funcname, List *argtypes, bool noError) args_item = lnext(args_item); } - return LookupFuncName(funcname, argcount, argoids, noError); + return LookupFuncName(func->objname, func->args_unspecified ? -1 : argcount, argoids, noError); } /* - * LookupAggNameTypeNames - * Find an aggregate function given a name and list of TypeName nodes. + * LookupAggWithArgs + * Find an aggregate function from a given ObjectWithArgs node. * - * This is almost like LookupFuncNameTypeNames, but the error messages refer + * This is almost like LookupFuncWithArgs, but the error messages refer * to aggregates rather than plain functions, and we verify that the found * function really is an aggregate. */ Oid -LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError) +LookupAggWithArgs(ObjectWithArgs *agg, bool noError) { Oid argoids[FUNC_MAX_ARGS]; int argcount; @@ -1980,7 +2015,7 @@ LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError) HeapTuple ftup; Form_pg_proc pform; - argcount = list_length(argtypes); + argcount = list_length(agg->objargs); if (argcount > FUNC_MAX_ARGS) ereport(ERROR, (errcode(ERRCODE_TOO_MANY_ARGUMENTS), @@ -1990,7 +2025,7 @@ LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError) FUNC_MAX_ARGS))); i = 0; - foreach(lc, argtypes) + foreach(lc, agg->objargs) { TypeName *t = (TypeName *) lfirst(lc); @@ -1998,7 +2033,7 @@ LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError) i++; } - oid = LookupFuncName(aggname, argcount, argoids, true); + oid = LookupFuncName(agg->objname, argcount, argoids, true); if (!OidIsValid(oid)) { @@ -2008,12 +2043,12 @@ LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("aggregate %s(*) does not exist", - NameListToString(aggname)))); + NameListToString(agg->objname)))); else ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("aggregate %s does not exist", - func_signature_string(aggname, argcount, + func_signature_string(agg->objname, argcount, NIL, argoids)))); } @@ -2032,7 +2067,7 @@ LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("function %s is not an aggregate", - func_signature_string(aggname, argcount, + func_signature_string(agg->objname, argcount, NIL, argoids)))); } @@ -2040,3 +2075,154 @@ LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError) return oid; } + + +/* + * check_srf_call_placement + * Verify that a set-returning function is called in a valid place, + * and throw a nice error if not. + * + * A side-effect is to set pstate->p_hasTargetSRFs true if appropriate. + */ +void +check_srf_call_placement(ParseState *pstate, int location) +{ + const char *err; + bool errkind; + + /* + * Check to see if the set-returning function is in an invalid place + * within the query. Basically, we don't allow SRFs anywhere except in + * the targetlist (which includes GROUP BY/ORDER BY expressions), VALUES, + * and functions in FROM. + * + * For brevity we support two schemes for reporting an error here: set + * "err" to a custom message, or set "errkind" true if the error context + * is sufficiently identified by what ParseExprKindName will return, *and* + * what it will return is just a SQL keyword. (Otherwise, use a custom + * message to avoid creating translation problems.) + */ + err = NULL; + errkind = false; + switch (pstate->p_expr_kind) + { + case EXPR_KIND_NONE: + Assert(false); /* can't happen */ + break; + case EXPR_KIND_OTHER: + /* Accept SRF here; caller must throw error if wanted */ + break; + case EXPR_KIND_JOIN_ON: + case EXPR_KIND_JOIN_USING: + err = _("set-returning functions are not allowed in JOIN conditions"); + break; + case EXPR_KIND_FROM_SUBSELECT: + /* can't get here, but just in case, throw an error */ + errkind = true; + break; + case EXPR_KIND_FROM_FUNCTION: + /* okay ... but we can't check nesting here */ + break; + case EXPR_KIND_WHERE: + errkind = true; + break; + case EXPR_KIND_POLICY: + err = _("set-returning functions are not allowed in policy expressions"); + break; + case EXPR_KIND_HAVING: + errkind = true; + break; + case EXPR_KIND_FILTER: + errkind = true; + break; + case EXPR_KIND_WINDOW_PARTITION: + case EXPR_KIND_WINDOW_ORDER: + /* okay, these are effectively GROUP BY/ORDER BY */ + pstate->p_hasTargetSRFs = true; + break; + case EXPR_KIND_WINDOW_FRAME_RANGE: + case EXPR_KIND_WINDOW_FRAME_ROWS: + err = _("set-returning functions are not allowed in window definitions"); + break; + case EXPR_KIND_SELECT_TARGET: + case EXPR_KIND_INSERT_TARGET: + /* okay */ + pstate->p_hasTargetSRFs = true; + break; + case EXPR_KIND_UPDATE_SOURCE: + case EXPR_KIND_UPDATE_TARGET: + /* disallowed because it would be ambiguous what to do */ + errkind = true; + break; + case EXPR_KIND_GROUP_BY: + case EXPR_KIND_ORDER_BY: + /* okay */ + pstate->p_hasTargetSRFs = true; + break; + case EXPR_KIND_DISTINCT_ON: + /* okay */ + pstate->p_hasTargetSRFs = true; + break; + case EXPR_KIND_LIMIT: + case EXPR_KIND_OFFSET: + errkind = true; + break; + case EXPR_KIND_RETURNING: + errkind = true; + break; + case EXPR_KIND_VALUES: + /* SRFs are presently not supported by nodeValuesscan.c */ + errkind = true; + break; + case EXPR_KIND_VALUES_SINGLE: + /* okay, since we process this like a SELECT tlist */ + pstate->p_hasTargetSRFs = true; + break; + case EXPR_KIND_CHECK_CONSTRAINT: + case EXPR_KIND_DOMAIN_CHECK: + err = _("set-returning functions are not allowed in check constraints"); + break; + case EXPR_KIND_COLUMN_DEFAULT: + case EXPR_KIND_FUNCTION_DEFAULT: + err = _("set-returning functions are not allowed in DEFAULT expressions"); + break; + case EXPR_KIND_INDEX_EXPRESSION: + err = _("set-returning functions are not allowed in index expressions"); + break; + case EXPR_KIND_INDEX_PREDICATE: + err = _("set-returning functions are not allowed in index predicates"); + break; + case EXPR_KIND_ALTER_COL_TRANSFORM: + err = _("set-returning functions are not allowed in transform expressions"); + break; + case EXPR_KIND_EXECUTE_PARAMETER: + err = _("set-returning functions are not allowed in EXECUTE parameters"); + break; + case EXPR_KIND_TRIGGER_WHEN: + err = _("set-returning functions are not allowed in trigger WHEN conditions"); + break; + case EXPR_KIND_PARTITION_EXPRESSION: + err = _("set-returning functions are not allowed in partition key expression"); + break; + + /* + * There is intentionally no default: case here, so that the + * compiler will warn if we add a new ParseExprKind without + * extending this switch. If we do see an unrecognized value at + * runtime, the behavior will be the same as for EXPR_KIND_OTHER, + * which is sane anyway. + */ + } + if (err) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg_internal("%s", err), + parser_errposition(pstate, location))); + if (errkind) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + /* translator: %s is name of a SQL construct, eg GROUP BY */ + errmsg("set-returning functions are not allowed in %s", + ParseExprKindName(pstate->p_expr_kind)), + parser_errposition(pstate, location))); +} diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c index 62d2f7105f..fb3d117a7d 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -3,7 +3,7 @@ * parse_node.c * various routines that make nodes for querytrees * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -51,6 +51,7 @@ make_parsestate(ParseState *parentParseState) /* Fill in fields that don't start at null/false/zero */ pstate->p_next_resno = 1; + pstate->p_resolve_unknowns = true; if (parentParseState) { @@ -61,6 +62,8 @@ make_parsestate(ParseState *parentParseState) pstate->p_paramref_hook = parentParseState->p_paramref_hook; pstate->p_coerce_param_hook = parentParseState->p_coerce_param_hook; pstate->p_ref_hook_state = parentParseState->p_ref_hook_state; + /* query environment stays in context for the whole parse analysis */ + pstate->p_queryEnv = parentParseState->p_queryEnv; } return pstate; @@ -334,10 +337,9 @@ transformArraySubscripts(ParseState *pstate, */ foreach(idx, indirection) { - A_Indices *ai = (A_Indices *) lfirst(idx); + A_Indices *ai = lfirst_node(A_Indices, idx); Node *subexpr; - Assert(IsA(ai, A_Indices)); if (isSlice) { if (ai->lidx) diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c index e913d05a79..e40b10d4f6 100644 --- a/src/backend/parser/parse_oper.c +++ b/src/backend/parser/parse_oper.c @@ -3,7 +3,7 @@ * parse_oper.c * handle operator things for parser * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -132,32 +132,34 @@ LookupOperName(ParseState *pstate, List *opername, Oid oprleft, Oid oprright, } /* - * LookupOperNameTypeNames + * LookupOperWithArgs * Like LookupOperName, but the argument types are specified by - * TypeName nodes. - * - * Pass oprleft = NULL for a prefix op, oprright = NULL for a postfix op. + * a ObjectWithArg node. */ Oid -LookupOperNameTypeNames(ParseState *pstate, List *opername, - TypeName *oprleft, TypeName *oprright, - bool noError, int location) +LookupOperWithArgs(ObjectWithArgs *oper, bool noError) { + TypeName *oprleft, + *oprright; Oid leftoid, rightoid; + Assert(list_length(oper->objargs) == 2); + oprleft = linitial(oper->objargs); + oprright = lsecond(oper->objargs); + if (oprleft == NULL) leftoid = InvalidOid; else - leftoid = LookupTypeNameOid(pstate, oprleft, noError); + leftoid = LookupTypeNameOid(NULL, oprleft, noError); if (oprright == NULL) rightoid = InvalidOid; else - rightoid = LookupTypeNameOid(pstate, oprright, noError); + rightoid = LookupTypeNameOid(NULL, oprright, noError); - return LookupOperName(pstate, opername, leftoid, rightoid, - noError, location); + return LookupOperName(NULL, oper->objname, leftoid, rightoid, + noError, -1); } /* @@ -839,6 +841,10 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree, result->args = args; result->location = location; + /* if it returns a set, check that's OK */ + if (result->opretset) + check_srf_call_placement(pstate, location); + ReleaseSysCache(tup); return (Expr *) result; diff --git a/src/backend/parser/parse_param.c b/src/backend/parser/parse_param.c index b402843680..20fd83f095 100644 --- a/src/backend/parser/parse_param.c +++ b/src/backend/parser/parse_param.c @@ -12,7 +12,7 @@ * Note that other approaches to parameters are possible using the parser * hooks defined in ParseState. * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -210,7 +210,7 @@ variable_coerce_param_hook(ParseState *pstate, Param *param, } else { - /* Ooops */ + /* Oops */ ereport(ERROR, (errcode(ERRCODE_AMBIGUOUS_PARAMETER), errmsg("inconsistent types deduced for parameter $%d", diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index c10e272d72..8ae8b00236 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -4,7 +4,7 @@ * parser support routines dealing with relations * * Portions Copyright (c) 2012-2014, TransLattice, Inc. - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -26,6 +26,7 @@ #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "parser/parsetree.h" +#include "parser/parse_enr.h" #include "parser/parse_relation.h" #include "parser/parse_type.h" #include "utils/builtins.h" @@ -39,6 +40,7 @@ #include "pgxc/pgxc.h" #include "miscadmin.h" #endif +#include "utils/varlena.h" #define MAX_FUZZY_DISTANCE 3 @@ -291,6 +293,16 @@ isFutureCTE(ParseState *pstate, const char *refname) } /* + * Search the query's ephemeral named relation namespace for a relation + * matching the given unqualified refname. + */ +bool +scanNameSpaceForENR(ParseState *pstate, const char *refname) +{ + return name_matches_visible_ENR(pstate, refname); +} + +/* * searchRangeTableForRel * See if any RangeTblEntry could possibly match the RangeVar. * If so, return a pointer to the RangeTblEntry; else return NULL. @@ -311,6 +323,7 @@ searchRangeTableForRel(ParseState *pstate, RangeVar *relation) const char *refname = relation->relname; Oid relId = InvalidOid; CommonTableExpr *cte = NULL; + bool isenr = false; Index ctelevelsup = 0; Index levelsup; @@ -327,11 +340,16 @@ searchRangeTableForRel(ParseState *pstate, RangeVar *relation) * unlocked. */ if (!relation->schemaname) + { cte = scanNameSpaceForCTE(pstate, refname, &ctelevelsup); - if (!cte) + if (!cte) + isenr = scanNameSpaceForENR(pstate, refname); + } + + if (!cte && !isenr) relId = RangeVarGetRelid(relation, NoLock, true); - /* Now look for RTEs matching either the relation/CTE or the alias */ + /* Now look for RTEs matching either the relation/CTE/ENR or the alias */ for (levelsup = 0; pstate != NULL; pstate = pstate->parentParseState, levelsup++) @@ -351,6 +369,10 @@ searchRangeTableForRel(ParseState *pstate, RangeVar *relation) rte->ctelevelsup + levelsup == ctelevelsup && strcmp(rte->ctename, refname) == 0) return rte; + if (rte->rtekind == RTE_NAMEDTUPLESTORE && + isenr && + strcmp(rte->enrname, refname) == 0) + return rte; if (strcmp(rte->eref->aliasname, refname) == 0) return rte; } @@ -938,12 +960,11 @@ markRTEForSelectPriv(ParseState *pstate, RangeTblEntry *rte, JoinExpr *j; if (rtindex > 0 && rtindex <= list_length(pstate->p_joinexprs)) - j = (JoinExpr *) list_nth(pstate->p_joinexprs, rtindex - 1); + j = list_nth_node(JoinExpr, pstate->p_joinexprs, rtindex - 1); else j = NULL; if (j == NULL) elog(ERROR, "could not find JoinExpr for whole-row reference"); - Assert(IsA(j, JoinExpr)); /* Note: we can't see FromExpr here */ if (IsA(j->larg, RangeTblRef)) @@ -1168,12 +1189,18 @@ parserOpenTable(ParseState *pstate, const RangeVar *relation, int lockmode) else { /* + * An unqualified name might be a named ephemeral relation. + */ + if (get_visible_ENR_metadata(pstate->p_queryEnv, relation->relname)) + rel = NULL; + + /* * An unqualified name might have been meant as a reference to * some not-yet-in-scope CTE. The bare "does not exist" message * has proven remarkably unhelpful for figuring out such problems, * so we take pains to offer a specific hint. */ - if (isFutureCTE(pstate, relation->relname)) + else if (isFutureCTE(pstate, relation->relname)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_TABLE), errmsg("relation \"%s\" does not exist", @@ -1719,6 +1746,69 @@ addRangeTableEntryForFunction(ParseState *pstate, } /* + * Add an entry for a table function to the pstate's range table (p_rtable). + * + * This is much like addRangeTableEntry() except that it makes a tablefunc RTE. + */ +RangeTblEntry * +addRangeTableEntryForTableFunc(ParseState *pstate, + TableFunc *tf, + Alias *alias, + bool lateral, + bool inFromCl) +{ + RangeTblEntry *rte = makeNode(RangeTblEntry); + char *refname = alias ? alias->aliasname : pstrdup("xmltable"); + Alias *eref; + int numaliases; + + Assert(pstate != NULL); + + rte->rtekind = RTE_TABLEFUNC; + rte->relid = InvalidOid; + rte->subquery = NULL; + rte->tablefunc = tf; + rte->coltypes = tf->coltypes; + rte->coltypmods = tf->coltypmods; + rte->colcollations = tf->colcollations; + rte->alias = alias; + + eref = alias ? copyObject(alias) : makeAlias(refname, NIL); + numaliases = list_length(eref->colnames); + + /* fill in any unspecified alias columns */ + if (numaliases < list_length(tf->colnames)) + eref->colnames = list_concat(eref->colnames, + list_copy_tail(tf->colnames, numaliases)); + + rte->eref = eref; + + /* + * Set flags and access permissions. + * + * Tablefuncs are never checked for access rights (at least, not by the + * RTE permissions mechanism). + */ + rte->lateral = lateral; + rte->inh = false; /* never true for tablefunc RTEs */ + rte->inFromCl = inFromCl; + + rte->requiredPerms = 0; + rte->checkAsUser = InvalidOid; + rte->selectedCols = NULL; + rte->insertedCols = NULL; + rte->updatedCols = NULL; + + /* + * Add completed RTE to pstate's range table list, but not to join list + * nor namespace --- caller must do that if appropriate. + */ + pstate->p_rtable = lappend(pstate->p_rtable, rte); + + return rte; +} + +/* * Add an entry for a VALUES list to the pstate's range table (p_rtable). * * This is much like addRangeTableEntry() except that it makes a values RTE. @@ -1726,7 +1816,9 @@ addRangeTableEntryForFunction(ParseState *pstate, RangeTblEntry * addRangeTableEntryForValues(ParseState *pstate, List *exprs, - List *collations, + List *coltypes, + List *coltypmods, + List *colcollations, Alias *alias, bool lateral, bool inFromCl) @@ -1743,7 +1835,9 @@ addRangeTableEntryForValues(ParseState *pstate, rte->relid = InvalidOid; rte->subquery = NULL; rte->values_lists = exprs; - rte->values_collations = collations; + rte->coltypes = coltypes; + rte->coltypmods = coltypmods; + rte->colcollations = colcollations; rte->alias = alias; eref = alias ? copyObject(alias) : makeAlias(refname, NIL); @@ -1828,7 +1922,7 @@ addRangeTableEntryForJoin(ParseState *pstate, rte->joinaliasvars = aliasvars; rte->alias = alias; - eref = alias ? (Alias *) copyObject(alias) : makeAlias("unnamed_join", NIL); + eref = alias ? copyObject(alias) : makeAlias("unnamed_join", NIL); numaliases = list_length(eref->colnames); /* fill in any unspecified alias columns */ @@ -1913,9 +2007,9 @@ addRangeTableEntryForCTE(ParseState *pstate, parser_errposition(pstate, rv->location))); } - rte->ctecoltypes = cte->ctecoltypes; - rte->ctecoltypmods = cte->ctecoltypmods; - rte->ctecolcollations = cte->ctecolcollations; + rte->coltypes = cte->ctecoltypes; + rte->coltypmods = cte->ctecoltypmods; + rte->colcollations = cte->ctecolcollations; rte->alias = alias; if (alias) @@ -1964,6 +2058,102 @@ addRangeTableEntryForCTE(ParseState *pstate, return rte; } +/* + * Add an entry for an ephemeral named relation reference to the pstate's + * range table (p_rtable). + * + * It is expected that the RangeVar, which up until now is only known to be an + * ephemeral named relation, will (in conjunction with the QueryEnvironment in + * the ParseState), create a RangeTblEntry for a specific *kind* of ephemeral + * named relation, based on enrtype. + * + * This is much like addRangeTableEntry() except that it makes an RTE for an + * ephemeral named relation. + */ +RangeTblEntry * +addRangeTableEntryForENR(ParseState *pstate, + RangeVar *rv, + bool inFromCl) +{ + RangeTblEntry *rte = makeNode(RangeTblEntry); + Alias *alias = rv->alias; + char *refname = alias ? alias->aliasname : rv->relname; + EphemeralNamedRelationMetadata enrmd; + TupleDesc tupdesc; + int attno; + + Assert(pstate != NULL); + enrmd = get_visible_ENR(pstate, rv->relname); + Assert(enrmd != NULL); + + switch (enrmd->enrtype) + { + case ENR_NAMED_TUPLESTORE: + rte->rtekind = RTE_NAMEDTUPLESTORE; + break; + + default: + elog(ERROR, "unexpected enrtype: %d", enrmd->enrtype); + return NULL; /* for fussy compilers */ + } + + /* + * Record dependency on a relation. This allows plans to be invalidated + * if they access transition tables linked to a table that is altered. + */ + rte->relid = enrmd->reliddesc; + + /* + * Build the list of effective column names using user-supplied aliases + * and/or actual column names. Also build the cannibalized fields. + */ + tupdesc = ENRMetadataGetTupDesc(enrmd); + rte->eref = makeAlias(refname, NIL); + buildRelationAliases(tupdesc, alias, rte->eref); + rte->enrname = enrmd->name; + rte->enrtuples = enrmd->enrtuples; + rte->coltypes = NIL; + rte->coltypmods = NIL; + rte->colcollations = NIL; + for (attno = 1; attno <= tupdesc->natts; ++attno) + { + if (tupdesc->attrs[attno - 1]->atttypid == InvalidOid && + !(tupdesc->attrs[attno - 1]->attisdropped)) + elog(ERROR, "atttypid was invalid for column which has not been dropped from \"%s\"", + rv->relname); + rte->coltypes = + lappend_oid(rte->coltypes, + tupdesc->attrs[attno - 1]->atttypid); + rte->coltypmods = + lappend_int(rte->coltypmods, + tupdesc->attrs[attno - 1]->atttypmod); + rte->colcollations = + lappend_oid(rte->colcollations, + tupdesc->attrs[attno - 1]->attcollation); + } + + /* + * Set flags and access permissions. + * + * ENRs are never checked for access rights. + */ + rte->lateral = false; + rte->inh = false; /* never true for ENRs */ + rte->inFromCl = inFromCl; + + rte->requiredPerms = 0; + rte->checkAsUser = InvalidOid; + rte->selectedCols = NULL; + + /* + * Add completed RTE to pstate's range table list, but not to join list + * nor namespace --- caller must do that if appropriate. + */ + pstate->p_rtable = lappend(pstate->p_rtable, rte); + + return rte; +} + /* * Has the specified refname been selected FOR UPDATE/FOR SHARE? @@ -2244,46 +2434,6 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, } } break; - case RTE_VALUES: - { - /* Values RTE */ - ListCell *aliasp_item = list_head(rte->eref->colnames); - ListCell *lcv; - ListCell *lcc; - - varattno = 0; - forboth(lcv, (List *) linitial(rte->values_lists), - lcc, rte->values_collations) - { - Node *col = (Node *) lfirst(lcv); - Oid colcollation = lfirst_oid(lcc); - - varattno++; - if (colnames) - { - /* Assume there is one alias per column */ - char *label = strVal(lfirst(aliasp_item)); - - *colnames = lappend(*colnames, - makeString(pstrdup(label))); - aliasp_item = lnext(aliasp_item); - } - - if (colvars) - { - Var *varnode; - - varnode = makeVar(rtindex, varattno, - exprType(col), - exprTypmod(col), - colcollation, - sublevels_up); - varnode->location = location; - *colvars = lappend(*colvars, varnode); - } - } - } - break; case RTE_JOIN: { /* Join RTE */ @@ -2353,17 +2503,21 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, } } break; + case RTE_TABLEFUNC: + case RTE_VALUES: case RTE_CTE: + case RTE_NAMEDTUPLESTORE: { + /* Tablefunc, Values or CTE RTE */ ListCell *aliasp_item = list_head(rte->eref->colnames); ListCell *lct; ListCell *lcm; ListCell *lcc; varattno = 0; - forthree(lct, rte->ctecoltypes, - lcm, rte->ctecoltypmods, - lcc, rte->ctecolcollations) + forthree(lct, rte->coltypes, + lcm, rte->coltypmods, + lcc, rte->colcollations) { Oid coltype = lfirst_oid(lct); int32 coltypmod = lfirst_int(lcm); @@ -2376,7 +2530,8 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, /* Assume there is one alias per output column */ char *label = strVal(lfirst(aliasp_item)); - *colnames = lappend(*colnames, makeString(pstrdup(label))); + *colnames = lappend(*colnames, + makeString(pstrdup(label))); aliasp_item = lnext(aliasp_item); } @@ -2387,6 +2542,8 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, varnode = makeVar(rtindex, varattno, coltype, coltypmod, colcoll, sublevels_up); + varnode->location = location; + *colvars = lappend(*colvars, varnode); } } @@ -2745,22 +2902,6 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, rte->eref->aliasname))); } break; - case RTE_VALUES: - { - /* Values RTE --- get type info from first sublist */ - /* collation is stored separately, though */ - List *collist = (List *) linitial(rte->values_lists); - Node *col; - - if (attnum < 1 || attnum > list_length(collist)) - elog(ERROR, "values list %s does not have attribute %d", - rte->eref->aliasname, attnum); - col = (Node *) list_nth(collist, attnum - 1); - *vartype = exprType(col); - *vartypmod = exprTypmod(col); - *varcollid = list_nth_oid(rte->values_collations, attnum - 1); - } - break; case RTE_JOIN: { /* @@ -2776,13 +2917,19 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, *varcollid = exprCollation(aliasvar); } break; + case RTE_TABLEFUNC: + case RTE_VALUES: case RTE_CTE: + case RTE_NAMEDTUPLESTORE: { - /* CTE RTE --- get type info from lists in the RTE */ - Assert(attnum > 0 && attnum <= list_length(rte->ctecoltypes)); - *vartype = list_nth_oid(rte->ctecoltypes, attnum - 1); - *vartypmod = list_nth_int(rte->ctecoltypmods, attnum - 1); - *varcollid = list_nth_oid(rte->ctecolcollations, attnum - 1); + /* + * tablefunc, VALUES or CTE RTE --- get type info from lists + * in the RTE + */ + Assert(attnum > 0 && attnum <= list_length(rte->coltypes)); + *vartype = list_nth_oid(rte->coltypes, attnum - 1); + *vartypmod = list_nth_int(rte->coltypmods, attnum - 1); + *varcollid = list_nth_oid(rte->colcollations, attnum - 1); } break; default: @@ -2821,11 +2968,29 @@ get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum) } break; case RTE_SUBQUERY: + case RTE_TABLEFUNC: case RTE_VALUES: case RTE_CTE: - /* Subselect, Values, CTE RTEs never have dropped columns */ + + /* + * Subselect, Table Functions, Values, CTE RTEs never have dropped + * columns + */ result = false; break; + case RTE_NAMEDTUPLESTORE: + { + Assert(rte->enrname); + + /* + * We checked when we loaded coltypes for the tuplestore that + * InvalidOid was only used for dropped columns, so it is safe + * to count on that here. + */ + result = + ((list_nth_oid(rte->coltypes, attnum - 1) == InvalidOid)); + } + break; case RTE_JOIN: { /* diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index bb78974532..f2ca840ef2 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -3,7 +3,7 @@ * parse_target.c * handle target lists * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -91,7 +91,17 @@ transformTargetEntry(ParseState *pstate, { /* Transform the node if caller didn't do it already */ if (expr == NULL) - expr = transformExpr(pstate, node, exprKind); + { + /* + * If it's a SetToDefault node and we should allow that, pass it + * through unmodified. (transformExpr will throw the appropriate + * error if we're disallowing it.) + */ + if (exprKind == EXPR_KIND_UPDATE_SOURCE && IsA(node, SetToDefault)) + expr = node; + else + expr = transformExpr(pstate, node, exprKind); + } if (colname == NULL && !resjunk) { @@ -122,11 +132,15 @@ transformTargetList(ParseState *pstate, List *targetlist, ParseExprKind exprKind) { List *p_target = NIL; + bool expand_star; ListCell *o_target; /* Shouldn't have any leftover multiassign items at start */ Assert(pstate->p_multiassign_exprs == NIL); + /* Expand "something.*" in SELECT and RETURNING, but not UPDATE */ + expand_star = (exprKind != EXPR_KIND_UPDATE_SOURCE); + foreach(o_target, targetlist) { ResTarget *res = (ResTarget *) lfirst(o_target); @@ -136,35 +150,42 @@ transformTargetList(ParseState *pstate, List *targetlist, * "something", the star could appear as the last field in ColumnRef, * or as the last indirection item in A_Indirection. */ - if (IsA(res->val, ColumnRef)) + if (expand_star) { - ColumnRef *cref = (ColumnRef *) res->val; - - if (IsA(llast(cref->fields), A_Star)) + if (IsA(res->val, ColumnRef)) { - /* It is something.*, expand into multiple items */ - p_target = list_concat(p_target, - ExpandColumnRefStar(pstate, cref, - true)); - continue; - } - } - else if (IsA(res->val, A_Indirection)) - { - A_Indirection *ind = (A_Indirection *) res->val; + ColumnRef *cref = (ColumnRef *) res->val; - if (IsA(llast(ind->indirection), A_Star)) + if (IsA(llast(cref->fields), A_Star)) + { + /* It is something.*, expand into multiple items */ + p_target = list_concat(p_target, + ExpandColumnRefStar(pstate, + cref, + true)); + continue; + } + } + else if (IsA(res->val, A_Indirection)) { - /* It is something.*, expand into multiple items */ - p_target = list_concat(p_target, - ExpandIndirectionStar(pstate, ind, - true, exprKind)); - continue; + A_Indirection *ind = (A_Indirection *) res->val; + + if (IsA(llast(ind->indirection), A_Star)) + { + /* It is something.*, expand into multiple items */ + p_target = list_concat(p_target, + ExpandIndirectionStar(pstate, + ind, + true, + exprKind)); + continue; + } } } /* - * Not "something.*", so transform as a single expression + * Not "something.*", or we want to treat that as a plain whole-row + * variable, so transform as a single expression */ p_target = lappend(p_target, transformTargetEntry(pstate, @@ -199,10 +220,13 @@ transformTargetList(ParseState *pstate, List *targetlist, * the input list elements are bare expressions without ResTarget decoration, * and the output elements are likewise just expressions without TargetEntry * decoration. We use this for ROW() and VALUES() constructs. + * + * exprKind is not enough to tell us whether to allow SetToDefault, so + * an additional flag is needed for that. */ List * transformExpressionList(ParseState *pstate, List *exprlist, - ParseExprKind exprKind) + ParseExprKind exprKind, bool allowDefault) { List *result = NIL; ListCell *lc; @@ -244,10 +268,17 @@ transformExpressionList(ParseState *pstate, List *exprlist, } /* - * Not "something.*", so transform as a single expression + * Not "something.*", so transform as a single expression. If it's a + * SetToDefault node and we should allow that, pass it through + * unmodified. (transformExpr will throw the appropriate error if + * we're disallowing it.) */ - result = lappend(result, - transformExpr(pstate, e, exprKind)); + if (allowDefault && IsA(e, SetToDefault)) + /* do nothing */ ; + else + e = transformExpr(pstate, e, exprKind); + + result = lappend(result, e); } /* Shouldn't have any multiassign items here */ @@ -258,12 +289,41 @@ transformExpressionList(ParseState *pstate, List *exprlist, /* + * resolveTargetListUnknowns() + * Convert any unknown-type targetlist entries to type TEXT. + * + * We do this after we've exhausted all other ways of identifying the output + * column types of a query. + */ +void +resolveTargetListUnknowns(ParseState *pstate, List *targetlist) +{ + ListCell *l; + + foreach(l, targetlist) + { + TargetEntry *tle = (TargetEntry *) lfirst(l); + Oid restype = exprType((Node *) tle->expr); + + if (restype == UNKNOWNOID) + { + tle->expr = (Expr *) coerce_type(pstate, (Node *) tle->expr, + restype, TEXTOID, -1, + COERCION_IMPLICIT, + COERCE_IMPLICIT_CAST, + -1); + } + } +} + + +/* * markTargetListOrigins() * Mark targetlist columns that are simple Vars with the source * table's OID and column number. * - * Currently, this is done only for SELECT targetlists, since we only - * need the info if we are going to send it to the frontend. + * Currently, this is done only for SELECT targetlists and RETURNING lists, + * since we only need the info if we are going to send it to the frontend. */ void markTargetListOrigins(ParseState *pstate, List *targetlist) @@ -336,6 +396,8 @@ markTargetListOrigin(ParseState *pstate, TargetEntry *tle, break; case RTE_FUNCTION: case RTE_VALUES: + case RTE_TABLEFUNC: + case RTE_NAMEDTUPLESTORE: /* not a simple relation, leave it unmarked */ break; case RTE_CTE: @@ -1449,6 +1511,7 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup) { case RTE_RELATION: case RTE_VALUES: + case RTE_NAMEDTUPLESTORE: /* * This case should not occur: a column of a table or values list @@ -1502,6 +1565,12 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup) * its result columns as RECORD, which is not allowed. */ break; + case RTE_TABLEFUNC: + + /* + * Table function cannot have columns with RECORD type. + */ + break; case RTE_CTE: /* CTE reference: examine subquery's output expr */ if (!rte->self_reference) @@ -1771,6 +1840,49 @@ FigureColnameInternal(Node *node, char **name) return 2; } break; + case T_SQLValueFunction: + /* make these act like a function or variable */ + switch (((SQLValueFunction *) node)->op) + { + case SVFOP_CURRENT_DATE: + *name = "current_date"; + return 2; + case SVFOP_CURRENT_TIME: + case SVFOP_CURRENT_TIME_N: + *name = "current_time"; + return 2; + case SVFOP_CURRENT_TIMESTAMP: + case SVFOP_CURRENT_TIMESTAMP_N: + *name = "current_timestamp"; + return 2; + case SVFOP_LOCALTIME: + case SVFOP_LOCALTIME_N: + *name = "localtime"; + return 2; + case SVFOP_LOCALTIMESTAMP: + case SVFOP_LOCALTIMESTAMP_N: + *name = "localtimestamp"; + return 2; + case SVFOP_CURRENT_ROLE: + *name = "current_role"; + return 2; + case SVFOP_CURRENT_USER: + *name = "current_user"; + return 2; + case SVFOP_USER: + *name = "user"; + return 2; + case SVFOP_SESSION_USER: + *name = "session_user"; + return 2; + case SVFOP_CURRENT_CATALOG: + *name = "current_catalog"; + return 2; + case SVFOP_CURRENT_SCHEMA: + *name = "current_schema"; + return 2; + } + break; case T_XmlExpr: /* make SQL/XML functions act like a regular function */ switch (((XmlExpr *) node)->op) diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c index d39f06e831..a723ea15ad 100644 --- a/src/backend/parser/parse_type.c +++ b/src/backend/parser/parse_type.c @@ -3,7 +3,7 @@ * parse_type.c * handle type operations for parser * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -478,9 +478,8 @@ TypeNameListToString(List *typenames) initStringInfo(&string); foreach(l, typenames) { - TypeName *typeName = (TypeName *) lfirst(l); + TypeName *typeName = lfirst_node(TypeName, l); - Assert(IsA(typeName, TypeName)); if (l != list_head(typenames)) appendStringInfoChar(&string, ','); appendTypeNameToBuffer(typeName, &string); @@ -720,7 +719,7 @@ typeStringToTypeName(const char *str) */ if (list_length(raw_parsetree_list) != 1) goto fail; - stmt = (SelectStmt *) linitial(raw_parsetree_list); + stmt = (SelectStmt *) linitial_node(RawStmt, raw_parsetree_list)->stmt; if (stmt == NULL || !IsA(stmt, SelectStmt) || stmt->distinctClause != NIL || diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index bd0a620285..c04e77775e 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -17,7 +17,7 @@ * * * Portions Copyright (c) 2012-2014, TransLattice, Inc. - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 2010-2012 Postgres-XC Development Group * @@ -47,13 +47,16 @@ #endif #include "commands/comment.h" #include "commands/defrem.h" +#include "commands/sequence.h" #include "commands/tablecmds.h" #include "commands/tablespace.h" #include "miscadmin.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" +#include "optimizer/planner.h" #include "parser/analyze.h" #include "parser/parse_clause.h" +#include "parser/parse_coerce.h" #include "parser/parse_collate.h" #include "parser/parse_expr.h" #include "parser/parse_relation.h" @@ -70,9 +73,9 @@ #include "rewrite/rewriteManip.h" #include "utils/acl.h" #include "utils/builtins.h" -#include "utils/guc.h" #include "utils/lsyscache.h" #include "utils/rel.h" +#include "utils/ruleutils.h" #include "utils/syscache.h" #include "utils/typcache.h" @@ -117,6 +120,8 @@ typedef struct DistributeBy *distributeby; /* original distribute by column of CREATE TABLE */ PGXCSubCluster *subcluster; /* original subcluster option of CREATE TABLE */ #endif + bool ispartitioned; /* true if table is partitioned */ + PartitionBoundSpec *partbound; /* transformed FOR VALUES */ } CreateStmtContext; /* State shared by transformCreateSchemaStmt and its subroutines */ @@ -169,6 +174,10 @@ static void checkLocalFKConstraints(CreateStmtContext *cxt); static List *transformSubclusterNodes(PGXCSubCluster *subcluster); static PGXCSubCluster *makeSubCluster(List *nodelist); #endif +static void transformPartitionCmd(CreateStmtContext *cxt, PartitionCmd *cmd); +static Const *transformPartitionBoundValue(ParseState *pstate, A_Const *con, + const char *colName, Oid colType, int32 colTypmod); + /* * transformCreateStmt - @@ -207,7 +216,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString) * We must not scribble on the passed-in CreateStmt, so copy it. (This is * overkill, but easy.) */ - stmt = (CreateStmt *) copyObject(stmt); + stmt = copyObject(stmt); /* Set up pstate */ pstate = make_parsestate(NULL); @@ -280,6 +289,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString) cxt.distributeby = stmt->distributeby; cxt.subcluster = stmt->subcluster; #endif + cxt.ispartitioned = stmt->partspec != NULL; /* * Notice that we allow OIDs here only for plain tables, even though @@ -298,12 +308,17 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString) if (stmt->ofTypename) transformOfType(&cxt, stmt->ofTypename); + if (stmt->partspec) + { + if (stmt->inhRelations && !stmt->partbound) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("cannot create partitioned table as inheritance child"))); + } + /* * Run through each primary element in the table creation clause. Separate - * column defs from constraints, and do preliminary analysis. We have to - * process column-defining clauses first because it can control the - * presence of columns which are referenced by columns referenced by - * constraints. + * column defs from constraints, and do preliminary analysis. */ foreach(elements, stmt->tableElts) { @@ -315,17 +330,13 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString) transformColumnDefinition(&cxt, (ColumnDef *) element); break; - case T_TableLikeClause: - if (!like_found) - { - cxt.hasoids = false; - like_found = true; - } - transformTableLikeClause(&cxt, (TableLikeClause *) element); + case T_Constraint: + transformTableConstraint(&cxt, (Constraint *) element); break; - case T_Constraint: - /* process later */ + case T_TableLikeClause: + like_found = true; + transformTableLikeClause(&cxt, (TableLikeClause *) element); break; default: @@ -335,26 +346,19 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString) } } - if (like_found) - { - /* - * To match INHERITS, the existence of any LIKE table with OIDs causes - * the new table to have oids. For the same reason, WITH/WITHOUT OIDs - * is also ignored with LIKE. We prepend because the first oid option - * list entry is honored. Our prepended WITHOUT OIDS clause will be - * overridden if an inherited table has oids. - */ + /* + * If we had any LIKE tables, they may require creation of an OID column + * even though the command's own WITH clause didn't ask for one (or, + * perhaps, even specifically rejected having one). Insert a WITH option + * to ensure that happens. We prepend to the list because the first oid + * option will be honored, and we want to override anything already there. + * (But note that DefineRelation will override this again to add an OID + * column if one appears in an inheritance parent table.) + */ + if (like_found && cxt.hasoids) stmt->options = lcons(makeDefElem("oids", - (Node *) makeInteger(cxt.hasoids)), stmt->options); - } - - foreach(elements, stmt->tableElts) - { - Node *element = lfirst(elements); - - if (nodeTag(element) == T_Constraint) - transformTableConstraint(&cxt, (Constraint *) element); - } + (Node *) makeInteger(true), -1), + stmt->options); /* * transformIndexConstraints wants cxt.alist to contain only index @@ -482,6 +486,133 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString) return result; } +static void +generateSerialExtraStmts(CreateStmtContext *cxt, ColumnDef *column, + Oid seqtypid, List *seqoptions, bool for_identity, + char **snamespace_p, char **sname_p) +{ + ListCell *option; + DefElem *nameEl = NULL; + Oid snamespaceid; + char *snamespace; + char *sname; + CreateSeqStmt *seqstmt; + AlterSeqStmt *altseqstmt; + List *attnamelist; + + /* + * Determine namespace and name to use for the sequence. + * + * First, check if a sequence name was passed in as an option. This is + * used by pg_dump. Else, generate a name. + * + * Although we use ChooseRelationName, it's not guaranteed that the + * selected sequence name won't conflict; given sufficiently long field + * names, two different serial columns in the same table could be assigned + * the same sequence name, and we'd not notice since we aren't creating + * the sequence quite yet. In practice this seems quite unlikely to be a + * problem, especially since few people would need two serial columns in + * one table. + */ + + foreach(option, seqoptions) + { + DefElem *defel = lfirst_node(DefElem, option); + + if (strcmp(defel->defname, "sequence_name") == 0) + { + if (nameEl) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + nameEl = defel; + } + } + + if (nameEl) + { + RangeVar *rv = makeRangeVarFromNameList(castNode(List, nameEl->arg)); + + snamespace = rv->schemaname; + sname = rv->relname; + seqoptions = list_delete_ptr(seqoptions, nameEl); + } + else + { + if (cxt->rel) + snamespaceid = RelationGetNamespace(cxt->rel); + else + { + snamespaceid = RangeVarGetCreationNamespace(cxt->relation); + RangeVarAdjustRelationPersistence(cxt->relation, snamespaceid); + } + snamespace = get_namespace_name(snamespaceid); + sname = ChooseRelationName(cxt->relation->relname, + column->colname, + "seq", + snamespaceid); + } + + ereport(DEBUG1, + (errmsg("%s will create implicit sequence \"%s\" for serial column \"%s.%s\"", + cxt->stmtType, sname, + cxt->relation->relname, column->colname))); + + /* + * Build a CREATE SEQUENCE command to create the sequence object, and add + * it to the list of things to be done before this CREATE/ALTER TABLE. + */ + seqstmt = makeNode(CreateSeqStmt); + seqstmt->for_identity = for_identity; + seqstmt->sequence = makeRangeVar(snamespace, sname, -1); + seqstmt->options = seqoptions; + + /* + * If a sequence data type was specified, add it to the options. Prepend + * to the list rather than append; in case a user supplied their own AS + * clause, the "redundant options" error will point to their occurrence, + * not our synthetic one. + */ + if (seqtypid) + seqstmt->options = lcons(makeDefElem("as", (Node *) makeTypeNameFromOid(seqtypid, -1), -1), + seqstmt->options); + + /* + * If this is ALTER ADD COLUMN, make sure the sequence will be owned by + * the table's owner. The current user might be someone else (perhaps a + * superuser, or someone who's only a member of the owning role), but the + * SEQUENCE OWNED BY mechanisms will bleat unless table and sequence have + * exactly the same owning role. + */ + if (cxt->rel) + seqstmt->ownerId = cxt->rel->rd_rel->relowner; + else + seqstmt->ownerId = InvalidOid; + + cxt->blist = lappend(cxt->blist, seqstmt); + + /* + * Build an ALTER SEQUENCE ... OWNED BY command to mark the sequence as + * owned by this column, and add it to the list of things to be done after + * this CREATE/ALTER TABLE. + */ + altseqstmt = makeNode(AlterSeqStmt); + altseqstmt->sequence = makeRangeVar(snamespace, sname, -1); + attnamelist = list_make3(makeString(snamespace), + makeString(cxt->relation->relname), + makeString(column->colname)); + altseqstmt->options = list_make1(makeDefElem("owned_by", + (Node *) attnamelist, -1)); + altseqstmt->for_identity = for_identity; + + cxt->alist = lappend(cxt->alist, altseqstmt); + + if (snamespace_p) + *snamespace_p = snamespace; + if (sname_p) + *sname_p = sname; +} + /* * transformColumnDefinition - * transform a single ColumnDef within CREATE TABLE @@ -493,7 +624,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) bool is_serial; bool saw_nullable; bool saw_default; - Constraint *constraint; + bool saw_identity; ListCell *clist; cxt->columns = lappend(cxt->columns, column); @@ -548,89 +679,18 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) /* Special actions for SERIAL pseudo-types */ if (is_serial) { - Oid snamespaceid; char *snamespace; char *sname; char *qstring; A_Const *snamenode; TypeCast *castnode; FuncCall *funccallnode; - CreateSeqStmt *seqstmt; - AlterSeqStmt *altseqstmt; - List *attnamelist; - - /* - * Determine namespace and name to use for the sequence. - * - * Although we use ChooseRelationName, it's not guaranteed that the - * selected sequence name won't conflict; given sufficiently long - * field names, two different serial columns in the same table could - * be assigned the same sequence name, and we'd not notice since we - * aren't creating the sequence quite yet. In practice this seems - * quite unlikely to be a problem, especially since few people would - * need two serial columns in one table. - */ - if (cxt->rel) - snamespaceid = RelationGetNamespace(cxt->rel); - else - { - snamespaceid = RangeVarGetCreationNamespace(cxt->relation); - RangeVarAdjustRelationPersistence(cxt->relation, snamespaceid); - } - snamespace = get_namespace_name(snamespaceid); - sname = ChooseRelationName(cxt->relation->relname, - column->colname, - "seq", - snamespaceid); - - ereport(DEBUG1, - (errmsg("%s will create implicit sequence \"%s\" for serial column \"%s.%s\"", - cxt->stmtType, sname, - cxt->relation->relname, column->colname))); - - /* - * Build a CREATE SEQUENCE command to create the sequence object, and - * add it to the list of things to be done before this CREATE/ALTER - * TABLE. - */ - seqstmt = makeNode(CreateSeqStmt); - seqstmt->sequence = makeRangeVar(snamespace, sname, -1); - seqstmt->options = NIL; -#ifdef PGXC - seqstmt->is_serial = true; -#endif - - /* - * If this is ALTER ADD COLUMN, make sure the sequence will be owned - * by the table's owner. The current user might be someone else - * (perhaps a superuser, or someone who's only a member of the owning - * role), but the SEQUENCE OWNED BY mechanisms will bleat unless table - * and sequence have exactly the same owning role. - */ - if (cxt->rel) - seqstmt->ownerId = cxt->rel->rd_rel->relowner; - else - seqstmt->ownerId = InvalidOid; - - cxt->blist = lappend(cxt->blist, seqstmt); - - /* - * Build an ALTER SEQUENCE ... OWNED BY command to mark the sequence - * as owned by this column, and add it to the list of things to be - * done after this CREATE/ALTER TABLE. - */ - altseqstmt = makeNode(AlterSeqStmt); - altseqstmt->sequence = makeRangeVar(snamespace, sname, -1); -#ifdef PGXC - altseqstmt->is_serial = true; -#endif - attnamelist = list_make3(makeString(snamespace), - makeString(cxt->relation->relname), - makeString(column->colname)); - altseqstmt->options = list_make1(makeDefElem("owned_by", - (Node *) attnamelist)); + Constraint *constraint; - cxt->alist = lappend(cxt->alist, altseqstmt); + /* XXX XL 9.6 was setting stmt->is_serial. CHECK */ + generateSerialExtraStmts(cxt, column, + column->typeName->typeOid, NIL, false, + &snamespace, &sname); /* * Create appropriate constraints for SERIAL. We do this in full, @@ -672,11 +732,11 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) saw_nullable = false; saw_default = false; + saw_identity = false; foreach(clist, column->constraints) { - constraint = lfirst(clist); - Assert(IsA(constraint, Constraint)); + Constraint *constraint = lfirst_node(Constraint, clist); switch (constraint->contype) { @@ -717,6 +777,33 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) saw_default = true; break; + case CONSTR_IDENTITY: + { + Type ctype; + Oid typeOid; + + ctype = typenameType(cxt->pstate, column->typeName, NULL); + typeOid = HeapTupleGetOid(ctype); + ReleaseSysCache(ctype); + + if (saw_identity) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("multiple identity specifications for column \"%s\" of table \"%s\"", + column->colname, cxt->relation->relname), + parser_errposition(cxt->pstate, + constraint->location))); + + generateSerialExtraStmts(cxt, column, + typeOid, constraint->options, true, + NULL, NULL); + + column->identity = constraint->generated_when; + saw_identity = true; + column->is_not_null = TRUE; + break; + } + case CONSTR_CHECK: cxt->ckconstraints = lappend(cxt->ckconstraints, constraint); break; @@ -728,6 +815,12 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) errmsg("primary key constraints are not supported on foreign tables"), parser_errposition(cxt->pstate, constraint->location))); + if (cxt->ispartitioned) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("primary key constraints are not supported on partitioned tables"), + parser_errposition(cxt->pstate, + constraint->location))); /* FALL THRU */ case CONSTR_UNIQUE: @@ -737,6 +830,12 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) errmsg("unique constraints are not supported on foreign tables"), parser_errposition(cxt->pstate, constraint->location))); + if (cxt->ispartitioned) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("unique constraints are not supported on partitioned tables"), + parser_errposition(cxt->pstate, + constraint->location))); if (constraint->keys == NIL) constraint->keys = list_make1(makeString(column->colname)); cxt->ixconstraints = lappend(cxt->ixconstraints, constraint); @@ -754,6 +853,12 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) errmsg("foreign key constraints are not supported on foreign tables"), parser_errposition(cxt->pstate, constraint->location))); + if (cxt->ispartitioned) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("foreign key constraints are not supported on partitioned tables"), + parser_errposition(cxt->pstate, + constraint->location))); /* * Fill in the current attribute's name and throw it into the @@ -775,6 +880,14 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) constraint->contype); break; } + + if (saw_default && saw_identity) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("both default and identity specified for column \"%s\" of table \"%s\"", + column->colname, cxt->relation->relname), + parser_errposition(cxt->pstate, + constraint->location))); } /* @@ -819,6 +932,12 @@ transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint) errmsg("primary key constraints are not supported on foreign tables"), parser_errposition(cxt->pstate, constraint->location))); + if (cxt->ispartitioned) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("primary key constraints are not supported on partitioned tables"), + parser_errposition(cxt->pstate, + constraint->location))); cxt->ixconstraints = lappend(cxt->ixconstraints, constraint); break; @@ -829,6 +948,12 @@ transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint) errmsg("unique constraints are not supported on foreign tables"), parser_errposition(cxt->pstate, constraint->location))); + if (cxt->ispartitioned) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("unique constraints are not supported on partitioned tables"), + parser_errposition(cxt->pstate, + constraint->location))); cxt->ixconstraints = lappend(cxt->ixconstraints, constraint); break; @@ -839,6 +964,12 @@ transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint) errmsg("exclusion constraints are not supported on foreign tables"), parser_errposition(cxt->pstate, constraint->location))); + if (cxt->ispartitioned) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("exclusion constraints are not supported on partitioned tables"), + parser_errposition(cxt->pstate, + constraint->location))); cxt->ixconstraints = lappend(cxt->ixconstraints, constraint); break; @@ -853,6 +984,12 @@ transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint) errmsg("foreign key constraints are not supported on foreign tables"), parser_errposition(cxt->pstate, constraint->location))); + if (cxt->ispartitioned) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("foreign key constraints are not supported on partitioned tables"), + parser_errposition(cxt->pstate, + constraint->location))); cxt->fkconstraints = lappend(cxt->fkconstraints, constraint); break; @@ -908,7 +1045,8 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla relation->rd_rel->relkind != RELKIND_VIEW && relation->rd_rel->relkind != RELKIND_MATVIEW && relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE && - relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE) + relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE && + relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a table, view, materialized view, composite type, or foreign table", @@ -994,6 +1132,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla def->is_local = true; def->is_not_null = attribute->attnotnull; def->is_from_type = false; + def->is_from_parent = false; def->storage = 0; def->raw_default = NULL; def->cooked_default = NULL; @@ -1054,6 +1193,27 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla def->cooked_default = this_default; } + /* + * Copy identity if requested + */ + if (attribute->attidentity && + (table_like_clause->options & CREATE_TABLE_LIKE_IDENTITY)) + { + Oid seq_relid; + List *seq_options; + + /* + * find sequence owned by old column; extract sequence parameters; + * build new create sequence command + */ + seq_relid = getOwnedSequence(RelationGetRelid(relation), attribute->attnum); + seq_options = sequence_options(seq_relid); + generateSerialExtraStmts(cxt, def, + InvalidOid, seq_options, true, + NULL, NULL); + def->identity = attribute->attidentity; + } + /* Likewise, copy storage if requested */ if (table_like_clause->options & CREATE_TABLE_LIKE_STORAGE) def->storage = attribute->attstorage; @@ -1069,10 +1229,9 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla CommentStmt *stmt = makeNode(CommentStmt); stmt->objtype = OBJECT_COLUMN; - stmt->objname = list_make3(makeString(cxt->relation->schemaname), - makeString(cxt->relation->relname), - makeString(def->colname)); - stmt->objargs = NIL; + stmt->object = (Node *) list_make3(makeString(cxt->relation->schemaname), + makeString(cxt->relation->relname), + makeString(def->colname)); stmt->comment = comment; cxt->alist = lappend(cxt->alist, stmt); @@ -1080,7 +1239,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla } /* We use oids if at least one LIKE'ed table has oids. */ - cxt->hasoids = cxt->hasoids || relation->rd_rel->relhasoids; + cxt->hasoids |= relation->rd_rel->relhasoids; /* * Copy CHECK constraints if requested, being careful to adjust attribute @@ -1135,10 +1294,9 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla CommentStmt *stmt = makeNode(CommentStmt); stmt->objtype = OBJECT_TABCONSTRAINT; - stmt->objname = list_make3(makeString(cxt->relation->schemaname), - makeString(cxt->relation->relname), - makeString(n->conname)); - stmt->objargs = NIL; + stmt->object = (Node *) list_make3(makeString(cxt->relation->schemaname), + makeString(cxt->relation->relname), + makeString(n->conname)); stmt->comment = comment; cxt->alist = lappend(cxt->alist, stmt); @@ -1227,6 +1385,7 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename) n->is_local = true; n->is_not_null = false; n->is_from_type = true; + n->is_from_parent = false; n->storage = 0; n->raw_default = NULL; n->cooked_default = NULL; @@ -1641,9 +1800,8 @@ transformIndexConstraints(CreateStmtContext *cxt) */ foreach(lc, cxt->ixconstraints) { - Constraint *constraint = (Constraint *) lfirst(lc); + Constraint *constraint = lfirst_node(Constraint, lc); - Assert(IsA(constraint, Constraint)); Assert(constraint->contype == CONSTR_PRIMARY || constraint->contype == CONSTR_UNIQUE || constraint->contype == CONSTR_EXCLUSION); @@ -1970,10 +2128,8 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) List *opname; Assert(list_length(pair) == 2); - elem = (IndexElem *) linitial(pair); - Assert(IsA(elem, IndexElem)); - opname = (List *) lsecond(pair); - Assert(IsA(opname, List)); + elem = linitial_node(IndexElem, pair); + opname = lsecond_node(List, pair); index->indexParams = lappend(index->indexParams, elem); index->excludeOpNames = lappend(index->excludeOpNames, opname); @@ -2000,8 +2156,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) foreach(columns, cxt->columns) { - column = (ColumnDef *) lfirst(columns); - Assert(IsA(column, ColumnDef)); + column = lfirst_node(ColumnDef, columns); if (strcmp(column->colname, key) == 0) { found = true; @@ -2030,15 +2185,15 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) foreach(inher, cxt->inhRelations) { - RangeVar *inh = (RangeVar *) lfirst(inher); + RangeVar *inh = lfirst_node(RangeVar, inher); Relation rel; int count; - Assert(IsA(inh, RangeVar)); rel = heap_openrv(inh, AccessShareLock); /* check user requested inheritance from valid relkind */ if (rel->rd_rel->relkind != RELKIND_RELATION && - rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE) + rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE && + rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("inherited relation \"%s\" is not a table or foreign table", @@ -2358,7 +2513,7 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString) * We must not scribble on the passed-in IndexStmt, so copy it. (This is * overkill, but easy.) */ - stmt = (IndexStmt *) copyObject(stmt); + stmt = copyObject(stmt); /* Set up pstate */ pstate = make_parsestate(NULL); @@ -2406,17 +2561,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString) /* * transformExpr() should have already rejected subqueries, - * aggregates, and window functions, based on the EXPR_KIND_ for - * an index expression. + * aggregates, window functions, and SRFs, based on the EXPR_KIND_ + * for an index expression. * - * Also reject expressions returning sets; this is for consistency - * with what transformWhereClause() checks for the predicate. * DefineIndex() will make more checks. */ - if (expression_returns_set(ielem->expr)) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("index expression cannot return a set"))); } } @@ -2784,7 +2933,7 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt, * We must not scribble on the passed-in AlterTableStmt, so copy it. (This * is overkill, but easy.) */ - stmt = (AlterTableStmt *) copyObject(stmt); + stmt = copyObject(stmt); /* Caller is responsible for locking the relation */ rel = relation_open(relid, NoLock); @@ -2830,6 +2979,8 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt, cxt.distributeby = NULL; cxt.subcluster = NULL; #endif + cxt.ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE); + cxt.partbound = NULL; /* * The only subtypes that currently require parse transformation handling @@ -2845,9 +2996,8 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt, case AT_AddColumn: case AT_AddColumnToView: { - ColumnDef *def = (ColumnDef *) cmd->def; + ColumnDef *def = castNode(ColumnDef, cmd->def); - Assert(IsA(def, ColumnDef)); transformColumnDefinition(&cxt, def); /* @@ -2898,6 +3048,7 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt, case AT_AlterColumnType: { ColumnDef *def = (ColumnDef *) cmd->def; + AttrNumber attnum; /* * For ALTER COLUMN TYPE, transform the USING clause if @@ -2908,18 +3059,133 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt, def->cooked_default = transformExpr(pstate, def->raw_default, EXPR_KIND_ALTER_COL_TRANSFORM); + } - /* it can't return a set */ - if (expression_returns_set(def->cooked_default)) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("transform expression must not return a set"))); + /* + * For identity column, create ALTER SEQUENCE command to + * change the data type of the sequence. + */ + attnum = get_attnum(relid, cmd->name); + + /* + * if attribute not found, something will error about it + * later + */ + if (attnum != InvalidAttrNumber && get_attidentity(relid, attnum)) + { + Oid seq_relid = getOwnedSequence(relid, attnum); + Oid typeOid = typenameTypeId(pstate, def->typeName); + AlterSeqStmt *altseqstmt = makeNode(AlterSeqStmt); + + altseqstmt->sequence = makeRangeVar(get_namespace_name(get_rel_namespace(seq_relid)), + get_rel_name(seq_relid), + -1); + altseqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(typeOid, -1), -1)); + altseqstmt->for_identity = true; + cxt.blist = lappend(cxt.blist, altseqstmt); + } + + newcmds = lappend(newcmds, cmd); + break; + } + + case AT_AddIdentity: + { + Constraint *def = castNode(Constraint, cmd->def); + ColumnDef *newdef = makeNode(ColumnDef); + AttrNumber attnum; + + newdef->colname = cmd->name; + newdef->identity = def->generated_when; + cmd->def = (Node *) newdef; + + attnum = get_attnum(relid, cmd->name); + + /* + * if attribute not found, something will error about it + * later + */ + if (attnum != InvalidAttrNumber) + generateSerialExtraStmts(&cxt, newdef, + get_atttype(relid, attnum), + def->options, true, + NULL, NULL); + + newcmds = lappend(newcmds, cmd); + break; + } + + case AT_SetIdentity: + { + /* + * Create an ALTER SEQUENCE statement for the internal + * sequence of the identity column. + */ + ListCell *lc; + List *newseqopts = NIL; + List *newdef = NIL; + List *seqlist; + AttrNumber attnum; + + /* + * Split options into those handled by ALTER SEQUENCE and + * those for ALTER TABLE proper. + */ + foreach(lc, castNode(List, cmd->def)) + { + DefElem *def = lfirst_node(DefElem, lc); + + if (strcmp(def->defname, "generated") == 0) + newdef = lappend(newdef, def); + else + newseqopts = lappend(newseqopts, def); } + attnum = get_attnum(relid, cmd->name); + + if (attnum) + { + seqlist = getOwnedSequences(relid, attnum); + if (seqlist) + { + AlterSeqStmt *seqstmt; + Oid seq_relid; + + seqstmt = makeNode(AlterSeqStmt); + seq_relid = linitial_oid(seqlist); + seqstmt->sequence = makeRangeVar(get_namespace_name(get_rel_namespace(seq_relid)), + get_rel_name(seq_relid), -1); + seqstmt->options = newseqopts; + seqstmt->for_identity = true; + seqstmt->missing_ok = false; + + cxt.alist = lappend(cxt.alist, seqstmt); + } + } + + /* + * If column was not found or was not an identity column, + * we just let the ALTER TABLE command error out later. + */ + + cmd->def = (Node *) newdef; newcmds = lappend(newcmds, cmd); break; } + case AT_AttachPartition: + case AT_DetachPartition: + { + PartitionCmd *partcmd = (PartitionCmd *) cmd->def; + + transformPartitionCmd(&cxt, partcmd); + /* assign transformed value of the partition bound */ + partcmd->bound = cxt.partbound; + } + + newcmds = lappend(newcmds, cmd); + break; + default: newcmds = lappend(newcmds, cmd); break; @@ -2947,9 +3213,8 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt, */ foreach(l, cxt.alist) { - IndexStmt *idxstmt = (IndexStmt *) lfirst(l); + IndexStmt *idxstmt = lfirst_node(IndexStmt, l); - Assert(IsA(idxstmt, IndexStmt)); idxstmt = transformIndexStmt(relid, idxstmt, queryString); newcmd = makeNode(AlterTableCmd); newcmd->subtype = OidIsValid(idxstmt->indexOid) ? AT_AddIndexConstraint : AT_AddIndex; @@ -3791,3 +4056,275 @@ makeSubCluster(List *nodelist) return result; } #endif + +/* + * transformPartitionCmd + * Analyze the ATTACH/DETACH PARTITION command + * + * In case of the ATTACH PARTITION command, cxt->partbound is set to the + * transformed value of cmd->bound. + */ +static void +transformPartitionCmd(CreateStmtContext *cxt, PartitionCmd *cmd) +{ + Relation parentRel = cxt->rel; + + /* the table must be partitioned */ + if (parentRel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("\"%s\" is not partitioned", + RelationGetRelationName(parentRel)))); + + /* transform the partition bound, if any */ + Assert(RelationGetPartitionKey(parentRel) != NULL); + if (cmd->bound != NULL) + cxt->partbound = transformPartitionBound(cxt->pstate, parentRel, + cmd->bound); +} + +/* + * transformPartitionBound + * + * Transform a partition bound specification + */ +PartitionBoundSpec * +transformPartitionBound(ParseState *pstate, Relation parent, + PartitionBoundSpec *spec) +{ + PartitionBoundSpec *result_spec; + PartitionKey key = RelationGetPartitionKey(parent); + char strategy = get_partition_strategy(key); + int partnatts = get_partition_natts(key); + List *partexprs = get_partition_exprs(key); + + /* Avoid scribbling on input */ + result_spec = copyObject(spec); + + if (strategy == PARTITION_STRATEGY_LIST) + { + ListCell *cell; + char *colname; + Oid coltype; + int32 coltypmod; + + if (spec->strategy != PARTITION_STRATEGY_LIST) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("invalid bound specification for a list partition"), + parser_errposition(pstate, exprLocation((Node *) spec)))); + + /* Get the only column's name in case we need to output an error */ + if (key->partattrs[0] != 0) + colname = get_relid_attribute_name(RelationGetRelid(parent), + key->partattrs[0]); + else + colname = deparse_expression((Node *) linitial(partexprs), + deparse_context_for(RelationGetRelationName(parent), + RelationGetRelid(parent)), + false, false); + /* Need its type data too */ + coltype = get_partition_col_typid(key, 0); + coltypmod = get_partition_col_typmod(key, 0); + + result_spec->listdatums = NIL; + foreach(cell, spec->listdatums) + { + A_Const *con = castNode(A_Const, lfirst(cell)); + Const *value; + ListCell *cell2; + bool duplicate; + + value = transformPartitionBoundValue(pstate, con, + colname, coltype, coltypmod); + + /* Don't add to the result if the value is a duplicate */ + duplicate = false; + foreach(cell2, result_spec->listdatums) + { + Const *value2 = castNode(Const, lfirst(cell2)); + + if (equal(value, value2)) + { + duplicate = true; + break; + } + } + if (duplicate) + continue; + + result_spec->listdatums = lappend(result_spec->listdatums, + value); + } + } + else if (strategy == PARTITION_STRATEGY_RANGE) + { + ListCell *cell1, + *cell2; + int i, + j; + bool seen_unbounded; + + if (spec->strategy != PARTITION_STRATEGY_RANGE) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("invalid bound specification for a range partition"), + parser_errposition(pstate, exprLocation((Node *) spec)))); + + if (list_length(spec->lowerdatums) != partnatts) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("FROM must specify exactly one value per partitioning column"))); + if (list_length(spec->upperdatums) != partnatts) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("TO must specify exactly one value per partitioning column"))); + + /* + * Check that no finite value follows an UNBOUNDED item in either of + * lower and upper bound lists. + */ + seen_unbounded = false; + foreach(cell1, spec->lowerdatums) + { + PartitionRangeDatum *ldatum = castNode(PartitionRangeDatum, + lfirst(cell1)); + + if (ldatum->infinite) + seen_unbounded = true; + else if (seen_unbounded) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("cannot specify finite value after UNBOUNDED"), + parser_errposition(pstate, exprLocation((Node *) ldatum)))); + } + seen_unbounded = false; + foreach(cell1, spec->upperdatums) + { + PartitionRangeDatum *rdatum = castNode(PartitionRangeDatum, + lfirst(cell1)); + + if (rdatum->infinite) + seen_unbounded = true; + else if (seen_unbounded) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("cannot specify finite value after UNBOUNDED"), + parser_errposition(pstate, exprLocation((Node *) rdatum)))); + } + + /* Transform all the constants */ + i = j = 0; + result_spec->lowerdatums = result_spec->upperdatums = NIL; + forboth(cell1, spec->lowerdatums, cell2, spec->upperdatums) + { + PartitionRangeDatum *ldatum = (PartitionRangeDatum *) lfirst(cell1); + PartitionRangeDatum *rdatum = (PartitionRangeDatum *) lfirst(cell2); + char *colname; + Oid coltype; + int32 coltypmod; + A_Const *con; + Const *value; + + /* Get the column's name in case we need to output an error */ + if (key->partattrs[i] != 0) + colname = get_relid_attribute_name(RelationGetRelid(parent), + key->partattrs[i]); + else + { + colname = deparse_expression((Node *) list_nth(partexprs, j), + deparse_context_for(RelationGetRelationName(parent), + RelationGetRelid(parent)), + false, false); + ++j; + } + /* Need its type data too */ + coltype = get_partition_col_typid(key, i); + coltypmod = get_partition_col_typmod(key, i); + + if (ldatum->value) + { + con = castNode(A_Const, ldatum->value); + value = transformPartitionBoundValue(pstate, con, + colname, + coltype, coltypmod); + if (value->constisnull) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("cannot specify NULL in range bound"))); + ldatum = copyObject(ldatum); /* don't scribble on input */ + ldatum->value = (Node *) value; + } + + if (rdatum->value) + { + con = castNode(A_Const, rdatum->value); + value = transformPartitionBoundValue(pstate, con, + colname, + coltype, coltypmod); + if (value->constisnull) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("cannot specify NULL in range bound"))); + rdatum = copyObject(rdatum); /* don't scribble on input */ + rdatum->value = (Node *) value; + } + + result_spec->lowerdatums = lappend(result_spec->lowerdatums, + ldatum); + result_spec->upperdatums = lappend(result_spec->upperdatums, + rdatum); + + ++i; + } + } + else + elog(ERROR, "unexpected partition strategy: %d", (int) strategy); + + return result_spec; +} + +/* + * Transform one constant in a partition bound spec + */ +static Const * +transformPartitionBoundValue(ParseState *pstate, A_Const *con, + const char *colName, Oid colType, int32 colTypmod) +{ + Node *value; + + /* Make it into a Const */ + value = (Node *) make_const(pstate, &con->val, con->location); + + /* Coerce to correct type */ + value = coerce_to_target_type(pstate, + value, exprType(value), + colType, + colTypmod, + COERCION_ASSIGNMENT, + COERCE_IMPLICIT_CAST, + -1); + + if (value == NULL) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("specified value cannot be cast to type %s for column \"%s\"", + format_type_be(colType), colName), + parser_errposition(pstate, con->location))); + + /* Simplify the expression, in case we had a coercion */ + if (!IsA(value, Const)) + value = (Node *) expression_planner((Expr *) value); + + /* Fail if we don't have a constant (i.e., non-immutable coercion) */ + if (!IsA(value, Const)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("specified value cannot be cast to type %s for column \"%s\"", + format_type_be(colType), colName), + errdetail("The cast requires a non-immutable conversion."), + errhint("Try putting the literal value in single quotes."), + parser_errposition(pstate, con->location))); + + return (Const *) value; +} diff --git a/src/backend/parser/parser.c b/src/backend/parser/parser.c index 2cc9b54dd5..522d7ec203 100644 --- a/src/backend/parser/parser.c +++ b/src/backend/parser/parser.c @@ -10,7 +10,7 @@ * analyze.c and related files. * * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION @@ -29,7 +29,8 @@ * raw_parser * Given a query in string form, do lexical and grammatical analysis. * - * Returns a list of raw (un-analyzed) parse trees. + * Returns a list of raw (un-analyzed) parse trees. The immediate elements + * of the list are always RawStmt nodes. */ List * raw_parser(const char *str, List **queries) diff --git a/src/backend/parser/scan.l b/src/backend/parser/scan.l index e693293c59..83a73410b8 100644 --- a/src/backend/parser/scan.l +++ b/src/backend/parser/scan.l @@ -21,7 +21,7 @@ * Postgres 9.2, this check is made automatically by the Makefile.) * * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION @@ -1439,6 +1439,13 @@ litbuf_udeescape(unsigned char escape, core_yyscan_t yyscanner) } } + /* unfinished surrogate pair? */ + if (pair_first) + { + ADVANCE_YYLLOC(in - litbuf + 3); /* 3 for U&" */ + yyerror("invalid Unicode surrogate pair"); + } + *out = '\0'; /* diff --git a/src/backend/parser/scansup.c b/src/backend/parser/scansup.c index 7aa5b76841..c3d2805803 100644 --- a/src/backend/parser/scansup.c +++ b/src/backend/parser/scansup.c @@ -4,7 +4,7 @@ * support routines for the lex/flex scanner, used by both the normal * backend as well as the bootstrap backend * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * |