1
0
mirror of https://github.com/postgres/postgres.git synced 2025-08-28 18:48:04 +03:00

Add infrastructure to support EphemeralNamedRelation references.

A QueryEnvironment concept is added, which allows new types of
objects to be passed into queries from parsing on through
execution.  At this point, the only thing implemented is a
collection of EphemeralNamedRelation objects -- relations which
can be referenced by name in queries, but do not exist in the
catalogs.  The only type of ENR implemented is NamedTuplestore, but
provision is made to add more types fairly easily.

An ENR can carry its own TupleDesc or reference a relation in the
catalogs by relid.

Although these features can be used without SPI, convenience
functions are added to SPI so that ENRs can easily be used by code
run through SPI.

The initial use of all this is going to be transition tables in
AFTER triggers, but that will be added to each PL as a separate
commit.

An incidental effect of this patch is to produce a more informative
error message if an attempt is made to modify the contents of a CTE
from a referencing DML statement.  No tests previously covered that
possibility, so one is added.

Kevin Grittner and Thomas Munro
Reviewed by Heikki Linnakangas, David Fetter, and Thomas Munro
with valuable comments and suggestions from many others
This commit is contained in:
Kevin Grittner
2017-03-31 23:17:18 -05:00
parent 25dc142a49
commit 18ce3a4ab2
78 changed files with 1598 additions and 122 deletions

View File

@@ -934,7 +934,8 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
querytree_sublist = pg_analyze_and_rewrite_params(parsetree,
prosrc,
(ParserSetupHook) sql_fn_parser_setup,
pinfo);
pinfo,
NULL);
querytree_list = list_concat(querytree_list,
querytree_sublist);
}

View File

@@ -1471,7 +1471,8 @@ BeginCopy(ParseState *pstate,
* DECLARE CURSOR and PREPARE.) XXX FIXME someday.
*/
rewritten = pg_analyze_and_rewrite(copyObject(raw_query),
pstate->p_sourcetext, NULL, 0);
pstate->p_sourcetext, NULL, 0,
NULL);
/* check that we got back something we can work with */
if (rewritten == NIL)
@@ -1574,7 +1575,7 @@ BeginCopy(ParseState *pstate,
cstate->queryDesc = CreateQueryDesc(plan, pstate->p_sourcetext,
GetActiveSnapshot(),
InvalidSnapshot,
dest, NULL, 0);
dest, NULL, NULL, 0);
/*
* Call ExecutorStart to prepare the plan for execution.

View File

@@ -222,7 +222,8 @@ create_ctas_nodata(List *tlist, IntoClause *into)
*/
ObjectAddress
ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
ParamListInfo params, char *completionTag)
ParamListInfo params, QueryEnvironment *queryEnv,
char *completionTag)
{
Query *query = castNode(Query, stmt->query);
IntoClause *into = stmt->into;
@@ -341,7 +342,7 @@ ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
/* Create a QueryDesc, redirecting output to our tuple receiver */
queryDesc = CreateQueryDesc(plan, queryString,
GetActiveSnapshot(), InvalidSnapshot,
dest, params, 0);
dest, params, queryEnv, 0);
/* call ExecutorStart to prepare the plan for execution */
ExecutorStart(queryDesc, GetIntoRelEFlags(into));

View File

@@ -55,7 +55,8 @@ explain_get_index_name_hook_type explain_get_index_name_hook = NULL;
static void ExplainOneQuery(Query *query, int cursorOptions,
IntoClause *into, ExplainState *es,
const char *queryString, ParamListInfo params);
const char *queryString, ParamListInfo params,
QueryEnvironment *queryEnv);
static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
ExplainState *es);
static double elapsed_time(instr_time *starttime);
@@ -142,7 +143,8 @@ static void escape_yaml(StringInfo buf, const char *str);
*/
void
ExplainQuery(ParseState *pstate, ExplainStmt *stmt, const char *queryString,
ParamListInfo params, DestReceiver *dest)
ParamListInfo params, QueryEnvironment *queryEnv,
DestReceiver *dest)
{
ExplainState *es = NewExplainState();
TupOutputState *tstate;
@@ -253,7 +255,7 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt, const char *queryString,
{
ExplainOneQuery(castNode(Query, lfirst(l)),
CURSOR_OPT_PARALLEL_OK, NULL, es,
queryString, params);
queryString, params, queryEnv);
/* Separate plans with an appropriate separator */
if (lnext(l) != NULL)
@@ -338,12 +340,14 @@ ExplainResultDesc(ExplainStmt *stmt)
static void
ExplainOneQuery(Query *query, int cursorOptions,
IntoClause *into, ExplainState *es,
const char *queryString, ParamListInfo params)
const char *queryString, ParamListInfo params,
QueryEnvironment *queryEnv)
{
/* planner will not cope with utility statements */
if (query->commandType == CMD_UTILITY)
{
ExplainOneUtility(query->utilityStmt, into, es, queryString, params);
ExplainOneUtility(query->utilityStmt, into, es, queryString, params,
queryEnv);
return;
}
@@ -366,7 +370,8 @@ ExplainOneQuery(Query *query, int cursorOptions,
INSTR_TIME_SUBTRACT(planduration, planstart);
/* run it (if needed) and produce output */
ExplainOnePlan(plan, into, es, queryString, params, &planduration);
ExplainOnePlan(plan, into, es, queryString, params, queryEnv,
&planduration);
}
}
@@ -383,7 +388,8 @@ ExplainOneQuery(Query *query, int cursorOptions,
*/
void
ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es,
const char *queryString, ParamListInfo params)
const char *queryString, ParamListInfo params,
QueryEnvironment *queryEnv)
{
if (utilityStmt == NULL)
return;
@@ -404,7 +410,7 @@ ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es,
Assert(list_length(rewritten) == 1);
ExplainOneQuery(castNode(Query, linitial(rewritten)),
0, ctas->into, es,
queryString, params);
queryString, params, queryEnv);
}
else if (IsA(utilityStmt, DeclareCursorStmt))
{
@@ -423,11 +429,11 @@ ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es,
Assert(list_length(rewritten) == 1);
ExplainOneQuery(castNode(Query, linitial(rewritten)),
dcs->options, NULL, es,
queryString, params);
queryString, params, queryEnv);
}
else if (IsA(utilityStmt, ExecuteStmt))
ExplainExecuteQuery((ExecuteStmt *) utilityStmt, into, es,
queryString, params);
queryString, params, queryEnv);
else if (IsA(utilityStmt, NotifyStmt))
{
if (es->format == EXPLAIN_FORMAT_TEXT)
@@ -460,7 +466,7 @@ ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es,
void
ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
const char *queryString, ParamListInfo params,
const instr_time *planduration)
QueryEnvironment *queryEnv, const instr_time *planduration)
{
DestReceiver *dest;
QueryDesc *queryDesc;
@@ -505,7 +511,7 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
/* Create a QueryDesc for the query */
queryDesc = CreateQueryDesc(plannedstmt, queryString,
GetActiveSnapshot(), InvalidSnapshot,
dest, params, instrument_option);
dest, params, queryEnv, instrument_option);
/* Select execution options */
if (es->analyze)
@@ -796,6 +802,7 @@ ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
case T_TableFuncScan:
case T_ValuesScan:
case T_CteScan:
case T_NamedTuplestoreScan:
case T_WorkTableScan:
*rels_used = bms_add_member(*rels_used,
((Scan *) plan)->scanrelid);
@@ -951,6 +958,9 @@ ExplainNode(PlanState *planstate, List *ancestors,
case T_CteScan:
pname = sname = "CTE Scan";
break;
case T_NamedTuplestoreScan:
pname = sname = "Named Tuplestore Scan";
break;
case T_WorkTableScan:
pname = sname = "WorkTable Scan";
break;
@@ -1389,6 +1399,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
case T_SeqScan:
case T_ValuesScan:
case T_CteScan:
case T_NamedTuplestoreScan:
case T_WorkTableScan:
case T_SubqueryScan:
show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
@@ -2679,6 +2690,11 @@ ExplainTargetRel(Plan *plan, Index rti, ExplainState *es)
objectname = rte->ctename;
objecttag = "CTE Name";
break;
case T_NamedTuplestoreScan:
Assert(rte->rtekind == RTE_NAMEDTUPLESTORE);
objectname = rte->enrname;
objecttag = "Tuplestore Name";
break;
case T_WorkTableScan:
/* Assert it's on a self-reference CTE */
Assert(rte->rtekind == RTE_CTE);

View File

@@ -721,7 +721,8 @@ execute_sql_string(const char *sql, const char *filename)
stmt_list = pg_analyze_and_rewrite(parsetree,
sql,
NULL,
0);
0,
NULL);
stmt_list = pg_plan_queries(stmt_list, CURSOR_OPT_PARALLEL_OK, NULL);
foreach(lc2, stmt_list)
@@ -739,7 +740,7 @@ execute_sql_string(const char *sql, const char *filename)
qdesc = CreateQueryDesc(stmt,
sql,
GetActiveSnapshot(), NULL,
dest, NULL, 0);
dest, NULL, NULL, 0);
ExecutorStart(qdesc, 0);
ExecutorRun(qdesc, ForwardScanDirection, 0, true);
@@ -759,6 +760,7 @@ execute_sql_string(const char *sql, const char *filename)
sql,
PROCESS_UTILITY_QUERY,
NULL,
NULL,
dest,
NULL);
}

