diff options
Diffstat (limited to 'src/backend/parser/parse_clause.c')
-rw-r--r-- | src/backend/parser/parse_clause.c | 245 |
1 files changed, 244 insertions, 1 deletions
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index b5eae56006d..47ca685b568 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -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" @@ -65,6 +66,8 @@ 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, @@ -693,6 +696,229 @@ 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 @@ -795,7 +1021,6 @@ transformRangeTableSample(ParseState *pstate, RangeTableSample *rts) return tablesample; } - /* * transformFromClauseItem - * Transform a FROM-clause item, adding any required entries to the @@ -891,6 +1116,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) */ |