static void pgss_ExecutorEnd(QueryDesc *queryDesc);
static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context, ParamListInfo params,
+ QueryEnvironment *queryEnv,
DestReceiver *dest, char *completionTag);
static uint32 pgss_hash_fn(const void *key, Size keysize);
static int pgss_match_fn(const void *key1, const void *key2, Size keysize);
*/
static void
pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
- ProcessUtilityContext context, ParamListInfo params,
+ ProcessUtilityContext context,
+ ParamListInfo params, QueryEnvironment *queryEnv,
DestReceiver *dest, char *completionTag)
{
Node *parsetree = pstmt->utilityStmt;
{
if (prev_ProcessUtility)
prev_ProcessUtility(pstmt, queryString,
- context, params,
+ context, params, queryEnv,
dest, completionTag);
else
standard_ProcessUtility(pstmt, queryString,
- context, params,
+ context, params, queryEnv,
dest, completionTag);
nested_level--;
}
{
if (prev_ProcessUtility)
prev_ProcessUtility(pstmt, queryString,
- context, params,
+ context, params, queryEnv,
dest, completionTag);
else
standard_ProcessUtility(pstmt, queryString,
- context, params,
+ context, params, queryEnv,
dest, completionTag);
}
}
APP_JUMB_STRING(rte->ctename);
APP_JUMB(rte->ctelevelsup);
break;
+ case RTE_NAMEDTUPLESTORE:
+ APP_JUMB_STRING(rte->enrname);
+ break;
default:
elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
break;
</refsect1>
</refentry>
+<!-- *********************************************** -->
+
+<refentry id="spi-spi-register-relation">
+ <indexterm><primary>SPI_register_relation</primary></indexterm>
+
+ <refmeta>
+ <refentrytitle>SPI_register_relation</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>SPI_register_relation</refname>
+ <refpurpose>make a ephemeral named relation available by name in SPI queries</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+int SPI_register_relation(EphemeralNamedRelation <parameter>enr</parameter>)
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <function>SPI_register_relation</function> makes an ephemeral named
+ relation, with associated information, available to queries planned and
+ executed through the current SPI connection.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Arguments</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>EphemeralNamedRelation <parameter>enr</parameter></literal></term>
+ <listitem>
+ <para>
+ the ephemeral named relation registry entry
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>
+ If the execution of the command was successful then the following
+ (nonnegative) value will be returned:
+
+ <variablelist>
+ <varlistentry>
+ <term><symbol>SPI_OK_REL_REGISTER</symbol></term>
+ <listitem>
+ <para>
+ if the relation has been successfully registered by name
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>
+ On error, one of the following negative values is returned:
+
+ <variablelist>
+ <varlistentry>
+ <term><symbol>SPI_ERROR_ARGUMENT</symbol></term>
+ <listitem>
+ <para>
+ if <parameter>enr</parameter> is <symbol>NULL</symbol> or its
+ <varname>name</varname> field is <symbol>NULL</symbol>
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><symbol>SPI_ERROR_UNCONNECTED</symbol></term>
+ <listitem>
+ <para>
+ if called from an unconnected procedure
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><symbol>SPI_ERROR_REL_DUPLICATE</symbol></term>
+ <listitem>
+ <para>
+ if the name specified in the <varname>name</varname> field of
+ <parameter>enr</parameter> is already registered for this connection
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </refsect1>
+</refentry>
+
+<!-- *********************************************** -->
+
+<refentry id="spi-spi-unregister-relation">
+ <indexterm><primary>SPI_unregister_relation</primary></indexterm>
+
+ <refmeta>
+ <refentrytitle>SPI_unregister_relation</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>SPI_unregister_relation</refname>
+ <refpurpose>remove an ephemeral named relation from the registry</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+int SPI_unregister_relation(const char * <parameter>name</parameter>)
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <function>SPI_unregister_relation</function> removes an ephemeral named
+ relation from the registry for the current connection.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Arguments</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>const char * <parameter>name</parameter></literal></term>
+ <listitem>
+ <para>
+ the relation registry entry name
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>
+ If the execution of the command was successful then the following
+ (nonnegative) value will be returned:
+
+ <variablelist>
+ <varlistentry>
+ <term><symbol>SPI_OK_REL_UNREGISTER</symbol></term>
+ <listitem>
+ <para>
+ if the tuplestore has been successfully removed from the registry
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>
+ On error, one of the following negative values is returned:
+
+ <variablelist>
+ <varlistentry>
+ <term><symbol>SPI_ERROR_ARGUMENT</symbol></term>
+ <listitem>
+ <para>
+ if <parameter>name</parameter> is <symbol>NULL</symbol>
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><symbol>SPI_ERROR_UNCONNECTED</symbol></term>
+ <listitem>
+ <para>
+ if called from an unconnected procedure
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><symbol>SPI_ERROR_REL_NOT_FOUND</symbol></term>
+ <listitem>
+ <para>
+ if <parameter>name</parameter> is not found in the registry for the
+ current connection
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </refsect1>
+</refentry>
+
+<!-- *********************************************** -->
+
</sect1>
<sect1 id="spi-interface-support">
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);
}
* 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)
cstate->queryDesc = CreateQueryDesc(plan, pstate->p_sourcetext,
GetActiveSnapshot(),
InvalidSnapshot,
- dest, NULL, 0);
+ dest, NULL, NULL, 0);
/*
* Call ExecutorStart to prepare the plan for execution.
*/
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;
/* 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));
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);
*/
void
ExplainQuery(ParseState *pstate, ExplainStmt *stmt, const char *queryString,
- ParamListInfo params, DestReceiver *dest)
+ ParamListInfo params, QueryEnvironment *queryEnv,
+ DestReceiver *dest)
{
ExplainState *es = NewExplainState();
TupOutputState *tstate;
{
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)
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;
}
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);
}
}
*/
void
ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es,
- const char *queryString, ParamListInfo params)
+ const char *queryString, ParamListInfo params,
+ QueryEnvironment *queryEnv)
{
if (utilityStmt == NULL)
return;
Assert(list_length(rewritten) == 1);
ExplainOneQuery(castNode(Query, linitial(rewritten)),
0, ctas->into, es,
- queryString, params);
+ queryString, params, queryEnv);
}
else if (IsA(utilityStmt, DeclareCursorStmt))
{
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)
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;
/* 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)
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);
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;
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);
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);
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)
qdesc = CreateQueryDesc(stmt,
sql,
GetActiveSnapshot(), NULL,
- dest, NULL, 0);
+ dest, NULL, NULL, 0);
ExecutorStart(qdesc, 0);
ExecutorRun(qdesc, ForwardScanDirection, 0, true);
sql,
PROCESS_UTILITY_QUERY,
NULL,
+ NULL,
dest,
NULL);
}
/* 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 */
/* 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);
* 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);
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;
/*
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);
*/
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;
}
/* 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);
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 */
queryString,
PROCESS_UTILITY_SUBCOMMAND,
NULL,
+ NULL,
None_Receiver,
NULL);
* 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),
/* ... 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 */
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.
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
#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"
ExecReScanCteScan((CteScanState *) node);
break;
+ case T_NamedTuplestoreScanState:
+ ExecReScanNamedTuplestoreScan((NamedTuplestoreScanState *) node);
+ break;
+
case T_WorkTableScanState:
ExecReScanWorkTableScan((WorkTableScanState *) node);
break;
case T_FunctionScan:
case T_TableFuncScan:
case T_CteScan:
+ case T_NamedTuplestoreScan:
case T_WorkTableScan:
case T_Sort:
return true;
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
*/
return CreateQueryDesc(pstmt,
queryString,
GetActiveSnapshot(), InvalidSnapshot,
- receiver, paramLI, instrument_options);
+ receiver, paramLI, NULL, instrument_options);
}
/*
#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"
estate, eflags);
break;
+ case T_NamedTuplestoreScan:
+ result = (PlanState *) ExecInitNamedTuplestoreScan((NamedTuplestoreScan *) node,
+ estate, eflags);
+ break;
+
case T_WorkTableScan:
result = (PlanState *) ExecInitWorkTableScan((WorkTableScan *) node,
estate, eflags);
result = ExecCteScan((CteScanState *) node);
break;
+ case T_NamedTuplestoreScanState:
+ result = ExecNamedTuplestoreScan((NamedTuplestoreScanState *) node);
+ break;
+
case T_WorkTableScanState:
result = ExecWorkTableScan((WorkTableScanState *) node);
break;
ExecEndCteScan((CteScanState *) node);
break;
+ case T_NamedTuplestoreScanState:
+ ExecEndNamedTuplestoreScan((NamedTuplestoreScanState *) node);
+ break;
+
case T_WorkTableScanState:
ExecEndWorkTableScan((WorkTableScanState *) node);
break;
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;
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));
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)
fcache->src,
PROCESS_UTILITY_QUERY,
es->qd->params,
+ es->qd->queryEnv,
es->qd->dest,
NULL);
result = true; /* never stops early */
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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);
+}
_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
*/
/* 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 */
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:
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);
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 */
*/
plansource = CreateCachedPlan(parsetree,
src,
- CreateCommandTag(parsetree->stmt));
+ CreateCommandTag(parsetree->stmt),
+ _SPI_current->queryEnv);
/*
* Parameter datatypes are driven by parserSetup hook if provided,
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 */
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 */
* 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;
/*
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);
plansource->query_string,
PROCESS_UTILITY_QUERY,
paramLI,
+ _SPI_current->queryEnv,
dest,
completionTag);
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;
+}
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
*/
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);
case T_CteScan:
retval = _copyCteScan(from);
break;
+ case T_NamedTuplestoreScan:
+ retval = _copyNamedTuplestoreScan(from);
+ break;
case T_WorkTableScan:
retval = _copyWorkTableScan(from);
break;
return true;
break;
case RTE_CTE:
+ case RTE_NAMEDTUPLESTORE:
/* nothing to do */
break;
case RTE_SUBQUERY:
/* we don't bother to copy eref, aliases, etc; OK? */
break;
case RTE_CTE:
+ case RTE_NAMEDTUPLESTORE:
/* nothing to do */
break;
case RTE_SUBQUERY:
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)
{
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;
case T_CteScan:
_outCteScan(str, obj);
break;
+ case T_NamedTuplestoreScan:
+ _outNamedTuplestoreScan(str, obj);
+ break;
case T_WorkTableScan:
_outWorkTableScan(str, obj);
break;
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);
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);
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);
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;
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;
* executed only once.
*/
return;
+
+ case RTE_NAMEDTUPLESTORE:
+ /*
+ * tuplestore cannot be shared, at least without more
+ * infrastructure to support that.
+ */
+ return;
}
/*
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
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,
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.
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,
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);
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);
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,
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'
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,
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;
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 */
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;
break;
case RTE_JOIN:
case RTE_CTE:
+ case RTE_NAMEDTUPLESTORE:
/* these can't contain any lateral references */
break;
}
break;
case RTE_JOIN:
case RTE_CTE:
+ case RTE_NAMEDTUPLESTORE:
/* these shouldn't be marked LATERAL */
Assert(false);
break;
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);
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,
* 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)
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);
case RTE_TABLEFUNC:
case RTE_VALUES:
case RTE_CTE:
+ case RTE_NAMEDTUPLESTORE:
/*
* Subquery, function, tablefunc, or values list --- set up attr
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
*/
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;
if (numParams > 0)
parse_fixed_parameters(pstate, paramTypes, numParams);
+ pstate->p_queryEnv = queryEnv;
+
query = transformTopLevelStmt(pstate, parseTree);
if (post_parse_analyze_hook)
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);
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,
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);
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
*/
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
RangeTblEntry *rte = NULL;
int rtindex;
- /* if it is an unqualified name, it might be a CTE reference */
+ /*
+ * if it is an unqualified name, it might be a CTE or tuplestore
+ * reference
+ */
if (!rv->schemaname)
- {
- CommonTableExpr *cte;
- Index levelsup;
-
- cte = scanNameSpaceForCTE(pstate, rv->relname, &levelsup);
- if (cte)
- rte = transformCTEReference(pstate, rv, cte, levelsup);
- }
+ rte = getRTEForSpecialRelationTypes(pstate, rv);
- /* if not found as a CTE, must be a table reference */
+ /* if not found above, must be a table reference */
if (!rte)
rte = transformTableEntry(pstate, rv);
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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);
+}
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;
#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"
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.
const char *refname = relation->relname;
Oid relId = InvalidOid;
CommonTableExpr *cte = NULL;
+ bool isenr = false;
Index ctelevelsup = 0;
Index levelsup;
* 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++)
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;
}
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",
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?
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);
case RTE_TABLEFUNC:
case RTE_VALUES:
case RTE_CTE:
+ case RTE_NAMEDTUPLESTORE:
{
/*
* tablefunc, VALUES or CTE RTE --- get type info from lists
*/
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:
{
/*
case RTE_FUNCTION:
case RTE_VALUES:
case RTE_TABLEFUNC:
+ case RTE_NAMEDTUPLESTORE:
/* not a simple relation, leave it unmarked */
break;
case RTE_CTE:
{
case RTE_RELATION:
case RTE_VALUES:
+ case RTE_NAMEDTUPLESTORE:
/*
* This case should not occur: a column of a table or values list
*/
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;
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");
pg_analyze_and_rewrite_params(RawStmt *parsetree,
const char *query_string,
ParserSetupHook parserSetup,
- void *parserSetupArg)
+ void *parserSetupArg,
+ QueryEnvironment *queryEnv)
{
ParseState *pstate;
Query *query;
pstate = make_parsestate(NULL);
pstate->p_sourcetext = query_string;
+ pstate->p_queryEnv = queryEnv;
(*parserSetup) (pstate, parserSetupArg);
query = transformTopLevelStmt(pstate, parsetree);
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);
* 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.
/* 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;
}
* 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.
List *tlist;
/* Get the plan's primary targetlist */
- tlist = CachedPlanGetTargetList(psrc);
+ tlist = CachedPlanGetTargetList(psrc, NULL);
SendRowDescriptionMessage(psrc->resultDesc, tlist, NULL);
}
static void ProcessQuery(PlannedStmt *plan,
const char *sourceText,
ParamListInfo params,
+ QueryEnvironment *queryEnv,
DestReceiver *dest,
char *completionTag);
static void FillPortalStore(Portal portal, bool isTopLevel);
Snapshot crosscheck_snapshot,
DestReceiver *dest,
ParamListInfo params,
+ QueryEnvironment *queryEnv,
int instrument_options)
{
QueryDesc *qd = (QueryDesc *) palloc(sizeof(QueryDesc));
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? */
ProcessQuery(PlannedStmt *plan,
const char *sourceText,
ParamListInfo params,
+ QueryEnvironment *queryEnv,
DestReceiver *dest,
char *completionTag)
{
*/
queryDesc = CreateQueryDesc(plan, sourceText,
GetActiveSnapshot(), InvalidSnapshot,
- dest, params, 0);
+ dest, params, queryEnv, 0);
/*
* Call ExecutorStart to prepare the plan for execution
InvalidSnapshot,
None_Receiver,
params,
+ portal->queryEnv,
0);
/*
portal->sourceText,
isTopLevel ? PROCESS_UTILITY_TOPLEVEL : PROCESS_UTILITY_QUERY,
portal->portalParams,
+ portal->queryEnv,
dest,
completionTag);
ProcessQuery(pstmt,
portal->sourceText,
portal->portalParams,
+ portal->queryEnv,
dest, completionTag);
}
else
ProcessQuery(pstmt,
portal->sourceText,
portal->portalParams,
+ portal->queryEnv,
altdest, NULL);
}
const char *queryString,
ProcessUtilityContext context,
ParamListInfo params,
+ QueryEnvironment *queryEnv,
DestReceiver *dest,
char *completionTag);
static void ExecDropStmt(DropStmt *stmt, bool isTopLevel);
const char *queryString,
ProcessUtilityContext context,
ParamListInfo params,
+ QueryEnvironment *queryEnv,
DestReceiver *dest,
char *completionTag)
{
*/
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);
}
const char *queryString,
ProcessUtilityContext context,
ParamListInfo params,
+ QueryEnvironment *queryEnv,
DestReceiver *dest,
char *completionTag)
{
break;
case T_ExplainStmt:
- ExplainQuery(pstate, (ExplainStmt *) parsetree, queryString, params, dest);
+ ExplainQuery(pstate, (ExplainStmt *) parsetree, queryString, params,
+ queryEnv, dest);
break;
case T_AlterSystemStmt:
if (EventTriggerSupportsGrantObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
- context, params,
+ context, params, queryEnv,
dest, completionTag);
else
ExecuteGrantStmt(stmt);
if (EventTriggerSupportsObjectType(stmt->removeType))
ProcessUtilitySlow(pstate, pstmt, queryString,
- context, params,
+ context, params, queryEnv,
dest, completionTag);
else
ExecDropStmt(stmt, isTopLevel);
if (EventTriggerSupportsObjectType(stmt->renameType))
ProcessUtilitySlow(pstate, pstmt, queryString,
- context, params,
+ context, params, queryEnv,
dest, completionTag);
else
ExecRenameStmt(stmt);
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
- context, params,
+ context, params, queryEnv,
dest, completionTag);
else
ExecAlterObjectDependsStmt(stmt, NULL);
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
- context, params,
+ context, params, queryEnv,
dest, completionTag);
else
ExecAlterObjectSchemaStmt(stmt, NULL);
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, pstmt, queryString,
- context, params,
+ context, params, queryEnv,
dest, completionTag);
else
ExecAlterOwnerStmt(stmt);
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
- context, params,
+ context, params, queryEnv,
dest, completionTag);
else
CommentObject(stmt);
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, pstmt, queryString,
- context, params,
+ context, params, queryEnv,
dest, completionTag);
else
ExecSecLabelStmt(stmt);
default:
/* All other statement types have event trigger support */
ProcessUtilitySlow(pstate, pstmt, queryString,
- context, params,
+ context, params, queryEnv,
dest, completionTag);
break;
}
const char *queryString,
ProcessUtilityContext context,
ParamListInfo params,
+ QueryEnvironment *queryEnv,
DestReceiver *dest,
char *completionTag)
{
queryString,
PROCESS_UTILITY_SUBCOMMAND,
params,
+ NULL,
None_Receiver,
NULL);
}
queryString,
PROCESS_UTILITY_SUBCOMMAND,
params,
+ NULL,
None_Receiver,
NULL);
EventTriggerAlterTableStart(parsetree);
case T_CreateTableAsStmt:
address = ExecCreateTableAs((CreateTableAsStmt *) parsetree,
- queryString, params, completionTag);
+ queryString, params, queryEnv,
+ completionTag);
break;
case T_RefreshMatViewStmt:
{
case RTE_RELATION:
case RTE_VALUES:
+ case RTE_NAMEDTUPLESTORE:
/*
* This case should not occur: a column of a table or values list
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);
CachedPlanSource *
CreateCachedPlan(RawStmt *raw_parse_tree,
const char *query_string,
- const char *commandTag)
+ const char *commandTag,
+ QueryEnvironment *queryEnv)
{
CachedPlanSource *plansource;
MemoryContext source_context;
* a tree copying step in a subsequent BuildCachedPlan call.)
*/
static List *
-RevalidateCachedQuery(CachedPlanSource *plansource)
+RevalidateCachedQuery(CachedPlanSource *plansource,
+ QueryEnvironment *queryEnv)
{
bool snapshot_set;
RawStmt *rawtree;
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)
*/
static CachedPlan *
BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
- ParamListInfo boundParams)
+ ParamListInfo boundParams, QueryEnvironment *queryEnv)
{
CachedPlan *plan;
List *plist;
* 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
*/
CachedPlan *
GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams,
- bool useResOwner)
+ bool useResOwner, QueryEnvironment *queryEnv)
{
CachedPlan *plan = NULL;
List *qlist;
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);
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 */
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)
{
* within the cached plan, and may disappear next time the plan is updated.
*/
List *
-CachedPlanGetTargetList(CachedPlanSource *plansource)
+CachedPlanGetTargetList(CachedPlanSource *plansource,
+ QueryEnvironment *queryEnv)
{
Query *pstmt;
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);
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.
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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;
+}
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 */
state->memtupdeleted = 0;
state->memtupcount = 0;
+ state->tuples = 0;
/*
* Initial size of array must be more than ALLOCSET_SEPARATE_THRESHOLD;
state->truncated = false;
state->memtupdeleted = 0;
state->memtupcount = 0;
+ state->tuples = 0;
readptr = state->readptrs;
for (i = 0; i < state->readptrcount; readptr++, i++)
{
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
*
int i;
ResourceOwner oldowner;
+ state->tuples++;
+
switch (state->status)
{
case TSS_INMEM:
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201703311
+#define CATALOG_VERSION_NO 201703312
#endif
#include "nodes/params.h"
#include "nodes/parsenodes.h"
#include "tcop/dest.h"
+#include "utils/queryenvironment.h"
extern ObjectAddress ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
- ParamListInfo params, char *completionTag);
+ ParamListInfo params, QueryEnvironment *queryEnv, char *completionTag);
extern int GetIntoRelEFlags(IntoClause *intoClause);
extern void ExplainQuery(ParseState *pstate, ExplainStmt *stmt, const char *queryString,
- ParamListInfo params, DestReceiver *dest);
+ ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest);
extern ExplainState *NewExplainState(void);
extern TupleDesc ExplainResultDesc(ExplainStmt *stmt);
extern void ExplainOneUtility(Node *utilityStmt, IntoClause *into,
- ExplainState *es,
- const char *queryString, ParamListInfo params);
+ ExplainState *es, const char *queryString,
+ ParamListInfo params, QueryEnvironment *queryEnv);
extern void ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into,
ExplainState *es, const char *queryString,
- ParamListInfo params, const instr_time *planduration);
+ ParamListInfo params, QueryEnvironment *queryEnv,
+ const instr_time *planduration);
extern void ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc);
extern void ExplainPrintTriggers(ExplainState *es, QueryDesc *queryDesc);
DestReceiver *dest, char *completionTag);
extern void DeallocateQuery(DeallocateStmt *stmt);
extern void ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into,
- ExplainState *es,
- const char *queryString, ParamListInfo params);
+ ExplainState *es, const char *queryString,
+ ParamListInfo params, QueryEnvironment *queryEnv);
/* Low-level access to stored prepared statements */
extern void StorePreparedStatement(const char *stmt_name,
Snapshot crosscheck_snapshot; /* crosscheck for RI update/delete */
DestReceiver *dest; /* the destination for tuple output */
ParamListInfo params; /* param values being passed in */
+ QueryEnvironment *queryEnv; /* query environment passed in */
int instrument_options; /* OR of InstrumentOption flags */
/* These fields are set by ExecutorStart */
Snapshot crosscheck_snapshot,
DestReceiver *dest,
ParamListInfo params,
+ QueryEnvironment *queryEnv,
int instrument_options);
extern void FreeQueryDesc(QueryDesc *qdesc);
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * nodeNamedtuplestorescan.h
+ *
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/executor/nodeNamedtuplestorescan.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NODENAMEDTUPLESTORESCAN_H
+#define NODENAMEDTUPLESTORESCAN_H
+
+#include "nodes/execnodes.h"
+
+extern NamedTuplestoreScanState *ExecInitNamedTuplestoreScan(NamedTuplestoreScan *node, EState *estate, int eflags);
+extern TupleTableSlot *ExecNamedTuplestoreScan(NamedTuplestoreScanState *node);
+extern void ExecEndNamedTuplestoreScan(NamedTuplestoreScanState *node);
+extern void ExecReScanNamedTuplestoreScan(NamedTuplestoreScanState *node);
+
+#endif /* NODENAMEDTUPLESTORESCAN_H */
#define SPI_ERROR_NOATTRIBUTE (-9)
#define SPI_ERROR_NOOUTFUNC (-10)
#define SPI_ERROR_TYPUNKNOWN (-11)
+#define SPI_ERROR_REL_DUPLICATE (-12)
+#define SPI_ERROR_REL_NOT_FOUND (-13)
#define SPI_OK_CONNECT 1
#define SPI_OK_FINISH 2
#define SPI_OK_DELETE_RETURNING 12
#define SPI_OK_UPDATE_RETURNING 13
#define SPI_OK_REWRITTEN 14
+#define SPI_OK_REL_REGISTER 15
+#define SPI_OK_REL_UNREGISTER 16
/* These used to be functions, now just no-ops for backwards compatibility */
#define SPI_push() ((void) 0)
extern void SPI_scroll_cursor_move(Portal, FetchDirection direction, long count);
extern void SPI_cursor_close(Portal portal);
+extern int SPI_register_relation(EphemeralNamedRelation enr);
+extern int SPI_unregister_relation(const char *name);
+
extern void AtEOXact_SPI(bool isCommit);
extern void AtEOSubXact_SPI(bool isCommit, SubTransactionId mySubid);
#define SPI_PRIV_H
#include "executor/spi.h"
+#include "utils/queryenvironment.h"
#define _SPI_PLAN_MAGIC 569278163
MemoryContext execCxt; /* executor context */
MemoryContext savedcxt; /* context of SPI_connect's caller */
SubTransactionId connectSubid; /* ID of connecting subtransaction */
+ QueryEnvironment *queryEnv; /* query environment setup for SPI level */
} _SPI_connection;
/*
#include "nodes/params.h"
#include "nodes/plannodes.h"
#include "utils/hsearch.h"
+#include "utils/queryenvironment.h"
#include "utils/reltrigger.h"
#include "utils/sortsupport.h"
#include "utils/tuplestore.h"
ParamListInfo es_param_list_info; /* values of external params */
ParamExecData *es_param_exec_vals; /* values of internal params */
+ QueryEnvironment *es_queryEnv; /* query environment */
+
/* Other working state: */
MemoryContext es_query_cxt; /* per-query context in which EState lives */
bool eof_cte; /* reached end of CTE query? */
} CteScanState;
+/* ----------------
+ * NamedTuplestoreScanState information
+ *
+ * NamedTuplestoreScan nodes are used to scan a Tuplestore created and
+ * named prior to execution of the query. An example is a transition
+ * table for an AFTER trigger.
+ *
+ * Multiple NamedTuplestoreScan nodes can read out from the same Tuplestore.
+ * ----------------
+ */
+typedef struct NamedTuplestoreScanState
+{
+ ScanState ss; /* its first field is NodeTag */
+ int readptr; /* index of my tuplestore read pointer */
+ TupleDesc tupdesc; /* format of the tuples in the tuplestore */
+ Tuplestorestate *relation; /* the rows */
+} NamedTuplestoreScanState;
+
/* ----------------
* WorkTableScanState information
*
T_ValuesScan,
T_TableFuncScan,
T_CteScan,
+ T_NamedTuplestoreScan,
T_WorkTableScan,
T_ForeignScan,
T_CustomScan,
T_TableFuncScanState,
T_ValuesScanState,
T_CteScanState,
+ T_NamedTuplestoreScanState,
T_WorkTableScanState,
T_ForeignScanState,
T_CustomScanState,
RTE_FUNCTION, /* function in FROM */
RTE_TABLEFUNC, /* TableFunc(.., column list) */
RTE_VALUES, /* VALUES (<exprlist>), (<exprlist>), ... */
- RTE_CTE /* common table expr (WITH list element) */
+ RTE_CTE, /* common table expr (WITH list element) */
+ RTE_NAMEDTUPLESTORE /* tuplestore, e.g. for AFTER triggers */
} RTEKind;
typedef struct RangeTblEntry
List *coltypmods; /* integer list of column typmods */
List *colcollations; /* OID list of column collation OIDs */
+ char *enrname; /* name of ephemeral named relation */
+ double enrtuples; /* estimated or actual from caller */
+
/*
* Fields valid in all RTEs:
*/
int cteParam; /* ID of Param representing CTE output */
} CteScan;
+/* ----------------
+ * NamedTuplestoreScan node
+ * ----------------
+ */
+typedef struct NamedTuplestoreScan
+{
+ Scan scan;
+ char *enrname; /* Name given to Ephemeral Named Relation */
+} NamedTuplestoreScan;
+
/* ----------------
* WorkTableScan node
* ----------------
RelOptInfo *baserel, ParamPathInfo *param_info);
extern void cost_ctescan(Path *path, PlannerInfo *root,
RelOptInfo *baserel, ParamPathInfo *param_info);
+extern void cost_namedtuplestorescan(Path *path, PlannerInfo *root,
+ RelOptInfo *baserel, ParamPathInfo *param_info);
extern void cost_recursive_union(Path *runion, Path *nrterm, Path *rterm);
extern void cost_sort(Path *path, PlannerInfo *root,
List *pathkeys, Cost input_cost, double tuples, int width,
extern void set_cte_size_estimates(PlannerInfo *root, RelOptInfo *rel,
double cte_rows);
extern void set_tablefunc_size_estimates(PlannerInfo *root, RelOptInfo *rel);
+extern void set_namedtuplestore_size_estimates(PlannerInfo *root, RelOptInfo *rel);
extern void set_foreign_size_estimates(PlannerInfo *root, RelOptInfo *rel);
extern PathTarget *set_pathtarget_cost_width(PlannerInfo *root, PathTarget *target);
extern double compute_bitmap_pages(PlannerInfo *root, RelOptInfo *baserel,
Relids required_outer);
extern Path *create_ctescan_path(PlannerInfo *root, RelOptInfo *rel,
Relids required_outer);
+extern Path *create_namedtuplestorescan_path(PlannerInfo *root, RelOptInfo *rel,
+ Relids required_outer);
extern Path *create_worktablescan_path(PlannerInfo *root, RelOptInfo *rel,
Relids required_outer);
extern ForeignPath *create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel,
extern Query *parse_analyze(RawStmt *parseTree, const char *sourceText,
- Oid *paramTypes, int numParams);
+ Oid *paramTypes, int numParams, QueryEnvironment *queryEnv);
extern Query *parse_analyze_varparams(RawStmt *parseTree, const char *sourceText,
Oid **paramTypes, int *numParams);
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * parse_enr.h
+ * Internal definitions for parser
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/parser/parse_enr.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PARSE_ENR_H
+#define PARSE_ENR_H
+
+#include "parser/parse_node.h"
+
+extern bool name_matches_visible_ENR(ParseState *pstate, const char *refname);
+extern EphemeralNamedRelationMetadata get_visible_ENR(ParseState *pstate, const char *refname);
+
+#endif /* PARSE_ENR_H */
#define PARSE_NODE_H
#include "nodes/parsenodes.h"
+#include "utils/queryenvironment.h"
#include "utils/relcache.h"
bool p_resolve_unknowns; /* resolve unknown-type SELECT outputs
* as type text */
+ QueryEnvironment *p_queryEnv; /* curr env, incl refs to enclosing env */
+
/* Flags telling about things found in the query: */
bool p_hasAggs;
bool p_hasWindowFuncs;
extern CommonTableExpr *scanNameSpaceForCTE(ParseState *pstate,
const char *refname,
Index *ctelevelsup);
+extern bool scanNameSpaceForENR(ParseState *pstate, const char *refname);
extern void checkNameSpaceConflicts(ParseState *pstate, List *namespace1,
List *namespace2);
extern int RTERangeTablePosn(ParseState *pstate,
Index levelsup,
RangeVar *rv,
bool inFromCl);
+extern RangeTblEntry *addRangeTableEntryForENR(ParseState *pstate,
+ RangeVar *rv,
+ bool inFromCl);
extern bool isLockedRefname(ParseState *pstate, const char *refname);
extern void addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte,
bool addToJoinList,
#include "nodes/plannodes.h"
#include "storage/procsignal.h"
#include "utils/guc.h"
+#include "utils/queryenvironment.h"
/* Required daylight between max_stack_depth and the kernel limit, in bytes */
extern List *pg_parse_query(const char *query_string);
extern List *pg_analyze_and_rewrite(RawStmt *parsetree,
const char *query_string,
- Oid *paramTypes, int numParams);
+ Oid *paramTypes, int numParams,
+ QueryEnvironment *queryEnv);
extern List *pg_analyze_and_rewrite_params(RawStmt *parsetree,
const char *query_string,
ParserSetupHook parserSetup,
- void *parserSetupArg);
+ void *parserSetupArg,
+ QueryEnvironment *queryEnv);
extern PlannedStmt *pg_plan_query(Query *querytree, int cursorOptions,
ParamListInfo boundParams);
extern List *pg_plan_queries(List *querytrees, int cursorOptions,
/* Hook for plugins to get control in ProcessUtility() */
typedef void (*ProcessUtility_hook_type) (PlannedStmt *pstmt,
const char *queryString, ProcessUtilityContext context,
- ParamListInfo params,
+ ParamListInfo params,
+ QueryEnvironment *queryEnv,
DestReceiver *dest, char *completionTag);
extern PGDLLIMPORT ProcessUtility_hook_type ProcessUtility_hook;
extern void ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context, ParamListInfo params,
+ QueryEnvironment *queryEnv,
DestReceiver *dest, char *completionTag);
extern void standard_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
ProcessUtilityContext context, ParamListInfo params,
+ QueryEnvironment *queryEnv,
DestReceiver *dest, char *completionTag);
extern bool UtilityReturnsTuples(Node *parsetree);
#include "access/tupdesc.h"
#include "nodes/params.h"
+#include "utils/queryenvironment.h"
/* Forward declaration, to avoid including parsenodes.h here */
struct RawStmt;
extern CachedPlanSource *CreateCachedPlan(struct RawStmt *raw_parse_tree,
const char *query_string,
- const char *commandTag);
+ const char *commandTag,
+ QueryEnvironment *queryEnv);
extern CachedPlanSource *CreateOneShotCachedPlan(struct RawStmt *raw_parse_tree,
const char *query_string,
const char *commandTag);
extern bool CachedPlanIsValid(CachedPlanSource *plansource);
-extern List *CachedPlanGetTargetList(CachedPlanSource *plansource);
+extern List *CachedPlanGetTargetList(CachedPlanSource *plansource,
+ QueryEnvironment *queryEnv);
extern CachedPlan *GetCachedPlan(CachedPlanSource *plansource,
ParamListInfo boundParams,
- bool useResOwner);
+ bool useResOwner,
+ QueryEnvironment *queryEnv);
extern void ReleaseCachedPlan(CachedPlan *plan, bool useResOwner);
#endif /* PLANCACHE_H */
CachedPlan *cplan; /* CachedPlan, if stmts are from one */
ParamListInfo portalParams; /* params to pass to query */
+ QueryEnvironment *queryEnv; /* environment for query */
/* Features/options */
PortalStrategy strategy; /* see above */
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * queryenvironment.h
+ * Access to functions to mutate the query environment and retrieve the
+ * actual data related to entries (if any).
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/queryenvironment.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef QUERYENVIRONMENT_H
+#define QUERYENVIRONMENT_H
+
+#include "access/tupdesc.h"
+
+
+typedef enum EphemeralNameRelationType
+{
+ ENR_NAMED_TUPLESTORE /* named tuplestore relation; e.g., deltas */
+} EphemeralNameRelationType;
+
+/*
+ * Some ephemeral named relations must match some relation (e.g., trigger
+ * transition tables), so to properly handle cached plans and DDL, we should
+ * carry the OID of that relation. In other cases an ENR might be independent
+ * of any relation which is stored in the system catalogs, so we need to be
+ * able to directly store the TupleDesc. We never need both.
+ */
+typedef struct EphemeralNamedRelationMetadataData
+{
+ char *name; /* name used to identify the relation */
+
+ /* only one of the next two fields should be used */
+ Oid reliddesc; /* oid of relation to get tupdesc */
+ TupleDesc tupdesc; /* description of result rows */
+
+ EphemeralNameRelationType enrtype; /* to identify type of relation */
+ double enrtuples; /* estimated number of tuples */
+} EphemeralNamedRelationMetadataData;
+
+typedef EphemeralNamedRelationMetadataData *EphemeralNamedRelationMetadata;
+
+/*
+ * Ephemeral Named Relation data; used for parsing named relations not in the
+ * catalog, like transition tables in AFTER triggers.
+ */
+typedef struct EphemeralNamedRelationData
+{
+ EphemeralNamedRelationMetadataData md;
+ void *reldata; /* structure for execution-time access to data */
+} EphemeralNamedRelationData;
+
+typedef EphemeralNamedRelationData *EphemeralNamedRelation;
+
+/*
+ * This is an opaque structure outside of queryenvironment.c itself. The
+ * intention is to be able to change the implementation or add new context
+ * features without needing to change existing code for use of existing
+ * features.
+ */
+typedef struct QueryEnvironment QueryEnvironment;
+
+
+extern QueryEnvironment *create_queryEnv(void);
+extern EphemeralNamedRelationMetadata get_visible_ENR_metadata(QueryEnvironment *queryEnv, const char *refname);
+extern void register_ENR(QueryEnvironment *queryEnv, EphemeralNamedRelation enr);
+extern void unregister_ENR(QueryEnvironment *queryEnv, const char *name);
+extern EphemeralNamedRelation get_ENR(QueryEnvironment *queryEnv, const char *name);
+extern TupleDesc ENRMetadataGetTupDesc(EphemeralNamedRelationMetadata enrmd);
+
+#endif /* QUERYENVIRONMENT_H */
extern bool tuplestore_skiptuples(Tuplestorestate *state,
int64 ntuples, bool forward);
+extern int64 tuplestore_tuple_count(Tuplestorestate *state);
+
extern bool tuplestore_ateof(Tuplestorestate *state);
extern void tuplestore_rescan(Tuplestorestate *state);
1
(1 row)
+-- check sane response to attempt to modify CTE relation
+WITH d AS (SELECT 42) INSERT INTO d VALUES (1);
+ERROR: relation "d" cannot be the target of a modifying statement
create table foo (with baz); -- fail, WITH is a reserved word
create table foo (with ordinality); -- fail, WITH is a reserved word
with ordinality as (select 1 as x) select * from ordinality;
+
+-- check sane response to attempt to modify CTE relation
+WITH d AS (SELECT 42) INSERT INTO d VALUES (1);