View File

@@ -1623,7 +1623,7 @@ ImportForeignSchema(ImportForeignSchemaStmt *stmt)
/* Execute statement */
ProcessUtility(pstmt,
cmd,
PROCESS_UTILITY_SUBCOMMAND, NULL,
PROCESS_UTILITY_SUBCOMMAND, NULL, NULL,
None_Receiver, NULL);
/* Be sure to advance the command counter between subcommands */

View File

@@ -418,7 +418,7 @@ refresh_matview_datafill(DestReceiver *dest, Query *query,
/* Create a QueryDesc, redirecting output to our tuple receiver */
queryDesc = CreateQueryDesc(plan, queryString,
GetActiveSnapshot(), InvalidSnapshot,
dest, NULL, 0);
dest, NULL, NULL, 0);
/* call ExecutorStart to prepare the plan for execution */
ExecutorStart(queryDesc, EXEC_FLAG_WITHOUT_OIDS);

View File

@@ -91,7 +91,7 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString,
* to see the unmodified raw parse tree.
*/
plansource = CreateCachedPlan(rawstmt, queryString,
CreateCommandTag(stmt->query));
CreateCommandTag(stmt->query), NULL);
/* Transform list of TypeNames to array of type OIDs */
nargs = list_length(stmt->argtypes);
@@ -243,7 +243,7 @@ ExecuteQuery(ExecuteStmt *stmt, IntoClause *intoClause,
entry->plansource->query_string);
/* Replan if needed, and increment plan refcount for portal */
cplan = GetCachedPlan(entry->plansource, paramLI, false);
cplan = GetCachedPlan(entry->plansource, paramLI, false, NULL);
plan_list = cplan->stmt_list;
/*
@@ -551,7 +551,7 @@ FetchPreparedStatementTargetList(PreparedStatement *stmt)
List *tlist;
/* Get the plan's primary targetlist */
tlist = CachedPlanGetTargetList(stmt->plansource);
tlist = CachedPlanGetTargetList(stmt->plansource, NULL);
/* Copy into caller's context in case plan gets invalidated */
return copyObject(tlist);
@@ -629,7 +629,8 @@ DropAllPreparedStatements(void)
*/
void
ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es,
const char *queryString, ParamListInfo params)
const char *queryString, ParamListInfo params,
QueryEnvironment *queryEnv)
{
PreparedStatement *entry;
const char *query_string;
@@ -668,7 +669,7 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es,
}
/* Replan if needed, and acquire a transient refcount */
cplan = GetCachedPlan(entry->plansource, paramLI, true);
cplan = GetCachedPlan(entry->plansource, paramLI, true, queryEnv);
INSTR_TIME_SET_CURRENT(planduration);
INSTR_TIME_SUBTRACT(planduration, planstart);
@@ -681,9 +682,11 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es,
PlannedStmt *pstmt = castNode(PlannedStmt, lfirst(p));
if (pstmt->commandType != CMD_UTILITY)
ExplainOnePlan(pstmt, into, es, query_string, paramLI, &planduration);
ExplainOnePlan(pstmt, into, es, query_string, paramLI, queryEnv,
&planduration);
else
ExplainOneUtility(pstmt->utilityStmt, into, es, query_string, paramLI);
ExplainOneUtility(pstmt->utilityStmt, into, es, query_string,
paramLI, queryEnv);
/* No need for CommandCounterIncrement, as ExplainOnePlan did it */

View File

@@ -194,6 +194,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString,
queryString,
PROCESS_UTILITY_SUBCOMMAND,
NULL,
NULL,
None_Receiver,
NULL);

View File

@@ -354,6 +354,13 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
* adjustments will be needed below.
*/
if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is a partitioned table",
RelationGetRelationName(rel)),
errdetail("Triggers on partitioned tables cannot have transition tables.")));
if (stmt->timing != TRIGGER_TYPE_AFTER)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -1173,7 +1180,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
/* ... and execute it */
ProcessUtility(wrapper,
"(generated ALTER TABLE ADD FOREIGN KEY command)",
PROCESS_UTILITY_SUBCOMMAND, NULL,
PROCESS_UTILITY_SUBCOMMAND, NULL, NULL,
None_Receiver, NULL);
/* Remove the matched item from the list */

View File

@@ -436,7 +436,7 @@ DefineView(ViewStmt *stmt, const char *queryString,
rawstmt->stmt_location = stmt_location;
rawstmt->stmt_len = stmt_len;
viewParse = parse_analyze(rawstmt, queryString, NULL, 0);
viewParse = parse_analyze(rawstmt, queryString, NULL, 0, NULL);
/*
* The grammar should ensure that the result is a single SELECT Query.

View File

@@ -25,7 +25,8 @@ OBJS = execAmi.o execCurrent.o execExpr.o execExprInterp.o \
nodeMaterial.o nodeMergeAppend.o nodeMergejoin.o nodeModifyTable.o \
nodeNestloop.o nodeProjectSet.o nodeRecursiveunion.o nodeResult.o \
nodeSamplescan.o nodeSeqscan.o nodeSetOp.o nodeSort.o nodeUnique.o \
nodeValuesscan.o nodeCtescan.o nodeWorktablescan.o \
nodeValuesscan.o \
nodeCtescan.o nodeNamedtuplestorescan.o nodeWorktablescan.o \
nodeGroup.o nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o \
nodeForeignscan.o nodeWindowAgg.o tstoreReceiver.o tqueue.o spi.o \
nodeTableFuncscan.o

View File

@@ -38,6 +38,7 @@
#include "executor/nodeMergeAppend.h"
#include "executor/nodeMergejoin.h"
#include "executor/nodeModifyTable.h"
#include "executor/nodeNamedtuplestorescan.h"
#include "executor/nodeNestloop.h"
#include "executor/nodeProjectSet.h"
#include "executor/nodeRecursiveunion.h"
@@ -211,6 +212,10 @@ ExecReScan(PlanState *node)
ExecReScanCteScan((CteScanState *) node);
break;
case T_NamedTuplestoreScanState:
ExecReScanNamedTuplestoreScan((NamedTuplestoreScanState *) node);
break;
case T_WorkTableScanState:
ExecReScanWorkTableScan((WorkTableScanState *) node);
break;
@@ -571,6 +576,7 @@ ExecMaterializesOutput(NodeTag plantype)
case T_FunctionScan:
case T_TableFuncScan:
case T_CteScan:
case T_NamedTuplestoreScan:
case T_WorkTableScan:
case T_Sort:
return true;

View File

@@ -198,6 +198,11 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
estate->es_sourceText = queryDesc->sourceText;
/*
* Fill in the query environment, if any, from queryDesc.
*/
estate->es_queryEnv = queryDesc->queryEnv;
/*
* If non-read-only query, set the command ID to mark output tuples with
*/

View File

@@ -710,7 +710,7 @@ ExecParallelGetQueryDesc(shm_toc *toc, DestReceiver *receiver,
return CreateQueryDesc(pstmt,
queryString,
GetActiveSnapshot(), InvalidSnapshot,
receiver, paramLI, instrument_options);
receiver, paramLI, NULL, instrument_options);
}
/*

View File

@@ -101,6 +101,7 @@
#include "executor/nodeMergeAppend.h"
#include "executor/nodeMergejoin.h"
#include "executor/nodeModifyTable.h"
#include "executor/nodeNamedtuplestorescan.h"
#include "executor/nodeNestloop.h"
#include "executor/nodeProjectSet.h"
#include "executor/nodeRecursiveunion.h"
@@ -256,6 +257,11 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
estate, eflags);
break;
case T_NamedTuplestoreScan:
result = (PlanState *) ExecInitNamedTuplestoreScan((NamedTuplestoreScan *) node,
estate, eflags);
break;
case T_WorkTableScan:
result = (PlanState *) ExecInitWorkTableScan((WorkTableScan *) node,
estate, eflags);
@@ -483,6 +489,10 @@ ExecProcNode(PlanState *node)
result = ExecCteScan((CteScanState *) node);
break;
case T_NamedTuplestoreScanState:
result = ExecNamedTuplestoreScan((NamedTuplestoreScanState *) node);
break;
case T_WorkTableScanState:
result = ExecWorkTableScan((WorkTableScanState *) node);
break;
@@ -751,6 +761,10 @@ ExecEndNode(PlanState *node)
ExecEndCteScan((CteScanState *) node);
break;
case T_NamedTuplestoreScanState:
ExecEndNamedTuplestoreScan((NamedTuplestoreScanState *) node);
break;
case T_WorkTableScanState:
ExecEndWorkTableScan((WorkTableScanState *) node);
break;

View File

@@ -120,6 +120,8 @@ CreateExecutorState(void)
estate->es_param_list_info = NULL;
estate->es_param_exec_vals = NULL;
estate->es_queryEnv = NULL;
estate->es_query_cxt = qcontext;
estate->es_tupleTable = NIL;

View File

@@ -713,7 +713,8 @@ init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK)
queryTree_sublist = pg_analyze_and_rewrite_params(parsetree,
fcache->src,
(ParserSetupHook) sql_fn_parser_setup,
fcache->pinfo);
fcache->pinfo,
NULL);
queryTree_list = lappend(queryTree_list, queryTree_sublist);
flat_query_list = list_concat(flat_query_list,
list_copy(queryTree_sublist));
@@ -809,7 +810,9 @@ postquel_start(execution_state *es, SQLFunctionCachePtr fcache)
GetActiveSnapshot(),
InvalidSnapshot,
dest,
fcache->paramLI, 0);
fcache->paramLI,
es->qd ? es->qd->queryEnv : NULL,
0);
/* Utility commands don't need Executor. */
if (es->qd->operation != CMD_UTILITY)
@@ -846,6 +849,7 @@ postquel_getnext(execution_state *es, SQLFunctionCachePtr fcache)
fcache->src,
PROCESS_UTILITY_QUERY,
es->qd->params,
es->qd->queryEnv,
es->qd->dest,
NULL);
result = true; /* never stops early */

