1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-03 20:02:46 +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:
Alvaro Herrera
2017-03-08 12:39:37 -03:00
parent 270d7dd8a5
commit fcec6caafa
52 changed files with 4606 additions and 50 deletions

View File

@ -134,6 +134,8 @@ static FunctionScan *create_functionscan_plan(PlannerInfo *root, Path *best_path
List *tlist, List *scan_clauses);
static ValuesScan *create_valuesscan_plan(PlannerInfo *root, Path *best_path,
List *tlist, List *scan_clauses);
static TableFuncScan *create_tablefuncscan_plan(PlannerInfo *root, Path *best_path,
List *tlist, List *scan_clauses);
static CteScan *create_ctescan_plan(PlannerInfo *root, Path *best_path,
List *tlist, List *scan_clauses);
static WorkTableScan *create_worktablescan_plan(PlannerInfo *root, Path *best_path,
@ -190,6 +192,8 @@ static FunctionScan *make_functionscan(List *qptlist, List *qpqual,
Index scanrelid, List *functions, bool funcordinality);
static ValuesScan *make_valuesscan(List *qptlist, List *qpqual,
Index scanrelid, List *values_lists);
static TableFuncScan *make_tablefuncscan(List *qptlist, List *qpqual,
Index scanrelid, TableFunc *tablefunc);
static CteScan *make_ctescan(List *qptlist, List *qpqual,
Index scanrelid, int ctePlanId, int cteParam);
static WorkTableScan *make_worktablescan(List *qptlist, List *qpqual,
@ -355,6 +359,7 @@ create_plan_recurse(PlannerInfo *root, Path *best_path, int flags)
case T_TidScan:
case T_SubqueryScan:
case T_FunctionScan:
case T_TableFuncScan:
case T_ValuesScan:
case T_CteScan:
case T_WorkTableScan:
@ -635,6 +640,13 @@ create_scan_plan(PlannerInfo *root, Path *best_path, int flags)
scan_clauses);
break;
case T_TableFuncScan:
plan = (Plan *) create_tablefuncscan_plan(root,
best_path,
tlist,
scan_clauses);
break;
case T_ValuesScan:
plan = (Plan *) create_valuesscan_plan(root,
best_path,
@ -749,11 +761,12 @@ use_physical_tlist(PlannerInfo *root, Path *path, int flags)
/*
* We can do this for real relation scans, subquery scans, function scans,
* values scans, and CTE scans (but not for, eg, joins).
* tablefunc scans, values scans, and CTE scans (but not for, eg, joins).
*/
if (rel->rtekind != RTE_RELATION &&
rel->rtekind != RTE_SUBQUERY &&
rel->rtekind != RTE_FUNCTION &&
rel->rtekind != RTE_TABLEFUNC &&
rel->rtekind != RTE_VALUES &&
rel->rtekind != RTE_CTE)
return false;
@ -3014,6 +3027,49 @@ create_functionscan_plan(PlannerInfo *root, Path *best_path,
return scan_plan;
}
/*
* create_tablefuncscan_plan
* Returns a tablefuncscan plan for the base relation scanned by 'best_path'
* with restriction clauses 'scan_clauses' and targetlist 'tlist'.
*/
static TableFuncScan *
create_tablefuncscan_plan(PlannerInfo *root, Path *best_path,
List *tlist, List *scan_clauses)
{
TableFuncScan *scan_plan;
Index scan_relid = best_path->parent->relid;
RangeTblEntry *rte;
TableFunc *tablefunc;
/* it should be a function base rel... */
Assert(scan_relid > 0);
rte = planner_rt_fetch(scan_relid, root);
Assert(rte->rtekind == RTE_TABLEFUNC);
tablefunc = rte->tablefunc;
/* Sort clauses into best execution order */
scan_clauses = order_qual_clauses(root, scan_clauses);
/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
scan_clauses = extract_actual_clauses(scan_clauses, false);
/* Replace any outer-relation variables with nestloop params */
if (best_path->param_info)
{
scan_clauses = (List *)
replace_nestloop_params(root, (Node *) scan_clauses);
/* The function expressions could contain nestloop params, too */
tablefunc = (TableFunc *) replace_nestloop_params(root, (Node *) tablefunc);
}
scan_plan = make_tablefuncscan(tlist, scan_clauses, scan_relid,
tablefunc);
copy_generic_path_info(&scan_plan->scan.plan, best_path);
return scan_plan;
}
/*
* create_valuesscan_plan
* Returns a valuesscan plan for the base relation scanned by 'best_path'
@ -4909,6 +4965,25 @@ make_functionscan(List *qptlist,
return node;
}
static TableFuncScan *
make_tablefuncscan(List *qptlist,
List *qpqual,
Index scanrelid,
TableFunc *tablefunc)
{
TableFuncScan *node = makeNode(TableFuncScan);
Plan *plan = &node->scan.plan;
plan->targetlist = qptlist;
plan->qual = qpqual;
plan->lefttree = NULL;
plan->righttree = NULL;
node->scan.scanrelid = scanrelid;
node->tablefunc = tablefunc;
return node;
}
static ValuesScan *
make_valuesscan(List *qptlist,
List *qpqual,