mirror of
https://github.com/postgres/postgres.git
synced 2025-10-29 22:49:41 +03:00
Support XMLTABLE query expression
XMLTABLE is defined by the SQL/XML standard as a feature that allows turning XML-formatted data into relational form, so that it can be used as a <table primary> in the FROM clause of a query. This new construct provides significant simplicity and performance benefit for XML data processing; what in a client-side custom implementation was reported to take 20 minutes can be executed in 400ms using XMLTABLE. (The same functionality was said to take 10 seconds using nested PostgreSQL XPath function calls, and 5 seconds using XMLReader under PL/Python). The implemented syntax deviates slightly from what the standard requires. First, the standard indicates that the PASSING clause is optional and that multiple XML input documents may be given to it; we make it mandatory and accept a single document only. Second, we don't currently support a default namespace to be specified. This implementation relies on a new executor node based on a hardcoded method table. (Because the grammar is fixed, there is no extensibility in the current approach; further constructs can be implemented on top of this such as JSON_TABLE, but they require changes to core code.) Author: Pavel Stehule, Álvaro Herrera Extensively reviewed by: Craig Ringer Discussion: https://postgr.es/m/CAFj8pRAgfzMD-LoSmnMGybD0WsEznLHWap8DO79+-GTRAPR4qA@mail.gmail.com
This commit is contained in:
@@ -2772,6 +2772,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),
|
||||
|
||||
@@ -464,7 +464,7 @@ 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
|
||||
columnref in_expr having_clause func_table xmltable array_expr
|
||||
ExclusionWhereClause
|
||||
%type <list> rowsfrom_item rowsfrom_list opt_col_def_list
|
||||
%type <boolean> opt_ordinality
|
||||
@@ -550,6 +550,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
|
||||
@@ -607,7 +612,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
||||
|
||||
CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
|
||||
CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
|
||||
CLUSTER COALESCE COLLATE COLLATION COLUMN COMMENT COMMENTS COMMIT
|
||||
CLUSTER COALESCE COLLATE COLLATION COLUMN COLUMNS COMMENT COMMENTS COMMIT
|
||||
COMMITTED CONCURRENTLY CONFIGURATION CONFLICT CONNECTION CONSTRAINT
|
||||
CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE
|
||||
CROSS CSV CUBE CURRENT_P
|
||||
@@ -681,8 +686,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
|
||||
|
||||
@@ -11187,6 +11192,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);
|
||||
@@ -11626,6 +11644,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
|
||||
@@ -14205,6 +14383,7 @@ unreserved_keyword:
|
||||
| CLASS
|
||||
| CLOSE
|
||||
| CLUSTER
|
||||
| COLUMNS
|
||||
| COMMENT
|
||||
| COMMENTS
|
||||
| COMMIT
|
||||
@@ -14510,10 +14689,12 @@ col_name_keyword:
|
||||
| XMLELEMENT
|
||||
| XMLEXISTS
|
||||
| XMLFOREST
|
||||
| XMLNAMESPACES
|
||||
| XMLPARSE
|
||||
| XMLPI
|
||||
| XMLROOT
|
||||
| XMLSERIALIZE
|
||||
| XMLTABLE
|
||||
;
|
||||
|
||||
/* Type/function identifier --- keywords that can be type or function names.
|
||||
|
||||
@@ -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,
|
||||
@@ -692,6 +695,229 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
|
||||
return rte;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
@@ -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) */
|
||||
|
||||
@@ -1096,9 +1096,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.
|
||||
*
|
||||
@@ -1106,9 +1106,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);
|
||||
|
||||
@@ -1117,7 +1117,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);
|
||||
@@ -1144,6 +1144,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
|
||||
|
||||
@@ -1627,6 +1627,69 @@ addRangeTableEntryForFunction(ParseState *pstate,
|
||||
return rte;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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).
|
||||
*
|
||||
@@ -2226,10 +2289,11 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RTE_TABLEFUNC:
|
||||
case RTE_VALUES:
|
||||
case RTE_CTE:
|
||||
{
|
||||
/* Values or CTE RTE */
|
||||
/* Tablefunc, Values or CTE RTE */
|
||||
ListCell *aliasp_item = list_head(rte->eref->colnames);
|
||||
ListCell *lct;
|
||||
ListCell *lcm;
|
||||
@@ -2638,10 +2702,14 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
|
||||
*varcollid = exprCollation(aliasvar);
|
||||
}
|
||||
break;
|
||||
case RTE_TABLEFUNC:
|
||||
case RTE_VALUES:
|
||||
case RTE_CTE:
|
||||
{
|
||||
/* VALUES or CTE RTE --- get type info from lists in the RTE */
|
||||
/*
|
||||
* 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);
|
||||
@@ -2684,9 +2752,14 @@ 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_JOIN:
|
||||
|
||||
@@ -396,6 +396,7 @@ markTargetListOrigin(ParseState *pstate, TargetEntry *tle,
|
||||
break;
|
||||
case RTE_FUNCTION:
|
||||
case RTE_VALUES:
|
||||
case RTE_TABLEFUNC:
|
||||
/* not a simple relation, leave it unmarked */
|
||||
break;
|
||||
case RTE_CTE:
|
||||
@@ -1557,6 +1558,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)
|
||||
|
||||
Reference in New Issue
Block a user