View File

@@ -0,0 +1,198 @@
/*-------------------------------------------------------------------------
*
* nodeNamedtuplestorescan.c
* routines to handle NamedTuplestoreScan nodes.
*
* Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/backend/executor/nodeNamedtuplestorescan.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "executor/execdebug.h"
#include "executor/nodeNamedtuplestorescan.h"
#include "miscadmin.h"
#include "utils/queryenvironment.h"
static TupleTableSlot *NamedTuplestoreScanNext(NamedTuplestoreScanState *node);
/* ----------------------------------------------------------------
* NamedTuplestoreScanNext
*
* This is a workhorse for ExecNamedTuplestoreScan
* ----------------------------------------------------------------
*/
static TupleTableSlot *
NamedTuplestoreScanNext(NamedTuplestoreScanState *node)
{
TupleTableSlot *slot;
/* We intentionally do not support backward scan. */
Assert(ScanDirectionIsForward(node->ss.ps.state->es_direction));
/*
* Get the next tuple from tuplestore. Return NULL if no more tuples.
*/
slot = node->ss.ss_ScanTupleSlot;
(void) tuplestore_gettupleslot(node->relation, true, false, slot);
return slot;
}
/*
* NamedTuplestoreScanRecheck -- access method routine to recheck a tuple in
* EvalPlanQual
*/
static bool
NamedTuplestoreScanRecheck(NamedTuplestoreScanState *node, TupleTableSlot *slot)
{
/* nothing to check */
return true;
}
/* ----------------------------------------------------------------
* ExecNamedTuplestoreScan(node)
*
* Scans the CTE sequentially and returns the next qualifying tuple.
* We call the ExecScan() routine and pass it the appropriate
* access method functions.
* ----------------------------------------------------------------
*/
TupleTableSlot *
ExecNamedTuplestoreScan(NamedTuplestoreScanState *node)
{
return ExecScan(&node->ss,
(ExecScanAccessMtd) NamedTuplestoreScanNext,
(ExecScanRecheckMtd) NamedTuplestoreScanRecheck);
}
/* ----------------------------------------------------------------
* ExecInitNamedTuplestoreScan
* ----------------------------------------------------------------
*/
NamedTuplestoreScanState *
ExecInitNamedTuplestoreScan(NamedTuplestoreScan *node, EState *estate, int eflags)
{
NamedTuplestoreScanState *scanstate;
EphemeralNamedRelation enr;
/* check for unsupported flags */
Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
/*
* NamedTuplestoreScan should not have any children.
*/
Assert(outerPlan(node) == NULL);
Assert(innerPlan(node) == NULL);
/*
* create new NamedTuplestoreScanState for node
*/
scanstate = makeNode(NamedTuplestoreScanState);
scanstate->ss.ps.plan = (Plan *) node;
scanstate->ss.ps.state = estate;
enr = get_ENR(estate->es_queryEnv, node->enrname);
if (!enr)
elog(ERROR, "executor could not find named tuplestore \"%s\"",
node->enrname);
Assert(enr->reldata);
scanstate->relation = (Tuplestorestate *) enr->reldata;
scanstate->tupdesc = ENRMetadataGetTupDesc(&(enr->md));
scanstate->readptr =
tuplestore_alloc_read_pointer(scanstate->relation, 0);
/*
* The new read pointer copies its position from read pointer 0, which
* could be anywhere, so explicitly rewind it.
*/
tuplestore_rescan(scanstate->relation);
/*
* XXX: Should we add a function to free that read pointer when done?
* This was attempted, but it did not improve performance or memory usage
* in any tested cases.
*/
/*
* Miscellaneous initialization
*
* create expression context for node
*/
ExecAssignExprContext(estate, &scanstate->ss.ps);
/*
* initialize child expressions
*/
scanstate->ss.ps.qual =
ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
/*
* tuple table initialization
*/
ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
ExecInitScanTupleSlot(estate, &scanstate->ss);
/*
* The scan tuple type is specified for the tuplestore.
*/
ExecAssignScanType(&scanstate->ss, scanstate->tupdesc);
/*
* Initialize result tuple type and projection info.
*/
ExecAssignResultTypeFromTL(&scanstate->ss.ps);
ExecAssignScanProjectionInfo(&scanstate->ss);
return scanstate;
}
/* ----------------------------------------------------------------
* ExecEndNamedTuplestoreScan
*
* frees any storage allocated through C routines.
* ----------------------------------------------------------------
*/
void
ExecEndNamedTuplestoreScan(NamedTuplestoreScanState *node)
{
/*
* Free exprcontext
*/
ExecFreeExprContext(&node->ss.ps);
/*
* clean out the tuple table
*/
ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
ExecClearTuple(node->ss.ss_ScanTupleSlot);
}
/* ----------------------------------------------------------------
* ExecReScanNamedTuplestoreScan
*
* Rescans the relation.
* ----------------------------------------------------------------
*/
void
ExecReScanNamedTuplestoreScan(NamedTuplestoreScanState *node)
{
Tuplestorestate *tuplestorestate = node->relation;
ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
ExecScanReScan(&node->ss);
/*
* Rewind my own pointer.
*/
tuplestore_select_read_pointer(tuplestorestate, node->readptr);
tuplestore_rescan(tuplestorestate);
}

View File

