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

@ -299,6 +299,7 @@ static void pgss_ExecutorFinish(QueryDesc *queryDesc);
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);
@ -956,7 +957,8 @@ pgss_ExecutorEnd(QueryDesc *queryDesc)
*/
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;
@ -994,11 +996,11 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
{
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--;
}
@ -1058,11 +1060,11 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
{
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);
}
}
@ -2424,6 +2426,9 @@ JumbleRangeTable(pgssJumbleState *jstate, List *rtable)
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;

View File

@ -2639,6 +2639,210 @@ SPIPlanPtr SPI_saveplan(SPIPlanPtr <parameter>plan</parameter>)
</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">

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:

View File

@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 201703311
#define CATALOG_VERSION_NO 201703312
#endif

View File

@ -18,10 +18,11 @@
#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);

View File

@ -62,19 +62,20 @@ extern PGDLLIMPORT explain_get_index_name_hook_type explain_get_index_name_hook;
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);

View File

@ -42,8 +42,8 @@ extern void ExecuteQuery(ExecuteStmt *stmt, IntoClause *intoClause,
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,

View File

@ -40,6 +40,7 @@ typedef struct QueryDesc
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 */
@ -61,6 +62,7 @@ extern QueryDesc *CreateQueryDesc(PlannedStmt *plannedstmt,
Snapshot crosscheck_snapshot,
DestReceiver *dest,
ParamListInfo params,
QueryEnvironment *queryEnv,
int instrument_options);
extern void FreeQueryDesc(QueryDesc *qdesc);

View File

@ -0,0 +1,24 @@
/*-------------------------------------------------------------------------
*
* 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 */

View File

@ -43,6 +43,8 @@ typedef struct _SPI_plan *SPIPlanPtr;
#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
@ -58,6 +60,8 @@ typedef struct _SPI_plan *SPIPlanPtr;
#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)
@ -146,6 +150,9 @@ extern void SPI_scroll_cursor_fetch(Portal, FetchDirection direction, long count
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);

View File

@ -14,6 +14,7 @@
#define SPI_PRIV_H
#include "executor/spi.h"
#include "utils/queryenvironment.h"
#define _SPI_PLAN_MAGIC 569278163
@ -31,6 +32,7 @@ typedef struct
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;
/*

View File

@ -22,6 +22,7 @@
#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"
@ -431,6 +432,8 @@ typedef struct EState
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 */
@ -1445,6 +1448,24 @@ typedef struct CteScanState
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
*

View File

@ -63,6 +63,7 @@ typedef enum NodeTag
T_ValuesScan,
T_TableFuncScan,
T_CteScan,
T_NamedTuplestoreScan,
T_WorkTableScan,
T_ForeignScan,
T_CustomScan,
@ -114,6 +115,7 @@ typedef enum NodeTag
T_TableFuncScanState,
T_ValuesScanState,
T_CteScanState,
T_NamedTuplestoreScanState,
T_WorkTableScanState,
T_ForeignScanState,
T_CustomScanState,

View File

@ -906,7 +906,8 @@ typedef enum RTEKind
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
@ -993,6 +994,9 @@ 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:
*/

View File

@ -527,6 +527,16 @@ typedef struct CteScan
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
* ----------------

View File

@ -98,6 +98,8 @@ extern void cost_tablefuncscan(Path *path, PlannerInfo *root,
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,
@ -187,6 +189,7 @@ extern void set_values_size_estimates(PlannerInfo *root, RelOptInfo *rel);
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,

View File

@ -100,6 +100,8 @@ extern Path *create_tablefuncscan_path(PlannerInfo *root, RelOptInfo *rel,
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,

View File

@ -23,7 +23,7 @@ extern PGDLLIMPORT post_parse_analyze_hook_type post_parse_analyze_hook;
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);

View File

@ -0,0 +1,22 @@
/*-------------------------------------------------------------------------
*
* 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 */

View File

@ -15,6 +15,7 @@
#define PARSE_NODE_H
#include "nodes/parsenodes.h"
#include "utils/queryenvironment.h"
#include "utils/relcache.h"
@ -188,6 +189,8 @@ struct ParseState
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;

View File

@ -42,6 +42,7 @@ extern RangeTblEntry *refnameRangeTblEntry(ParseState *pstate,
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,
@ -107,6 +108,9 @@ extern RangeTblEntry *addRangeTableEntryForCTE(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,

View File

@ -24,6 +24,7 @@
#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 */
@ -49,11 +50,13 @@ extern int log_statement;
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,

View File

@ -26,15 +26,18 @@ typedef enum
/* 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);

View File

@ -17,6 +17,7 @@
#include "access/tupdesc.h"
#include "nodes/params.h"
#include "utils/queryenvironment.h"
/* Forward declaration, to avoid including parsenodes.h here */
struct RawStmt;
@ -148,7 +149,8 @@ extern void ResetPlanCache(void);
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);
@ -172,11 +174,13 @@ extern CachedPlanSource *CopyCachedPlan(CachedPlanSource *plansource);
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 */

View File

@ -137,6 +137,7 @@ typedef struct PortalData
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 */

View File

@ -0,0 +1,74 @@
/*-------------------------------------------------------------------------
*
* 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 */

View File

@ -78,6 +78,8 @@ extern bool tuplestore_advance(Tuplestorestate *state, bool forward);
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);

View File

@ -2272,3 +2272,6 @@ with ordinality as (select 1 as x) select * from ordinality;
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

View File

@ -1028,3 +1028,6 @@ DROP RULE y_rule ON y;
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);