diff options
Diffstat (limited to 'src/backend/parser')
-rw-r--r-- | src/backend/parser/Makefile | 5 | ||||
-rw-r--r-- | src/backend/parser/analyze.c | 14 | ||||
-rw-r--r-- | src/backend/parser/parse_clause.c | 59 | ||||
-rw-r--r-- | src/backend/parser/parse_enr.c | 29 | ||||
-rw-r--r-- | src/backend/parser/parse_node.c | 2 | ||||
-rw-r--r-- | src/backend/parser/parse_relation.c | 143 | ||||
-rw-r--r-- | src/backend/parser/parse_target.c | 2 |
7 files changed, 238 insertions, 16 deletions
diff --git a/src/backend/parser/Makefile b/src/backend/parser/Makefile index df9a9fbb35e..4b97f838036 100644 --- a/src/backend/parser/Makefile +++ b/src/backend/parser/Makefile @@ -14,8 +14,9 @@ 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 diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 8f11c46621e..811fccaec97 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -94,7 +94,8 @@ static bool test_raw_expression_coverage(Node *node, void *context); */ Query * parse_analyze(RawStmt *parseTree, const char *sourceText, - Oid *paramTypes, int numParams) + Oid *paramTypes, int numParams, + QueryEnvironment *queryEnv) { ParseState *pstate = make_parsestate(NULL); Query *query; @@ -106,6 +107,8 @@ parse_analyze(RawStmt *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) @@ -2799,6 +2802,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/parse_clause.c b/src/backend/parser/parse_clause.c index 4f391d2d411..e268a127d13 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -59,9 +59,12 @@ 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, @@ -181,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); @@ -435,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 * @@ -1021,6 +1046,24 @@ transformRangeTableSample(ParseState *pstate, RangeTableSample *rts) return tablesample; } + +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 @@ -1055,18 +1098,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); diff --git a/src/backend/parser/parse_enr.c b/src/backend/parser/parse_enr.c new file mode 100644 index 00000000000..1cfcf65a512 --- /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_node.c b/src/backend/parser/parse_node.c index 30cc7dadca3..34006c70cd6 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -62,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; diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 2c19e0cbf58..7db13f37f72 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -25,6 +25,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" @@ -282,6 +283,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. @@ -302,6 +313,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; @@ -318,11 +330,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++) @@ -342,6 +359,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; } @@ -1139,12 +1160,17 @@ 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", @@ -1940,6 +1966,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 = + get_visible_ENR(pstate, rv->relname); + TupleDesc tupdesc; + int attno; + + Assert(enrmd != NULL); + + switch (enrmd->enrtype) + { + case ENR_NAMED_TUPLESTORE: + rte->rtekind = RTE_NAMEDTUPLESTORE; + break; + + default: + elog(ERROR, "unexpected enrtype of %i", 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. + */ + if (pstate != NULL) + pstate->p_rtable = lappend(pstate->p_rtable, rte); + + return rte; +} + /* * Has the specified refname been selected FOR UPDATE/FOR SHARE? @@ -2292,6 +2414,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, 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); @@ -2705,6 +2828,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, case RTE_TABLEFUNC: case RTE_VALUES: case RTE_CTE: + case RTE_NAMEDTUPLESTORE: { /* * tablefunc, VALUES or CTE RTE --- get type info from lists @@ -2762,6 +2886,19 @@ get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum) */ result = false; break; + case RTE_NAMEDTUPLESTORE: + { + Assert(rte->enrname); + + /* + * We checked when we loaded ctecoltypes for the tuplestore + * that InvalidOid was only used for dropped columns, so it is + * safe to count on that here. + */ + result = + (list_nth(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 3b84140a9be..c46c3b38a49 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -397,6 +397,7 @@ markTargetListOrigin(ParseState *pstate, TargetEntry *tle, case RTE_FUNCTION: case RTE_VALUES: case RTE_TABLEFUNC: + case RTE_NAMEDTUPLESTORE: /* not a simple relation, leave it unmarked */ break; case RTE_CTE: @@ -1505,6 +1506,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 |