@@ -122,6 +122,7 @@ SPI_connect(void)
_SPI_current->procCxt = NULL; /* in case we fail to create 'em */
_SPI_current->execCxt = NULL;
_SPI_current->connectSubid = GetCurrentSubTransactionId();
_SPI_current->queryEnv = NULL;
/*
* Create memory contexts for this procedure
@@ -1193,7 +1194,7 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
*/
/* Replan if needed, and increment plan refcount for portal */
cplan = GetCachedPlan(plansource, paramLI, false);
cplan = GetCachedPlan(plansource, paramLI, false, _SPI_current->queryEnv);
stmt_list = cplan->stmt_list;
/* Pop the error context stack */
@@ -1532,6 +1533,10 @@ SPI_result_code_string(int code)
return "SPI_ERROR_NOOUTFUNC";
case SPI_ERROR_TYPUNKNOWN:
return "SPI_ERROR_TYPUNKNOWN";
case SPI_ERROR_REL_DUPLICATE:
return "SPI_ERROR_REL_DUPLICATE";
case SPI_ERROR_REL_NOT_FOUND:
return "SPI_ERROR_REL_NOT_FOUND";
case SPI_OK_CONNECT:
return "SPI_OK_CONNECT";
case SPI_OK_FINISH:
@@ -1560,6 +1565,10 @@ SPI_result_code_string(int code)
return "SPI_OK_UPDATE_RETURNING";
case SPI_OK_REWRITTEN:
return "SPI_OK_REWRITTEN";
case SPI_OK_REL_REGISTER:
return "SPI_OK_REL_REGISTER";
case SPI_OK_REL_UNREGISTER:
return "SPI_OK_REL_UNREGISTER";
}
/* Unrecognized code ... return something useful ... */
sprintf(buf, "Unrecognized SPI code %d", code);
@@ -1615,7 +1624,8 @@ SPI_plan_get_cached_plan(SPIPlanPtr plan)
error_context_stack = &spierrcontext;
/* Get the generic plan for the query */
cplan = GetCachedPlan(plansource, NULL, plan->saved);
cplan = GetCachedPlan(plansource, NULL, plan->saved,
_SPI_current->queryEnv);
Assert(cplan == plansource->gplan);
/* Pop the error context stack */
@@ -1767,7 +1777,8 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan)
*/
plansource = CreateCachedPlan(parsetree,
src,
CreateCommandTag(parsetree->stmt));
CreateCommandTag(parsetree->stmt),
_SPI_current->queryEnv);
/*
* Parameter datatypes are driven by parserSetup hook if provided,
@@ -1779,14 +1790,16 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan)
stmt_list = pg_analyze_and_rewrite_params(parsetree,
src,
plan->parserSetup,
plan->parserSetupArg);
plan->parserSetupArg,
_SPI_current->queryEnv);
}
else
{
stmt_list = pg_analyze_and_rewrite(parsetree,
src,
plan->argtypes,
plan->nargs);
plan->nargs,
_SPI_current->queryEnv);
}
/* Finish filling in the CachedPlanSource */
@@ -1975,14 +1988,16 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
stmt_list = pg_analyze_and_rewrite_params(parsetree,
src,
plan->parserSetup,
plan->parserSetupArg);
plan->parserSetupArg,
_SPI_current->queryEnv);
}
else
{
stmt_list = pg_analyze_and_rewrite(parsetree,
src,
plan->argtypes,
plan->nargs);
plan->nargs,
_SPI_current->queryEnv);
}
/* Finish filling in the CachedPlanSource */
@@ -2001,7 +2016,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
* Replan if needed, and increment plan refcount. If it's a saved
* plan, the refcount must be backed by the CurrentResourceOwner.
*/
cplan = GetCachedPlan(plansource, paramLI, plan->saved);
cplan = GetCachedPlan(plansource, paramLI, plan->saved, _SPI_current->queryEnv);
stmt_list = cplan->stmt_list;
/*
@@ -2081,7 +2096,8 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
plansource->query_string,
snap, crosscheck_snapshot,
dest,
paramLI, 0);
paramLI, _SPI_current->queryEnv,
0);
res = _SPI_pquery(qdesc, fire_triggers,
canSetTag ? tcount : 0);
FreeQueryDesc(qdesc);
@@ -2094,6 +2110,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
plansource->query_string,
PROCESS_UTILITY_QUERY,
paramLI,
_SPI_current->queryEnv,
dest,
completionTag);
@@ -2619,3 +2636,84 @@ _SPI_save_plan(SPIPlanPtr plan)
return newplan;
}
/*
* Internal lookup of ephemeral named relation by name.
*/
static EphemeralNamedRelation
_SPI_find_ENR_by_name(const char *name)
{
/* internal static function; any error is bug in SPI itself */
Assert(name != NULL);
/* fast exit if no tuplestores have been added */
if (_SPI_current->queryEnv == NULL)
return NULL;
return get_ENR(_SPI_current->queryEnv, name);
}
/*
* Register an ephemeral named relation for use by the planner and executor on
* subsequent calls using this SPI connection.
*/
int
SPI_register_relation(EphemeralNamedRelation enr)
{
EphemeralNamedRelation match;
int res;
if (enr == NULL || enr->md.name == NULL)
return SPI_ERROR_ARGUMENT;
res = _SPI_begin_call(false); /* keep current memory context */
if (res < 0)
return res;
match = _SPI_find_ENR_by_name(enr->md.name);
if (match)
res = SPI_ERROR_REL_DUPLICATE;
else
{
if (_SPI_current->queryEnv == NULL)
_SPI_current->queryEnv = create_queryEnv();
register_ENR(_SPI_current->queryEnv, enr);
res = SPI_OK_REL_REGISTER;
}
_SPI_end_call(false);
return res;
}
/*
* Unregister an ephemeral named relation by name. This will probably be a
* rarely used function, since SPI_finish will clear it automatically.
*/
int
SPI_unregister_relation(const char *name)
{
EphemeralNamedRelation match;
int res;
if (name == NULL)
return SPI_ERROR_ARGUMENT;
res = _SPI_begin_call(false); /* keep current memory context */
if (res < 0)
return res;
match = _SPI_find_ENR_by_name(name);
if (match)
{
unregister_ENR(_SPI_current->queryEnv, match->md.name);
res = SPI_OK_REL_UNREGISTER;
}
else
res = SPI_ERROR_REL_NOT_FOUND;
_SPI_end_call(false);
return res;
}

View File

@@ -682,6 +682,27 @@ _copyCteScan(const CteScan *from)
return newnode;
}
/*
* _copyNamedTuplestoreScan
*/
static NamedTuplestoreScan *
_copyNamedTuplestoreScan(const NamedTuplestoreScan *from)
{
NamedTuplestoreScan *newnode = makeNode(NamedTuplestoreScan);
/*
* copy node superclass fields
*/
CopyScanFields((const Scan *) from, (Scan *) newnode);
/*
* copy remainder of node
*/
COPY_STRING_FIELD(enrname);
return newnode;
}
/*
* _copyWorkTableScan
*/
@@ -2265,6 +2286,7 @@ _copyRangeTblEntry(const RangeTblEntry *from)
COPY_STRING_FIELD(ctename);
COPY_SCALAR_FIELD(ctelevelsup);
COPY_SCALAR_FIELD(self_reference);
COPY_STRING_FIELD(enrname);
COPY_NODE_FIELD(coltypes);
COPY_NODE_FIELD(coltypmods);
COPY_NODE_FIELD(colcollations);
@@ -4706,6 +4728,9 @@ copyObjectImpl(const void *from)
case T_CteScan:
retval = _copyCteScan(from);
break;
case T_NamedTuplestoreScan:
retval = _copyNamedTuplestoreScan(from);
break;
case T_WorkTableScan:
retval = _copyWorkTableScan(from);
break;

View File

@@ -2321,6 +2321,7 @@ range_table_walker(List *rtable,
return true;
break;
case RTE_CTE:
case RTE_NAMEDTUPLESTORE:
/* nothing to do */
break;
case RTE_SUBQUERY:
@@ -3135,6 +3136,7 @@ range_table_mutator(List *rtable,
/* we don't bother to copy eref, aliases, etc; OK? */
break;
case RTE_CTE:
case RTE_NAMEDTUPLESTORE:
/* nothing to do */
break;
case RTE_SUBQUERY:

View File

@@ -631,6 +631,16 @@ _outCteScan(StringInfo str, const CteScan *node)
WRITE_INT_FIELD(cteParam);
}
static void
_outNamedTuplestoreScan(StringInfo str, const NamedTuplestoreScan *node)
{
WRITE_NODE_TYPE("NAMEDTUPLESTORESCAN");
_outScanInfo(str, (const Scan *) node);
WRITE_STRING_FIELD(enrname);
}
static void
_outWorkTableScan(StringInfo str, const WorkTableScan *node)
{
@@ -3024,6 +3034,13 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
WRITE_NODE_FIELD(coltypmods);
WRITE_NODE_FIELD(colcollations);
break;
case RTE_NAMEDTUPLESTORE:
WRITE_STRING_FIELD(enrname);
WRITE_OID_FIELD(relid);
WRITE_NODE_FIELD(coltypes);
WRITE_NODE_FIELD(coltypmods);
WRITE_NODE_FIELD(colcollations);
break;
default:
elog(ERROR, "unrecognized RTE kind: %d", (int) node->rtekind);
break;
@@ -3621,6 +3638,9 @@ outNode(StringInfo str, const void *obj)
case T_CteScan:
_outCteScan(str, obj);
break;
case T_NamedTuplestoreScan:
_outNamedTuplestoreScan(str, obj);
break;
case T_WorkTableScan:
_outWorkTableScan(str, obj);
break;

View File

@@ -291,6 +291,10 @@ print_rt(const List *rtable)
printf("%d\t%s\t[cte]",
i, rte->eref->aliasname);
break;
case RTE_NAMEDTUPLESTORE:
printf("%d\t%s\t[tuplestore]",
i, rte->eref->aliasname);
break;
default:
printf("%d\t%s\t[unknown rtekind]",
i, rte->eref->aliasname);

View File

@@ -1355,6 +1355,13 @@ _readRangeTblEntry(void)
READ_NODE_FIELD(coltypmods);
READ_NODE_FIELD(colcollations);
break;
case RTE_NAMEDTUPLESTORE:
READ_STRING_FIELD(enrname);
READ_OID_FIELD(relid);
READ_NODE_FIELD(coltypes);
READ_NODE_FIELD(coltypmods);
READ_NODE_FIELD(colcollations);
break;
default:
elog(ERROR, "unrecognized RTE kind: %d",
(int) local_node->rtekind);

View File

@@ -111,6 +111,8 @@ static void set_tablefunc_pathlist(PlannerInfo *root, RelOptInfo *rel,
RangeTblEntry *rte);
static void set_cte_pathlist(PlannerInfo *root, RelOptInfo *rel,
RangeTblEntry *rte);
static void set_namedtuplestore_pathlist(PlannerInfo *root, RelOptInfo *rel,
RangeTblEntry *rte);
static void set_worktable_pathlist(PlannerInfo *root, RelOptInfo *rel,
RangeTblEntry *rte);
static RelOptInfo *make_rel_from_joinlist(PlannerInfo *root, List *joinlist);
@@ -396,6 +398,9 @@ set_rel_size(PlannerInfo *root, RelOptInfo *rel,
else
set_cte_pathlist(root, rel, rte);
break;
case RTE_NAMEDTUPLESTORE:
set_namedtuplestore_pathlist(root, rel, rte);
break;
default:
elog(ERROR, "unexpected rtekind: %d", (int) rel->rtekind);
break;
@@ -464,6 +469,9 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
case RTE_CTE:
/* CTE reference --- fully handled during set_rel_size */
break;
case RTE_NAMEDTUPLESTORE:
/* tuplestore reference --- fully handled during set_rel_size */
break;
default:
elog(ERROR, "unexpected rtekind: %d", (int) rel->rtekind);
break;
@@ -639,6 +647,13 @@ set_rel_consider_parallel(PlannerInfo *root, RelOptInfo *rel,
* executed only once.
*/
return;
case RTE_NAMEDTUPLESTORE:
/*
* tuplestore cannot be shared, at least without more
* infrastructure to support that.
*/
return;
}
/*
@@ -2089,6 +2104,36 @@ set_cte_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
add_path(rel, create_ctescan_path(root, rel, required_outer));
}
/*
* set_namedtuplestore_pathlist
* Build the (single) access path for a named tuplestore RTE
*
* There's no need for a separate set_namedtuplestore_size phase, since we
* don't support join-qual-parameterized paths for tuplestores.
*/
static void
set_namedtuplestore_pathlist(PlannerInfo *root, RelOptInfo *rel,
RangeTblEntry *rte)
{
Relids required_outer;
/* Mark rel with estimated output rows, width, etc */
set_namedtuplestore_size_estimates(root, rel);
/*
* We don't support pushing join clauses into the quals of a tuplestore
* scan, but it could still have required parameterization due to LATERAL
* refs in its tlist.
*/
required_outer = rel->lateral_relids;
/* Generate appropriate path */
add_path(rel, create_namedtuplestorescan_path(root, rel, required_outer));
/* Select cheapest path (pretty easy in this case...) */
set_cheapest(rel);
}
/*
* set_worktable_pathlist
* Build the (single) access path for a self-reference CTE RTE

View File

@@ -1516,6 +1516,43 @@ cost_ctescan(Path *path, PlannerInfo *root,
path->total_cost = startup_cost + run_cost;
}
/*
* cost_namedtuplestorescan
* Determines and returns the cost of scanning a named tuplestore.
*/
void
cost_namedtuplestorescan(Path *path, PlannerInfo *root,
RelOptInfo *baserel, ParamPathInfo *param_info)
{
Cost startup_cost = 0;
Cost run_cost = 0;
QualCost qpqual_cost;
Cost cpu_per_tuple;
/* Should only be applied to base relations that are Tuplestores */
Assert(baserel->relid > 0);
Assert(baserel->rtekind == RTE_NAMEDTUPLESTORE);
/* Mark the path with the correct row estimate */
if (param_info)
path->rows = param_info->ppi_rows;
else
path->rows = baserel->rows;
/* Charge one CPU tuple cost per row for tuplestore manipulation */
cpu_per_tuple = cpu_tuple_cost;
/* Add scanning CPU costs */
get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost);
startup_cost += qpqual_cost.startup;
cpu_per_tuple += cpu_tuple_cost + qpqual_cost.per_tuple;
run_cost += cpu_per_tuple * baserel->tuples;
path->startup_cost = startup_cost;
path->total_cost = startup_cost + run_cost;
}
/*
* cost_recursive_union
* Determines and returns the cost of performing a recursive union,
@@ -4684,6 +4721,39 @@ set_cte_size_estimates(PlannerInfo *root, RelOptInfo *rel, double cte_rows)
set_baserel_size_estimates(root, rel);
}
/*
* set_namedtuplestore_size_estimates
* Set the size estimates for a base relation that is a tuplestore reference.
*
* The rel's targetlist and restrictinfo list must have been constructed
* already.
*
* We set the same fields as set_baserel_size_estimates.
*/
void
set_namedtuplestore_size_estimates(PlannerInfo *root, RelOptInfo *rel)
{
RangeTblEntry *rte;
/* Should only be applied to base relations that are tuplestore references */
Assert(rel->relid > 0);
rte = planner_rt_fetch(rel->relid, root);
Assert(rte->rtekind == RTE_NAMEDTUPLESTORE);
/*
* Use the estimate provided by the code which is generating the named
* tuplestore. In some cases, the actual number might be available; in
* others the same plan will be re-used, so a "typical" value might be
* estimated and used.
*/
rel->tuples = rte->enrtuples;
if (rel->tuples < 0)
rel->tuples = 1000;
/* Now estimate number of output rows, etc */
set_baserel_size_estimates(root, rel);
}
/*
* set_foreign_size_estimates
* Set the size estimates for a base relation that is a foreign table.

View File

@@ -139,6 +139,8 @@ static TableFuncScan *create_tablefuncscan_plan(PlannerInfo *root, Path *best_pa
List *tlist, List *scan_clauses);
static CteScan *create_ctescan_plan(PlannerInfo *root, Path *best_path,
List *tlist, List *scan_clauses);
static NamedTuplestoreScan *create_namedtuplestorescan_plan(PlannerInfo *root,
Path *best_path, List *tlist, List *scan_clauses);
static WorkTableScan *create_worktablescan_plan(PlannerInfo *root, Path *best_path,
List *tlist, List *scan_clauses);
static ForeignScan *create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
@@ -197,6 +199,8 @@ 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 NamedTuplestoreScan *make_namedtuplestorescan(List *qptlist, List *qpqual,
Index scanrelid, char *enrname);
static WorkTableScan *make_worktablescan(List *qptlist, List *qpqual,
Index scanrelid, int wtParam);
static Append *make_append(List *appendplans, List *tlist, List *partitioned_rels);
@@ -366,6 +370,7 @@ create_plan_recurse(PlannerInfo *root, Path *best_path, int flags)
case T_ValuesScan:
case T_CteScan:
case T_WorkTableScan:
case T_NamedTuplestoreScan:
case T_ForeignScan:
case T_CustomScan:
plan = create_scan_plan(root, best_path, flags);
@@ -668,6 +673,13 @@ create_scan_plan(PlannerInfo *root, Path *best_path, int flags)
scan_clauses);
break;
case T_NamedTuplestoreScan:
plan = (Plan *) create_namedtuplestorescan_plan(root,
best_path,
tlist,
scan_clauses);
break;
case T_WorkTableScan:
plan = (Plan *) create_worktablescan_plan(root,
best_path,
@@ -3285,6 +3297,45 @@ create_ctescan_plan(PlannerInfo *root, Path *best_path,
return scan_plan;
}
/*
* create_namedtuplestorescan_plan
* Returns a tuplestorescan plan for the base relation scanned by
* 'best_path' with restriction clauses 'scan_clauses' and targetlist
* 'tlist'.
*/
static NamedTuplestoreScan *
create_namedtuplestorescan_plan(PlannerInfo *root, Path *best_path,
List *tlist, List *scan_clauses)
{
NamedTuplestoreScan *scan_plan;
Index scan_relid = best_path->parent->relid;
RangeTblEntry *rte;
Assert(scan_relid > 0);
rte = planner_rt_fetch(scan_relid, root);
Assert(rte->rtekind == RTE_NAMEDTUPLESTORE);
/* 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);
}
scan_plan = make_namedtuplestorescan(tlist, scan_clauses, scan_relid,
rte->enrname);
copy_generic_path_info(&scan_plan->scan.plan, best_path);
return scan_plan;
}
/*
* create_worktablescan_plan
* Returns a worktablescan plan for the base relation scanned by 'best_path'
@@ -5120,6 +5171,26 @@ make_ctescan(List *qptlist,
return node;
}
static NamedTuplestoreScan *
make_namedtuplestorescan(List *qptlist,
List *qpqual,
Index scanrelid,
char *enrname)
{
NamedTuplestoreScan *node = makeNode(NamedTuplestoreScan);
Plan *plan = &node->scan.plan;
/* cost should be inserted by caller */
plan->targetlist = qptlist;
plan->qual = qpqual;
plan->lefttree = NULL;
plan->righttree = NULL;
node->scan.scanrelid = scanrelid;
node->enrname = enrname;
return node;
}
static WorkTableScan *
make_worktablescan(List *qptlist,
List *qpqual,

View File

@@ -591,6 +591,17 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
fix_scan_list(root, splan->scan.plan.qual, rtoffset);
}
break;
case T_NamedTuplestoreScan:
{
NamedTuplestoreScan *splan = (NamedTuplestoreScan *) plan;
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist, rtoffset);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual, rtoffset);
}
break;
case T_WorkTableScan:
{
WorkTableScan *splan = (WorkTableScan *) plan;
@@ -2571,6 +2582,11 @@ extract_query_dependencies_walker(Node *node, PlannerInfo *context)
if (rte->rtekind == RTE_RELATION)
context->glob->relationOids =
lappend_oid(context->glob->relationOids, rte->relid);
else if (rte->rtekind == RTE_NAMEDTUPLESTORE &&
OidIsValid(rte->relid))
context->glob->relationOids =
lappend_oid(context->glob->relationOids,
rte->relid);
}
/* And recurse into the query's subexpressions */

View File

@@ -2476,6 +2476,10 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
context.paramids = bms_add_members(context.paramids, scan_params);
break;
case T_NamedTuplestoreScan:
context.paramids = bms_add_members(context.paramids, scan_params);
break;
case T_ForeignScan:
{
ForeignScan *fscan = (ForeignScan *) plan;

View File

@@ -1123,6 +1123,7 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
break;
case RTE_JOIN:
case RTE_CTE:
case RTE_NAMEDTUPLESTORE:
/* these can't contain any lateral references */
break;
}
@@ -1977,6 +1978,7 @@ replace_vars_in_jointree(Node *jtnode,
break;
case RTE_JOIN:
case RTE_CTE:
case RTE_NAMEDTUPLESTORE:
/* these shouldn't be marked LATERAL */
Assert(false);
break;

View File

@@ -4910,7 +4910,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
querytree_list = pg_analyze_and_rewrite_params(linitial(raw_parsetree_list),
src,
(ParserSetupHook) sql_fn_parser_setup,
pinfo);
pinfo, NULL);
if (list_length(querytree_list) != 1)
goto fail;
querytree = linitial(querytree_list);

View File

@@ -1892,6 +1892,32 @@ create_ctescan_path(PlannerInfo *root, RelOptInfo *rel, Relids required_outer)
return pathnode;
}
/*
* create_namedtuplestorescan_path
* Creates a path corresponding to a scan of a named tuplestore, returning
* the pathnode.
*/
Path *
create_namedtuplestorescan_path(PlannerInfo *root, RelOptInfo *rel,
Relids required_outer)
{
Path *pathnode = makeNode(Path);
pathnode->pathtype = T_NamedTuplestoreScan;
pathnode->parent = rel;
pathnode->pathtarget = rel->reltarget;
pathnode->param_info = get_baserel_parampathinfo(root, rel,
required_outer);
pathnode->parallel_aware = false;
pathnode->parallel_safe = rel->consider_parallel;
pathnode->parallel_workers = 0;
pathnode->pathkeys = NIL; /* result is always unordered */
cost_namedtuplestorescan(pathnode, root, rel, pathnode->param_info);
return pathnode;
}
/*
* create_worktablescan_path
* Creates a path corresponding to a scan of a self-reference CTE,

View File

@@ -1446,9 +1446,9 @@ relation_excluded_by_constraints(PlannerInfo *root,
* dropped cols.
*
* We also support building a "physical" tlist for subqueries, functions,
* values lists, table expressions and CTEs, since the same optimization can
* occur in SubqueryScan, FunctionScan, ValuesScan, CteScan, TableFunc
* and WorkTableScan nodes.
* values lists, table expressions, and CTEs, since the same optimization can
* occur in SubqueryScan, FunctionScan, ValuesScan, CteScan, TableFunc,
* NamedTuplestoreScan, and WorkTableScan nodes.
*/
List *
build_physical_tlist(PlannerInfo *root, RelOptInfo *rel)
@@ -1523,6 +1523,7 @@ build_physical_tlist(PlannerInfo *root, RelOptInfo *rel)
case RTE_TABLEFUNC:
case RTE_VALUES:
case RTE_CTE:
case RTE_NAMEDTUPLESTORE:
/* Not all of these can have dropped cols, but share code anyway */
expandRTE(rte, varno, 0, -1, true /* include dropped */ ,
NULL, &colvars);

View File

@@ -156,6 +156,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
case RTE_TABLEFUNC:
case RTE_VALUES:
case RTE_CTE:
case RTE_NAMEDTUPLESTORE:
/*
* Subquery, function, tablefunc, or values list --- set up attr

View File

@@ -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

View File

@@ -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);

View File

@@ -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);
@@ -434,6 +445,20 @@ transformCTEReference(ParseState *pstate, RangeVar *r,
return rte;
}
/*
* 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
*/
@@ -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;
rte = getRTEForSpecialRelationTypes(pstate, rv);
cte = scanNameSpaceForCTE(pstate, rv->relname, &levelsup);
if (cte)
rte = transformCTEReference(pstate, rv, cte, levelsup);
}
/* 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);

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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"
@@ -281,6 +282,16 @@ isFutureCTE(ParseState *pstate, const char *refname)
return false;
}
/*
* 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.
@@ -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;
}
@@ -1138,13 +1159,18 @@ parserOpenTable(ParseState *pstate, const RangeVar *relation, int lockmode)
relation->schemaname, relation->relname)));
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:
{
/*

View File

@@ -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

View File

@@ -642,7 +642,8 @@ pg_parse_query(const char *query_string)
*/
List *
pg_analyze_and_rewrite(RawStmt *parsetree, const char *query_string,
Oid *paramTypes, int numParams)
Oid *paramTypes, int numParams,
QueryEnvironment *queryEnv)
{
Query *query;
List *querytree_list;
@@ -655,7 +656,8 @@ pg_analyze_and_rewrite(RawStmt *parsetree, const char *query_string,
if (log_parser_stats)
ResetUsage();
query = parse_analyze(parsetree, query_string, paramTypes, numParams);
query = parse_analyze(parsetree, query_string, paramTypes, numParams,
queryEnv);
if (log_parser_stats)
ShowUsage("PARSE ANALYSIS STATISTICS");
@@ -679,7 +681,8 @@ List *
pg_analyze_and_rewrite_params(RawStmt *parsetree,
const char *query_string,
ParserSetupHook parserSetup,
void *parserSetupArg)
void *parserSetupArg,
QueryEnvironment *queryEnv)
{
ParseState *pstate;
Query *query;
@@ -697,6 +700,7 @@ pg_analyze_and_rewrite_params(RawStmt *parsetree,
pstate = make_parsestate(NULL);
pstate->p_sourcetext = query_string;
pstate->p_queryEnv = queryEnv;
(*parserSetup) (pstate, parserSetupArg);
query = transformTopLevelStmt(pstate, parsetree);
@@ -1024,7 +1028,7 @@ exec_simple_query(const char *query_string)
oldcontext = MemoryContextSwitchTo(MessageContext);
querytree_list = pg_analyze_and_rewrite(parsetree, query_string,
NULL, 0);
NULL, 0, NULL);
plantree_list = pg_plan_queries(querytree_list,
CURSOR_OPT_PARALLEL_OK, NULL);
@@ -1314,7 +1318,8 @@ exec_parse_message(const char *query_string, /* string to execute */
* Create the CachedPlanSource before we do parse analysis, since it
* needs to see the unmodified raw parse tree.
*/
psrc = CreateCachedPlan(raw_parse_tree, query_string, commandTag);
psrc = CreateCachedPlan(raw_parse_tree, query_string, commandTag,
NULL);
/*
* Set up a snapshot if parse analysis will need one.
@@ -1366,7 +1371,8 @@ exec_parse_message(const char *query_string, /* string to execute */
/* Empty input string. This is legal. */
raw_parse_tree = NULL;
commandTag = NULL;
psrc = CreateCachedPlan(raw_parse_tree, query_string, commandTag);
psrc = CreateCachedPlan(raw_parse_tree, query_string, commandTag,
NULL);
querytree_list = NIL;
}
@@ -1769,7 +1775,7 @@ exec_bind_message(StringInfo input_message)
* will be generated in MessageContext. The plan refcount will be
* assigned to the Portal, so it will be released at portal destruction.
*/
cplan = GetCachedPlan(psrc, params, false);
cplan = GetCachedPlan(psrc, params, false, NULL);
/*
* Now we can define the portal.
@@ -2367,7 +2373,7 @@ exec_describe_statement_message(const char *stmt_name)
List *tlist;
/* Get the plan's primary targetlist */
tlist = CachedPlanGetTargetList(psrc);
tlist = CachedPlanGetTargetList(psrc, NULL);
SendRowDescriptionMessage(psrc->resultDesc, tlist, NULL);
}

View File

@@ -38,6 +38,7 @@ Portal ActivePortal = NULL;
static void ProcessQuery(PlannedStmt *plan,
const char *sourceText,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
char *completionTag);
static void FillPortalStore(Portal portal, bool isTopLevel);
@@ -69,6 +70,7 @@ CreateQueryDesc(PlannedStmt *plannedstmt,
Snapshot crosscheck_snapshot,
DestReceiver *dest,
ParamListInfo params,
QueryEnvironment *queryEnv,
int instrument_options)
{
QueryDesc *qd = (QueryDesc *) palloc(sizeof(QueryDesc));
@@ -81,6 +83,7 @@ CreateQueryDesc(PlannedStmt *plannedstmt,
qd->crosscheck_snapshot = RegisterSnapshot(crosscheck_snapshot);
qd->dest = dest; /* output dest */
qd->params = params; /* parameter values passed into query */
qd->queryEnv = queryEnv;
qd->instrument_options = instrument_options; /* instrumentation
* wanted? */
@@ -135,6 +138,7 @@ static void
ProcessQuery(PlannedStmt *plan,
const char *sourceText,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
char *completionTag)
{
@@ -145,7 +149,7 @@ ProcessQuery(PlannedStmt *plan,
*/
queryDesc = CreateQueryDesc(plan, sourceText,
GetActiveSnapshot(), InvalidSnapshot,
dest, params, 0);
dest, params, queryEnv, 0);
/*
* Call ExecutorStart to prepare the plan for execution
@@ -498,6 +502,7 @@ PortalStart(Portal portal, ParamListInfo params,
InvalidSnapshot,
None_Receiver,
params,
portal->queryEnv,
0);
/*
@@ -1175,6 +1180,7 @@ PortalRunUtility(Portal portal, PlannedStmt *pstmt,
portal->sourceText,
isTopLevel ? PROCESS_UTILITY_TOPLEVEL : PROCESS_UTILITY_QUERY,
portal->portalParams,
portal->queryEnv,
dest,
completionTag);
@@ -1281,6 +1287,7 @@ PortalRunMulti(Portal portal,
ProcessQuery(pstmt,
portal->sourceText,
portal->portalParams,
portal->queryEnv,
dest, completionTag);
}
else
@@ -1289,6 +1296,7 @@ PortalRunMulti(Portal portal,
ProcessQuery(pstmt,
portal->sourceText,
portal->portalParams,
portal->queryEnv,
altdest, NULL);
}

View File

@@ -78,6 +78,7 @@ static void ProcessUtilitySlow(ParseState *pstate,
const char *queryString,
ProcessUtilityContext context,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
char *completionTag);
static void ExecDropStmt(DropStmt *stmt, bool isTopLevel);
@@ -333,6 +334,7 @@ ProcessUtility(PlannedStmt *pstmt,
const char *queryString,
ProcessUtilityContext context,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
char *completionTag)
{
@@ -347,11 +349,11 @@ ProcessUtility(PlannedStmt *pstmt,
*/
if (ProcessUtility_hook)
(*ProcessUtility_hook) (pstmt, queryString,
context, params,
context, params, queryEnv,
dest, completionTag);
else
standard_ProcessUtility(pstmt, queryString,
context, params,
context, params, queryEnv,
dest, completionTag);
}
@@ -371,6 +373,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
const char *queryString,
ProcessUtilityContext context,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
char *completionTag)
{
@@ -672,7 +675,8 @@ standard_ProcessUtility(PlannedStmt *pstmt,
break;
case T_ExplainStmt:
ExplainQuery(pstate, (ExplainStmt *) parsetree, queryString, params, dest);
ExplainQuery(pstate, (ExplainStmt *) parsetree, queryString, params,
queryEnv, dest);
break;
case T_AlterSystemStmt:
@@ -819,7 +823,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsGrantObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params,
context, params, queryEnv,
dest, completionTag);
else
ExecuteGrantStmt(stmt);
@@ -832,7 +836,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->removeType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params,
context, params, queryEnv,
dest, completionTag);
else
ExecDropStmt(stmt, isTopLevel);
@@ -845,7 +849,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->renameType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params,
context, params, queryEnv,
dest, completionTag);
else
ExecRenameStmt(stmt);
@@ -858,7 +862,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params,
context, params, queryEnv,
dest, completionTag);
else
ExecAlterObjectDependsStmt(stmt, NULL);
@@ -871,7 +875,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params,
context, params, queryEnv,
dest, completionTag);
else
ExecAlterObjectSchemaStmt(stmt, NULL);
@@ -884,7 +888,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params,
context, params, queryEnv,
dest, completionTag);
else
ExecAlterOwnerStmt(stmt);
@@ -897,7 +901,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params,
context, params, queryEnv,
dest, completionTag);
else
CommentObject(stmt);
@@ -910,7 +914,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params,
context, params, queryEnv,
dest, completionTag);
else
ExecSecLabelStmt(stmt);
@@ -920,7 +924,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
default:
/* All other statement types have event trigger support */
ProcessUtilitySlow(pstate, pstmt, queryString,
context, params,
context, params, queryEnv,
dest, completionTag);
break;
}
@@ -939,6 +943,7 @@ ProcessUtilitySlow(ParseState *pstate,
const char *queryString,
ProcessUtilityContext context,
ParamListInfo params,
QueryEnvironment *queryEnv,
DestReceiver *dest,
char *completionTag)
{
@@ -1062,6 +1067,7 @@ ProcessUtilitySlow(ParseState *pstate,
queryString,
PROCESS_UTILITY_SUBCOMMAND,
params,
NULL,
None_Receiver,
NULL);
}
@@ -1140,6 +1146,7 @@ ProcessUtilitySlow(ParseState *pstate,
queryString,
PROCESS_UTILITY_SUBCOMMAND,
params,
NULL,
None_Receiver,
NULL);
EventTriggerAlterTableStart(parsetree);
@@ -1438,7 +1445,8 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateTableAsStmt:
address = ExecCreateTableAs((CreateTableAsStmt *) parsetree,
queryString, params, completionTag);
queryString, params, queryEnv,
completionTag);
break;
case T_RefreshMatViewStmt:

View File

@@ -6710,6 +6710,7 @@ get_name_for_var_field(Var *var, int fieldno,
{
case RTE_RELATION:
case RTE_VALUES:
case RTE_NAMEDTUPLESTORE:
/*
* This case should not occur: a column of a table or values list

View File

@@ -88,10 +88,11 @@
static CachedPlanSource *first_saved_plan = NULL;
static void ReleaseGenericPlan(CachedPlanSource *plansource);
static List *RevalidateCachedQuery(CachedPlanSource *plansource);
static List *RevalidateCachedQuery(CachedPlanSource *plansource,
QueryEnvironment *queryEnv);
static bool CheckCachedPlan(CachedPlanSource *plansource);
static CachedPlan *BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
ParamListInfo boundParams);
ParamListInfo boundParams, QueryEnvironment *queryEnv);
static bool choose_custom_plan(CachedPlanSource *plansource,
ParamListInfo boundParams);
static double cached_plan_cost(CachedPlan *plan, bool include_planner);
@@ -150,7 +151,8 @@ InitPlanCache(void)
CachedPlanSource *
CreateCachedPlan(RawStmt *raw_parse_tree,
const char *query_string,
const char *commandTag)
const char *commandTag,
QueryEnvironment *queryEnv)
{
CachedPlanSource *plansource;
MemoryContext source_context;
@@ -553,7 +555,8 @@ ReleaseGenericPlan(CachedPlanSource *plansource)
* a tree copying step in a subsequent BuildCachedPlan call.)
*/
static List *
RevalidateCachedQuery(CachedPlanSource *plansource)
RevalidateCachedQuery(CachedPlanSource *plansource,
QueryEnvironment *queryEnv)
{
bool snapshot_set;
RawStmt *rawtree;
@@ -685,12 +688,14 @@ RevalidateCachedQuery(CachedPlanSource *plansource)
tlist = pg_analyze_and_rewrite_params(rawtree,
plansource->query_string,
plansource->parserSetup,
plansource->parserSetupArg);
plansource->parserSetupArg,
queryEnv);
else
tlist = pg_analyze_and_rewrite(rawtree,
plansource->query_string,
plansource->param_types,
plansource->num_params);
plansource->num_params,
queryEnv);
/* Release snapshot if we got one */
if (snapshot_set)
@@ -875,7 +880,7 @@ CheckCachedPlan(CachedPlanSource *plansource)
*/
static CachedPlan *
BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
ParamListInfo boundParams)
ParamListInfo boundParams, QueryEnvironment *queryEnv)
{
CachedPlan *plan;
List *plist;
@@ -899,7 +904,7 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
* safety, let's treat it as real and redo the RevalidateCachedQuery call.
*/
if (!plansource->is_valid)
qlist = RevalidateCachedQuery(plansource);
qlist = RevalidateCachedQuery(plansource, queryEnv);
/*
* If we don't already have a copy of the querytree list that can be
@@ -1129,7 +1134,7 @@ cached_plan_cost(CachedPlan *plan, bool include_planner)
*/
CachedPlan *
GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams,
bool useResOwner)
bool useResOwner, QueryEnvironment *queryEnv)
{
CachedPlan *plan = NULL;
List *qlist;
@@ -1143,7 +1148,7 @@ GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams,
elog(ERROR, "cannot apply ResourceOwner to non-saved cached plan");
/* Make sure the querytree list is valid and we have parse-time locks */
qlist = RevalidateCachedQuery(plansource);
qlist = RevalidateCachedQuery(plansource, queryEnv);
/* Decide whether to use a custom plan */
customplan = choose_custom_plan(plansource, boundParams);
@@ -1159,7 +1164,7 @@ GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams,
else
{
/* Build a new generic plan */
plan = BuildCachedPlan(plansource, qlist, NULL);
plan = BuildCachedPlan(plansource, qlist, NULL, queryEnv);
/* Just make real sure plansource->gplan is clear */
ReleaseGenericPlan(plansource);
/* Link the new generic plan into the plansource */
@@ -1204,7 +1209,7 @@ GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams,
if (customplan)
{
/* Build a custom plan */
plan = BuildCachedPlan(plansource, qlist, boundParams);
plan = BuildCachedPlan(plansource, qlist, boundParams, queryEnv);
/* Accumulate total costs of custom plans, but 'ware overflow */
if (plansource->num_custom_plans < INT_MAX)
{
@@ -1418,7 +1423,8 @@ CachedPlanIsValid(CachedPlanSource *plansource)
* within the cached plan, and may disappear next time the plan is updated.
*/
List *
CachedPlanGetTargetList(CachedPlanSource *plansource)
CachedPlanGetTargetList(CachedPlanSource *plansource,
QueryEnvironment *queryEnv)
{
Query *pstmt;
@@ -1434,7 +1440,7 @@ CachedPlanGetTargetList(CachedPlanSource *plansource)
return NIL;
/* Make sure the querytree list is valid and we have parse-time locks */
RevalidateCachedQuery(plansource);
RevalidateCachedQuery(plansource, queryEnv);
/* Get the primary statement and find out what it returns */
pstmt = QueryListGetPrimaryStmt(plansource->query_list);

View File

@@ -15,8 +15,8 @@ include $(top_builddir)/src/Makefile.global
override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
OBJS = backend_random.o guc.o help_config.o pg_config.o pg_controldata.o \
pg_rusage.o ps_status.o rls.o sampling.o superuser.o timeout.o \
tzparser.o
pg_rusage.o ps_status.o queryenvironment.o rls.o sampling.o \
superuser.o timeout.o tzparser.o
# This location might depend on the installation directories. Therefore
# we can't substitute it into pg_config.h.

View File

@@ -0,0 +1,144 @@
/*-------------------------------------------------------------------------
*
* queryenvironment.c
* Query environment, to store context-specific values like ephemeral named
* relations. Initial use is for named tuplestores for delta information
* from "normal" relations.
*
* The initial implementation uses a list because the number of such relations
* in any one context is expected to be very small. If that becomes a
* performance problem, the implementation can be changed with no other impact
* on callers, since this is an opaque structure. This is the reason to
* require a create function.
*
* Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/backend/backend/utils/misc/queryenvironment.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/heapam.h"
#include "utils/queryenvironment.h"
#include "utils/rel.h"
/*
* Private state of a query environment.
*/
struct QueryEnvironment
{
List *namedRelList;
};
QueryEnvironment *
create_queryEnv()
{
return (QueryEnvironment *) palloc0(sizeof(QueryEnvironment));
}
EphemeralNamedRelationMetadata
get_visible_ENR_metadata(QueryEnvironment *queryEnv, const char *refname)
{
EphemeralNamedRelation enr;
Assert(refname != NULL);
if (queryEnv == NULL)
return NULL;
enr = get_ENR(queryEnv, refname);
if (enr)
return &(enr->md);
return NULL;
}
/*
* Register a named relation for use in the given environment.
*
* If this is intended exclusively for planning purposes, the tstate field can
* be left NULL;
*/
void
register_ENR(QueryEnvironment *queryEnv, EphemeralNamedRelation enr)
{
Assert(enr != NULL);
Assert(get_ENR(queryEnv, enr->md.name) == NULL);
queryEnv->namedRelList = lappend(queryEnv->namedRelList, enr);
}
/*
* Unregister an ephemeral relation by name. This will probably be a rarely
* used function, but seems like it should be provided "just in case".
*/
void
unregister_ENR(QueryEnvironment *queryEnv, const char *name)
{
EphemeralNamedRelation match;
match = get_ENR(queryEnv, name);
if (match)
queryEnv->namedRelList = list_delete(queryEnv->namedRelList, match);
}
/*
* This returns an ENR if there is a name match in the given collection. It
* must quietly return NULL if no match is found.
*/
EphemeralNamedRelation
get_ENR(QueryEnvironment *queryEnv, const char *name)
{
ListCell *lc;
Assert(name != NULL);
if (queryEnv == NULL)
return NULL;
foreach(lc, queryEnv->namedRelList)
{
EphemeralNamedRelation enr = (EphemeralNamedRelation) lfirst(lc);
if (strcmp(enr->md.name, name) == 0)
return enr;
}
return NULL;
}
/*
* Gets the TupleDesc for a Ephemeral Named Relation, based on which field was
* filled.
*
* When the TupleDesc is based on a relation from the catalogs, we count on
* that relation being used at the same time, so that appropriate locks will
* already be held. Locking here would be too late anyway.
*/
TupleDesc
ENRMetadataGetTupDesc(EphemeralNamedRelationMetadata enrmd)
{
TupleDesc tupdesc;
/* One, and only one, of these fields must be filled. */
Assert((enrmd->reliddesc == InvalidOid) != (enrmd->tupdesc == NULL));
if (enrmd->tupdesc != NULL)
tupdesc = enrmd->tupdesc;
else
{
Relation relation;
relation = heap_open(enrmd->reliddesc, NoLock);
tupdesc = relation->rd_att;
heap_close(relation, NoLock);
}
return tupdesc;
}

View File

@@ -109,6 +109,7 @@ struct Tuplestorestate
bool truncated; /* tuplestore_trim has removed tuples? */
int64 availMem; /* remaining memory available, in bytes */
int64 allowedMem; /* total memory allowed, in bytes */
int64 tuples; /* number of tuples added */
BufFile *myfile; /* underlying file, or NULL if none */
MemoryContext context; /* memory context for holding tuples */
ResourceOwner resowner; /* resowner for holding temp files */
@@ -267,6 +268,7 @@ tuplestore_begin_common(int eflags, bool interXact, int maxKBytes)
state->memtupdeleted = 0;
state->memtupcount = 0;
state->tuples = 0;
/*
* Initial size of array must be more than ALLOCSET_SEPARATE_THRESHOLD;
@@ -433,6 +435,7 @@ tuplestore_clear(Tuplestorestate *state)
state->truncated = false;
state->memtupdeleted = 0;
state->memtupcount = 0;
state->tuples = 0;
readptr = state->readptrs;
for (i = 0; i < state->readptrcount; readptr++, i++)
{
@@ -533,6 +536,18 @@ tuplestore_select_read_pointer(Tuplestorestate *state, int ptr)
state->activeptr = ptr;
}
/*
* tuplestore_tuple_count
*
* Returns the number of tuples added since creation or the last
* tuplestore_clear().
*/
int64
tuplestore_tuple_count(Tuplestorestate *state)
{
return state->tuples;
}
/*
* tuplestore_ateof
*
@@ -753,6 +768,8 @@ tuplestore_puttuple_common(Tuplestorestate *state, void *tuple)
int i;
ResourceOwner oldowner;
state->tuples++;
switch (state->status)
{
case TSS_INMEM: