diff --git a/doc/src/sgml/spi.sgml b/doc/src/sgml/spi.sgml
index 836ce08..a4ca54d 100644
--- a/doc/src/sgml/spi.sgml
+++ b/doc/src/sgml/spi.sgml
@@ -2639,6 +2639,310 @@ SPIPlanPtr SPI_saveplan(SPIPlanPtr plan)
+
+
+
+ SPI_register_relation
+
+
+ SPI_register_relation
+ 3
+
+
+
+ SPI_register_relation
+ make a tuplestore available by name in SPI queries
+
+
+
+
+ int SPI_register_relation(const char *name,
+ TupleDesc tupdesc, int tuples,
+ Tuplestorestate *tuplestore)
+
+
+
+
+ Description
+
+
+ SPI_register_relation makes a tuplestore and
+ associated metadata available to queries planned and executed through the
+ current SPI connection.
+
+
+
+
+ Arguments
+
+
+
+ const char *name
+
+
+ the name that SQL queries may use to refer to the relation being registered
+
+
+
+
+ TupleDesc tupdesc
+
+
+ a TupleDesc describing the columns of the relation
+
+
+
+
+ int tuples
+
+
+ an estimate of the number of tuples, for use by the planner
+
+
+
+
+ Tuplestorestate tuplestore
+
+
+ a tuplestore
+
+
+
+
+
+
+
+ Return Value
+
+
+ If the execution of the command was successful then the following
+ (nonnegative) value will be returned:
+
+
+
+ SPI_OK_REGISTER
+
+
+ if the tuplestore has been successfully registered by name
+
+
+
+
+
+
+
+ On error, one of the following negative values is returned:
+
+
+
+ SPI_ERROR_ARGUMENT
+
+
+ if any argument is NULL
+
+
+
+
+
+ SPI_ERROR_UNCONNECTED
+
+
+ if called from an unconnected procedure
+
+
+
+
+
+ SPI_ERROR_DUPLICATE
+
+
+ if the name specified in the name argument
+ is already registered for this connection
+
+
+
+
+
+
+
+
+
+
+
+ SPI_unregister_relation
+
+
+ SPI_unregister_relation
+ 3
+
+
+
+ SPI_unregister_relation
+ remove a named tuplestore from the registry
+
+
+
+
+int SPI_unregister_tuplestore(const char * name)
+
+
+
+
+ Description
+
+
+ SPI_unregister_relation removes a tuplestore from
+ the registry for the current connection.
+
+
+
+
+ Arguments
+
+
+
+ const char * name
+
+
+ the tuplestore registry entry name
+
+
+
+
+
+
+
+ Return Value
+
+
+ If the execution of the command was successful then the following
+ (nonnegative) value will be returned:
+
+
+
+ SPI_OK_UNREGISTER
+
+
+ if the tuplestore has been successfully removed from the registry
+
+
+
+
+
+
+
+ On error, one of the following negative values is returned:
+
+
+
+ SPI_ERROR_ARGUMENT
+
+
+ if name is NULL
+
+
+
+
+
+ SPI_ERROR_UNCONNECTED
+
+
+ if called from an unconnected procedure
+
+
+
+
+
+ SPI_ERROR_NOT_FOUND
+
+
+ if name is not found in the registry for the
+ current connection
+
+
+
+
+
+
+
+
+
+
+
+ SPI_get_caller_tuplestore
+
+
+ SPI_get_caller_tuplestore
+ 3
+
+
+
+ SPI_get_caller_tuplestore
+ return a tuplestore from the registry
+
+
+
+
+int SPI_get_caller_tuplestore(const char * name)
+
+
+
+
+ Description
+
+
+ SPI_get_caller_tuplestore returns a tuplestore from
+ the registry for the current connection.
+
+
+
+
+ Arguments
+
+
+
+ const char * name
+
+
+ the tuplestore registry entry name
+
+
+
+
+
+
+
+ Return Value
+
+
+ Pointer to the tuplestore registry entry; or NULL if
+ unsuccessful. On error, SPI_result is set thus:
+
+
+
+ SPI_ERROR_ARGUMENT
+
+
+ if name is NULL
+
+
+
+
+
+ SPI_ERROR_UNCONNECTED
+
+
+ if called from an unconnected procedure
+
+
+
+
+
+
+
+
+
+
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index c1d1505..e3341c8 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -940,7 +940,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);
}
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 3c81906..2a3b497 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -1425,7 +1425,8 @@ BeginCopy(ParseState *pstate,
* DECLARE CURSOR and PREPARE.) XXX FIXME someday.
*/
rewritten = pg_analyze_and_rewrite((Node *) 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)
@@ -1528,7 +1529,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.
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 5b4f6af..8ed0ff2 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -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 = (Query *) stmt->query;
IntoClause *into = stmt->into;
@@ -342,7 +343,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));
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 0a669d9..8f0cd74 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -54,7 +54,8 @@ explain_get_index_name_hook_type explain_get_index_name_hook = NULL;
#define X_NOWHITESPACE 4
static void ExplainOneQuery(Query *query, 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);
@@ -141,7 +142,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;
@@ -246,7 +248,7 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt, const char *queryString,
foreach(l, rewritten)
{
ExplainOneQuery((Query *) lfirst(l), NULL, es,
- queryString, params);
+ queryString, params, queryEnv);
/* Separate plans with an appropriate separator */
if (lnext(l) != NULL)
@@ -330,12 +332,14 @@ ExplainResultDesc(ExplainStmt *stmt)
*/
static void
ExplainOneQuery(Query *query, 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;
}
@@ -357,7 +361,8 @@ ExplainOneQuery(Query *query, IntoClause *into, ExplainState *es,
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);
}
}
@@ -374,7 +379,8 @@ ExplainOneQuery(Query *query, IntoClause *into, ExplainState *es,
*/
void
ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es,
- const char *queryString, ParamListInfo params)
+ const char *queryString, ParamListInfo params,
+ QueryEnvironment *queryEnv)
{
if (utilityStmt == NULL)
return;
@@ -393,11 +399,11 @@ ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es,
rewritten = QueryRewrite((Query *) copyObject(ctas->query));
Assert(list_length(rewritten) == 1);
ExplainOneQuery((Query *) linitial(rewritten), ctas->into, 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)
@@ -435,7 +441,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;
@@ -478,7 +484,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)
@@ -762,6 +768,7 @@ ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
case T_FunctionScan:
case T_ValuesScan:
case T_CteScan:
+ case T_SpiRelationScan:
case T_WorkTableScan:
*rels_used = bms_add_member(*rels_used,
((Scan *) plan)->scanrelid);
@@ -908,6 +915,9 @@ ExplainNode(PlanState *planstate, List *ancestors,
case T_CteScan:
pname = sname = "CTE Scan";
break;
+ case T_SpiRelationScan:
+ pname = sname = "SPI Relation Scan";
+ break;
case T_WorkTableScan:
pname = sname = "WorkTable Scan";
break;
@@ -1341,6 +1351,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
case T_SeqScan:
case T_ValuesScan:
case T_CteScan:
+ case T_SpiRelationScan:
case T_WorkTableScan:
case T_SubqueryScan:
show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
@@ -2581,6 +2592,11 @@ ExplainTargetRel(Plan *plan, Index rti, ExplainState *es)
objectname = rte->ctename;
objecttag = "CTE Name";
break;
+ case T_SpiRelationScan:
+ Assert(rte->rtekind == RTE_SPIRELATION);
+ objectname = rte->srname;
+ objecttag = "SPI Relation Name";
+ break;
case T_WorkTableScan:
/* Assert it's on a self-reference CTE */
Assert(rte->rtekind == RTE_CTE);
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index f6c2c8a..ec4625e 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -719,7 +719,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)
@@ -743,7 +744,7 @@ execute_sql_string(const char *sql, const char *filename)
qdesc = CreateQueryDesc((PlannedStmt *) stmt,
sql,
GetActiveSnapshot(), NULL,
- dest, NULL, 0);
+ dest, NULL, NULL, 0);
ExecutorStart(qdesc, 0);
ExecutorRun(qdesc, ForwardScanDirection, 0);
@@ -758,6 +759,7 @@ execute_sql_string(const char *sql, const char *filename)
sql,
PROCESS_UTILITY_QUERY,
NULL,
+ NULL,
dest,
NULL);
}
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index eb531af..92e8596 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -1596,7 +1596,7 @@ ImportForeignSchema(ImportForeignSchemaStmt *stmt)
/* Execute statement */
ProcessUtility((Node *) cstmt,
cmd,
- PROCESS_UTILITY_SUBCOMMAND, NULL,
+ PROCESS_UTILITY_SUBCOMMAND, NULL, NULL,
None_Receiver, NULL);
/* Be sure to advance the command counter between subcommands */
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index 6cddcbd..0c4ad16 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -401,7 +401,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);
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index cec37ce..70c7f49 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -52,7 +52,8 @@ static Datum build_regtype_array(Oid *param_types, int num_params);
* Implements the 'PREPARE' utility statement.
*/
void
-PrepareQuery(PrepareStmt *stmt, const char *queryString)
+PrepareQuery(PrepareStmt *stmt, const char *queryString,
+ QueryEnvironment *queryEnv)
{
CachedPlanSource *plansource;
Oid *argtypes = NULL;
@@ -114,7 +115,8 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString)
*/
query = parse_analyze_varparams((Node *) copyObject(stmt->query),
queryString,
- &argtypes, &nargs);
+ &argtypes, &nargs,
+ queryEnv);
/*
* Check that all parameter types were determined.
@@ -620,7 +622,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;
@@ -665,9 +668,11 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es,
PlannedStmt *pstmt = (PlannedStmt *) lfirst(p);
if (IsA(pstmt, PlannedStmt))
- ExplainOnePlan(pstmt, into, es, query_string, paramLI, NULL);
+ ExplainOnePlan(pstmt, into, es, query_string, paramLI, queryEnv,
+ NULL);
else
- ExplainOneUtility((Node *) pstmt, into, es, query_string, paramLI);
+ ExplainOneUtility((Node *) pstmt, into, es, query_string, paramLI,
+ queryEnv);
/* No need for CommandCounterIncrement, as ExplainOnePlan did it */
diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c
index a60ceb8..5238271 100644
--- a/src/backend/commands/schemacmds.c
+++ b/src/backend/commands/schemacmds.c
@@ -178,6 +178,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
queryString,
PROCESS_UTILITY_SUBCOMMAND,
NULL,
+ NULL,
None_Receiver,
NULL);
/* make sure later steps can see the object created here */
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 1c264b7..0fcb5f3 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -1162,7 +1162,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
/* ... and execute it */
ProcessUtility((Node *) atstmt,
"(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 */
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index 325a810..5f62809 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -400,7 +400,7 @@ DefineView(ViewStmt *stmt, const char *queryString)
* this ensures we don't corrupt a prepared statement, for example.
*/
viewParse = parse_analyze((Node *) copyObject(stmt->query),
- queryString, NULL, 0);
+ queryString, NULL, 0, NULL);
/*
* The grammar should ensure that the result is a single SELECT Query.
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 51edd4c..c760271 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -24,6 +24,7 @@ OBJS = execAmi.o execCurrent.o execGrouping.o execIndexing.o execJunk.o \
nodeNestloop.o nodeFunctionscan.o nodeRecursiveunion.o nodeResult.o \
nodeSamplescan.o nodeSeqscan.o nodeSetOp.o nodeSort.o nodeUnique.o \
nodeValuesscan.o nodeCtescan.o nodeWorktablescan.o \
+ nodeSpirelationscan.o \
nodeGroup.o nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o \
nodeForeignscan.o nodeWindowAgg.o tstoreReceiver.o tqueue.o spi.o
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index 2587ef7..d5b57ce 100644
--- a/src/backend/executor/execAmi.c
+++ b/src/backend/executor/execAmi.c
@@ -45,6 +45,7 @@
#include "executor/nodeSeqscan.h"
#include "executor/nodeSetOp.h"
#include "executor/nodeSort.h"
+#include "executor/nodeSpirelationscan.h"
#include "executor/nodeSubplan.h"
#include "executor/nodeSubqueryscan.h"
#include "executor/nodeTidscan.h"
@@ -202,6 +203,10 @@ ExecReScan(PlanState *node)
ExecReScanCteScan((CteScanState *) node);
break;
+ case T_SpiRelationScanState:
+ ExecReScanSpiRelationScan((SpiRelationScanState *) node);
+ break;
+
case T_WorkTableScanState:
ExecReScanWorkTableScan((WorkTableScanState *) node);
break;
@@ -581,6 +586,7 @@ ExecMaterializesOutput(NodeTag plantype)
case T_Material:
case T_FunctionScan:
case T_CteScan:
+ case T_SpiRelationScan:
case T_WorkTableScan:
case T_Sort:
return true;
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 32bb3f9..9d5cedc 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -187,6 +187,9 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
estate->es_param_exec_vals = (ParamExecData *)
palloc0(queryDesc->plannedstmt->nParamExec * sizeof(ParamExecData));
+ if (queryDesc->queryEnv)
+ estate->es_spi_relations = queryDesc->queryEnv->spi_relations;
+
/*
* If non-read-only query, set the command ID to mark output tuples with
*/
diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c
index 5aa6f02..30758d6 100644
--- a/src/backend/executor/execParallel.c
+++ b/src/backend/executor/execParallel.c
@@ -627,7 +627,7 @@ ExecParallelGetQueryDesc(shm_toc *toc, DestReceiver *receiver,
return CreateQueryDesc(pstmt,
"",
GetActiveSnapshot(), InvalidSnapshot,
- receiver, paramLI, instrument_options);
+ receiver, paramLI, NULL, instrument_options);
}
/*
diff --git a/src/backend/executor/execProcnode.c b/src/backend/executor/execProcnode.c
index 554244f..dc894a8 100644
--- a/src/backend/executor/execProcnode.c
+++ b/src/backend/executor/execProcnode.c
@@ -107,6 +107,7 @@
#include "executor/nodeSeqscan.h"
#include "executor/nodeSetOp.h"
#include "executor/nodeSort.h"
+#include "executor/nodeSpirelationscan.h"
#include "executor/nodeSubplan.h"
#include "executor/nodeSubqueryscan.h"
#include "executor/nodeTidscan.h"
@@ -243,6 +244,11 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
estate, eflags);
break;
+ case T_SpiRelationScan:
+ result = (PlanState *) ExecInitSpiRelationScan((SpiRelationScan *) node,
+ estate, eflags);
+ break;
+
case T_WorkTableScan:
result = (PlanState *) ExecInitWorkTableScan((WorkTableScan *) node,
estate, eflags);
@@ -457,6 +463,10 @@ ExecProcNode(PlanState *node)
result = ExecCteScan((CteScanState *) node);
break;
+ case T_SpiRelationScanState:
+ result = ExecSpiRelationScan((SpiRelationScanState *) node);
+ break;
+
case T_WorkTableScanState:
result = ExecWorkTableScan((WorkTableScanState *) node);
break;
@@ -709,6 +719,10 @@ ExecEndNode(PlanState *node)
ExecEndCteScan((CteScanState *) node);
break;
+ case T_SpiRelationScanState:
+ ExecEndSpiRelationScan((SpiRelationScanState *) node);
+ break;
+
case T_WorkTableScanState:
ExecEndWorkTableScan((WorkTableScanState *) node);
break;
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index a3bcb10..d809809 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -115,6 +115,8 @@ CreateExecutorState(void)
estate->es_param_list_info = NULL;
estate->es_param_exec_vals = NULL;
+ estate->es_spi_relations = NULL;
+
estate->es_query_cxt = qcontext;
estate->es_tupleTable = NIL;
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 470db5b..2a199cd 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -701,7 +701,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));
@@ -798,13 +799,16 @@ postquel_start(execution_state *es, SQLFunctionCachePtr fcache)
GetActiveSnapshot(),
InvalidSnapshot,
dest,
- fcache->paramLI, 0);
+ fcache->paramLI,
+ es->qd ? es->qd->queryEnv : NULL,
+ 0);
else
es->qd = CreateUtilityQueryDesc(es->stmt,
fcache->src,
GetActiveSnapshot(),
dest,
- fcache->paramLI);
+ fcache->paramLI,
+ es->qd ? es->qd->queryEnv : NULL);
/* Utility commands don't need Executor. */
if (es->qd->utilitystmt == NULL)
@@ -844,6 +848,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 */
diff --git a/src/backend/executor/nodeSpirelationscan.c b/src/backend/executor/nodeSpirelationscan.c
new file mode 100644
index 0000000..5e2c4bc
--- /dev/null
+++ b/src/backend/executor/nodeSpirelationscan.c
@@ -0,0 +1,212 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeSpirelationscan.c
+ * routines to handle SpiRelationScan nodes.
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/executor/nodeSpirelationscan.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "executor/execdebug.h"
+#include "executor/nodeSpirelationscan.h"
+#include "miscadmin.h"
+
+static TupleTableSlot *SpiRelationScanNext(SpiRelationScanState *node);
+
+/* ----------------------------------------------------------------
+ * SpiRelationScanNext
+ *
+ * This is a workhorse for ExecSpiRelationScan
+ * ----------------------------------------------------------------
+ */
+static TupleTableSlot *
+SpiRelationScanNext(SpiRelationScanState *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;
+ tuplestore_select_read_pointer(node->table, node->readptr);
+ (void) tuplestore_gettupleslot(node->table, true, false, slot);
+ return slot;
+}
+
+/*
+ * SpiRelationScanRecheck -- access method routine to recheck a tuple in
+ * EvalPlanQual
+ */
+static bool
+SpiRelationScanRecheck(SpiRelationScanState *node, TupleTableSlot *slot)
+{
+ /* nothing to check */
+ return true;
+}
+
+/* ----------------------------------------------------------------
+ * ExecSpiRelationScan(node)
+ *
+ * Scans the tuplestore sequentially and returns the next qualifying
+ * tuple. We call the ExecScan() routine and pass it the appropriate
+ * access method functions.
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *
+ExecSpiRelationScan(SpiRelationScanState *node)
+{
+ return ExecScan(&node->ss,
+ (ExecScanAccessMtd) SpiRelationScanNext,
+ (ExecScanRecheckMtd) SpiRelationScanRecheck);
+}
+
+
+/* ----------------------------------------------------------------
+ * ExecInitSpiRelationScan
+ * ----------------------------------------------------------------
+ */
+SpiRelationScanState *
+ExecInitSpiRelationScan(SpiRelationScan *node, EState *estate, int eflags)
+{
+ SpiRelationScanState *scanstate;
+ ListCell *lc;
+
+ /* check for unsupported flags */
+ Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
+
+ /*
+ * SpiRelationScan should not have any children.
+ */
+ Assert(outerPlan(node) == NULL);
+ Assert(innerPlan(node) == NULL);
+
+ /*
+ * create new SpiRelationScanState for node
+ */
+ scanstate = makeNode(SpiRelationScanState);
+ scanstate->ss.ps.plan = (Plan *) node;
+ scanstate->ss.ps.state = estate;
+
+ /* Find the SpiRelation by name to get the tuplestore + TupleDesc. */
+ foreach (lc, estate->es_spi_relations)
+ {
+ SpiRelation *sr = (SpiRelation *) lfirst(lc);
+
+ if (strcmp(sr->name, node->srname) == 0)
+ {
+ scanstate->table = sr->tuplestore;
+ scanstate->tupdesc = sr->tupdesc;
+ break;
+ }
+ }
+
+ /*
+ * We could fail to find it if you planned a query and then tried to run
+ * it when the SPI relation was no longer registered.
+ */
+ if (scanstate->table == NULL)
+ elog(ERROR, "cannot find SPI relation \"%s\"", node->srname);
+
+ scanstate->readptr = tuplestore_alloc_read_pointer(scanstate->table,
+ EXEC_FLAG_REWIND);
+
+ tuplestore_select_read_pointer(scanstate->table, scanstate->readptr);
+
+ /*
+ * The new read pointer copies its position from read pointer 0, which
+ * could be anywhere, so explicitly rewind it.
+ */
+ tuplestore_rescan(scanstate->table);
+
+ /*
+ * Miscellaneous initialization
+ *
+ * create expression context for node
+ */
+ ExecAssignExprContext(estate, &scanstate->ss.ps);
+
+ /*
+ * initialize child expressions
+ */
+ scanstate->ss.ps.targetlist = (List *)
+ ExecInitExpr((Expr *) node->scan.plan.targetlist,
+ (PlanState *) scanstate);
+ scanstate->ss.ps.qual = (List *)
+ ExecInitExpr((Expr *) 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);
+
+ scanstate->ss.ps.ps_TupFromTlist = false;
+
+ return scanstate;
+}
+
+/* ----------------------------------------------------------------
+ * ExecEndSpiRelationScan
+ *
+ * frees any storage allocated through C routines.
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndSpiRelationScan(SpiRelationScanState *node)
+{
+ /*
+ * Free exprcontext
+ */
+ ExecFreeExprContext(&node->ss.ps);
+
+ /*
+ * clean out the tuple table
+ */
+ ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
+ ExecClearTuple(node->ss.ss_ScanTupleSlot);
+}
+
+/* ----------------------------------------------------------------
+ * ExecReScanSpiRelationScan
+ *
+ * Rescans the relation.
+ * ----------------------------------------------------------------
+ */
+void
+ExecReScanSpiRelationScan(SpiRelationScanState *node)
+{
+ Tuplestorestate *tuplestorestate = node->table;
+
+ ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
+
+ ExecScanReScan(&node->ss);
+
+ /*
+ * Rewind my own pointer.
+ */
+ tuplestore_select_read_pointer(tuplestorestate, node->readptr);
+ tuplestore_rescan(tuplestorestate);
+}
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 80fc4c4..d465c9d 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -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
@@ -1307,6 +1308,9 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
MemoryContextSwitchTo(oldcontext);
}
+ /* Make the SPI connection's QueryEnvironment visible to this portal. */
+ portal->queryEnv = _SPI_current->queryEnv;
+
/*
* Start portal execution.
*/
@@ -1532,6 +1536,10 @@ SPI_result_code_string(int code)
return "SPI_ERROR_NOOUTFUNC";
case SPI_ERROR_TYPUNKNOWN:
return "SPI_ERROR_TYPUNKNOWN";
+ case SPI_ERROR_DUPLICATE:
+ return "SPI_ERROR_DUPLICATE";
+ case SPI_ERROR_NOT_FOUND:
+ return "SPI_ERROR_NOT_FOUND";
case SPI_OK_CONNECT:
return "SPI_OK_CONNECT";
case SPI_OK_FINISH:
@@ -1560,6 +1568,10 @@ SPI_result_code_string(int code)
return "SPI_OK_UPDATE_RETURNING";
case SPI_OK_REWRITTEN:
return "SPI_OK_REWRITTEN";
+ case SPI_OK_REGISTER:
+ return "SPI_OK_REGISTER";
+ case SPI_OK_UNREGISTER:
+ return "SPI_OK_UNREGISTER";
}
/* Unrecognized code ... return something useful ... */
sprintf(buf, "Unrecognized SPI code %d", code);
@@ -1779,14 +1791,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 +1989,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 */
@@ -2089,7 +2105,9 @@ _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);
@@ -2102,6 +2120,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
plansource->query_string,
PROCESS_UTILITY_QUERY,
paramLI,
+ _SPI_current->queryEnv,
dest,
completionTag);
@@ -2628,3 +2647,105 @@ _SPI_save_plan(SPIPlanPtr plan)
return newplan;
}
+
+/*
+ * Register a named tuplestore for use by the planner and executor on
+ * subsequent calls using this SPI connection.
+ */
+int
+SPI_register_relation(const char *name, TupleDesc tupdesc,
+ int tuples, Tuplestorestate *tuplestore)
+{
+ MemoryContext oldcxt;
+ SpiRelation *sr = NULL;
+ ListCell *lc;
+ int res;
+
+ if (name == NULL || tupdesc == NULL || tuplestore == NULL)
+ return SPI_ERROR_ARGUMENT;
+
+ /* Make sure we are connected. */
+ res = _SPI_begin_call(false);
+ if (res < 0)
+ return res;
+
+ /* Use procedure-duration memory for QueryEnvironment and contents. */
+ oldcxt = _SPI_procmem();
+
+ /* Create a QueryEnvironment if we don't have one already. */
+ if (_SPI_current->queryEnv == NULL)
+ _SPI_current->queryEnv =
+ (QueryEnvironment *) palloc0(sizeof(QueryEnvironment));
+
+ /* Check if this name is already registered. */
+ foreach(lc, _SPI_current->queryEnv->spi_relations)
+ {
+ sr = (SpiRelation *) lfirst(lc);
+
+ if (strcmp(sr->name, name) == 0)
+ break;
+ sr = NULL;
+ }
+
+ /* Register if we didn't find it. */
+ if (sr == NULL)
+ {
+ sr = (SpiRelation *) palloc0(sizeof(SpiRelation));
+ sr->name = pstrdup(name);
+ sr->tupdesc = CreateTupleDescCopy(tupdesc);
+ sr->tuples = tuples;
+ sr->tuplestore = tuplestore;
+ _SPI_current->queryEnv->spi_relations =
+ lappend(_SPI_current->queryEnv->spi_relations, sr);
+ res = SPI_OK_REGISTER;
+ }
+ else
+ res = SPI_ERROR_DUPLICATE;
+
+ _SPI_end_call(false);
+
+ MemoryContextSwitchTo(oldcxt);
+
+ return res;
+}
+
+/*
+ * Unregister a named tuplestore by name. This will probably be a rarely used
+ * function, since SPI_finish will clear it automatically.
+ */
+int
+SPI_unregister_relation(const char *name)
+{
+ ListCell *prev = NULL;
+ ListCell *lc;
+ int res = SPI_ERROR_NOT_FOUND;
+
+ /* Make sure we are connected. */
+ res = _SPI_begin_call(false);
+ if (res < 0)
+ return res;
+
+ /* Search for the relation by name. */
+ if (_SPI_current->queryEnv != NULL)
+ {
+ foreach (lc, _SPI_current->queryEnv->spi_relations)
+ {
+ SpiRelation *sr;
+
+ sr = (SpiRelation *) lfirst(lc);
+ if (strcmp(sr->name, name) == 0)
+ {
+ _SPI_current->queryEnv->spi_relations =
+ list_delete_cell(_SPI_current->queryEnv->spi_relations,
+ lc, prev);
+ res = SPI_OK_UNREGISTER;
+ break;
+ }
+ prev = lc;
+ }
+ }
+
+ _SPI_end_call(false);
+
+ return res;
+}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 04e49b7..04dfb81 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -613,6 +613,27 @@ _copyCteScan(const CteScan *from)
}
/*
+ * _copySpiRelationScan
+ */
+static SpiRelationScan *
+_copySpiRelationScan(const SpiRelationScan *from)
+{
+ SpiRelationScan *newnode = makeNode(SpiRelationScan);
+
+ /*
+ * copy node superclass fields
+ */
+ CopyScanFields((const Scan *) from, (Scan *) newnode);
+
+ /*
+ * copy remainder of node
+ */
+ COPY_STRING_FIELD(srname);
+
+ return newnode;
+}
+
+/*
* _copyWorkTableScan
*/
static WorkTableScan *
@@ -2156,6 +2177,7 @@ _copyRangeTblEntry(const RangeTblEntry *from)
COPY_NODE_FIELD(ctecoltypes);
COPY_NODE_FIELD(ctecoltypmods);
COPY_NODE_FIELD(ctecolcollations);
+ COPY_STRING_FIELD(srname);
COPY_NODE_FIELD(alias);
COPY_NODE_FIELD(eref);
COPY_SCALAR_FIELD(lateral);
@@ -4389,6 +4411,9 @@ copyObject(const void *from)
case T_CteScan:
retval = _copyCteScan(from);
break;
+ case T_SpiRelationScan:
+ retval = _copySpiRelationScan(from);
+ break;
case T_WorkTableScan:
retval = _copyWorkTableScan(from);
break;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 3997441..b4a25c9 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -2302,6 +2302,7 @@ range_table_walker(List *rtable,
return true;
break;
case RTE_CTE:
+ case RTE_SPIRELATION:
/* nothing to do */
break;
case RTE_SUBQUERY:
@@ -3098,6 +3099,7 @@ range_table_mutator(List *rtable,
/* we don't bother to copy eref, aliases, etc; OK? */
break;
case RTE_CTE:
+ case RTE_SPIRELATION:
/* nothing to do */
break;
case RTE_SUBQUERY:
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 748b687..9e923ae 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -577,6 +577,16 @@ _outCteScan(StringInfo str, const CteScan *node)
}
static void
+_outSpiRelationScan(StringInfo str, const SpiRelationScan *node)
+{
+ WRITE_NODE_TYPE("SPIRELATIONSCAN");
+
+ _outScanInfo(str, (const Scan *) node);
+
+ WRITE_STRING_FIELD(srname);
+}
+
+static void
_outWorkTableScan(StringInfo str, const WorkTableScan *node)
{
WRITE_NODE_TYPE("WORKTABLESCAN");
@@ -2849,6 +2859,13 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
WRITE_NODE_FIELD(ctecoltypmods);
WRITE_NODE_FIELD(ctecolcollations);
break;
+ case RTE_SPIRELATION:
+ WRITE_STRING_FIELD(srname);
+ WRITE_OID_FIELD(relid);
+ WRITE_NODE_FIELD(ctecoltypes);
+ WRITE_NODE_FIELD(ctecoltypmods);
+ WRITE_NODE_FIELD(ctecolcollations);
+ break;
default:
elog(ERROR, "unrecognized RTE kind: %d", (int) node->rtekind);
break;
@@ -3368,6 +3385,9 @@ outNode(StringInfo str, const void *obj)
case T_CteScan:
_outCteScan(str, obj);
break;
+ case T_SpiRelationScan:
+ _outSpiRelationScan(str, obj);
+ break;
case T_WorkTableScan:
_outWorkTableScan(str, obj);
break;
diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c
index a1f2941..53a6164 100644
--- a/src/backend/nodes/print.c
+++ b/src/backend/nodes/print.c
@@ -287,6 +287,10 @@ print_rt(const List *rtable)
printf("%d\t%s\t[cte]",
i, rte->eref->aliasname);
break;
+ case RTE_SPIRELATION:
+ printf("%d\t%s\t[spirelation]",
+ i, rte->eref->aliasname);
+ break;
default:
printf("%d\t%s\t[unknown rtekind]",
i, rte->eref->aliasname);
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 917e6c8..3eca76f 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -1324,6 +1324,13 @@ _readRangeTblEntry(void)
READ_NODE_FIELD(ctecoltypmods);
READ_NODE_FIELD(ctecolcollations);
break;
+ case RTE_SPIRELATION:
+ READ_STRING_FIELD(srname);
+ READ_OID_FIELD(relid);
+ READ_NODE_FIELD(ctecoltypes);
+ READ_NODE_FIELD(ctecoltypmods);
+ READ_NODE_FIELD(ctecolcollations);
+ break;
default:
elog(ERROR, "unrecognized RTE kind: %d",
(int) local_node->rtekind);
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index e42ef98..6e9b112 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -107,6 +107,8 @@ static void set_values_pathlist(PlannerInfo *root, RelOptInfo *rel,
RangeTblEntry *rte);
static void set_cte_pathlist(PlannerInfo *root, RelOptInfo *rel,
RangeTblEntry *rte);
+static void set_spirelation_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);
@@ -379,6 +381,9 @@ set_rel_size(PlannerInfo *root, RelOptInfo *rel,
else
set_cte_pathlist(root, rel, rte);
break;
+ case RTE_SPIRELATION:
+ set_spirelation_pathlist(root, rel, rte);
+ break;
default:
elog(ERROR, "unexpected rtekind: %d", (int) rel->rtekind);
break;
@@ -443,6 +448,9 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
case RTE_CTE:
/* CTE reference --- fully handled during set_rel_size */
break;
+ case RTE_SPIRELATION:
+ /* SPI relation reference --- fully handled during set_rel_size */
+ break;
default:
elog(ERROR, "unexpected rtekind: %d", (int) rel->rtekind);
break;
@@ -614,6 +622,13 @@ set_rel_consider_parallel(PlannerInfo *root, RelOptInfo *rel,
* executed only once.
*/
return;
+
+ case RTE_SPIRELATION:
+ /*
+ * SPI relation tuplestores cannot be shared, at least without
+ * more infrastructure to support that.
+ */
+ return;
}
/*
@@ -1948,6 +1963,35 @@ set_cte_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
}
/*
+ * set_spirelation_pathlist
+ * Build the (single) access path for an SPI relation RTE
+ *
+ * There's no need for a separate set_spirelation_size phase, since we don't
+ * support join-qual-parameterized paths for SPI relations.
+ */
+static void
+set_spirelation_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
+{
+ Relids required_outer;
+
+ /* Mark rel with estimated output rows, width, etc */
+ set_spirelation_size_estimates(root, rel);
+
+ /*
+ * We don't support pushing join clauses into the quals of an SPI relation
+ * 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_spirelationscan_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
*
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index e42895d..680e383 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -1383,6 +1383,44 @@ cost_ctescan(Path *path, PlannerInfo *root,
}
/*
+ * cost_spirelationscan
+ * Determines and returns the cost of scanning a tuplestore supplied
+ * by SPI client code.
+ */
+void
+cost_spirelationscan(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 SPI relations */
+ Assert(baserel->relid > 0);
+ Assert(baserel->rtekind == RTE_SPIRELATION);
+
+ /* 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,
* and also the estimated output size.
@@ -4463,6 +4501,38 @@ set_cte_size_estimates(PlannerInfo *root, RelOptInfo *rel, double cte_rows)
}
/*
+ * set_spirelation_size_estimates
+ * Set the size estimates for a base relation that is a SPI relation
+ * 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_spirelation_size_estimates(PlannerInfo *root, RelOptInfo *rel)
+{
+ RangeTblEntry *rte;
+
+ /* Should only be applied to base relations that are SPI relation refs */
+ Assert(rel->relid > 0);
+ rte = planner_rt_fetch(rel->relid, root);
+ Assert(rte->rtekind == RTE_SPIRELATION);
+
+ /*
+ * Use the esimate provided by the SPI client that provided this SPI
+ * relation. We could instead ask the tuplestore, but since the resulting
+ * plan may be reused for later queries, it seems better to let the user
+ * control the estimate.
+ */
+ rel->tuples = rte->srtuples;
+
+ /* 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.
*
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index ad49674..7c94e21 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -135,6 +135,8 @@ static ValuesScan *create_valuesscan_plan(PlannerInfo *root, Path *best_path,
List *tlist, List *scan_clauses);
static CteScan *create_ctescan_plan(PlannerInfo *root, Path *best_path,
List *tlist, List *scan_clauses);
+static SpiRelationScan *create_spirelationscan_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,
@@ -191,6 +193,8 @@ static ValuesScan *make_valuesscan(List *qptlist, List *qpqual,
Index scanrelid, List *values_lists);
static CteScan *make_ctescan(List *qptlist, List *qpqual,
Index scanrelid, int ctePlanId, int cteParam);
+static SpiRelationScan *make_spirelationscan(List *qptlist, List *qpqual,
+ Index scanrelid, char *srname);
static WorkTableScan *make_worktablescan(List *qptlist, List *qpqual,
Index scanrelid, int wtParam);
static Append *make_append(List *appendplans, List *tlist);
@@ -356,6 +360,7 @@ create_plan_recurse(PlannerInfo *root, Path *best_path, int flags)
case T_ValuesScan:
case T_CteScan:
case T_WorkTableScan:
+ case T_SpiRelationScan:
case T_ForeignScan:
case T_CustomScan:
plan = create_scan_plan(root, best_path, flags);
@@ -644,6 +649,13 @@ create_scan_plan(PlannerInfo *root, Path *best_path, int flags)
scan_clauses);
break;
+ case T_SpiRelationScan:
+ plan = (Plan *) create_spirelationscan_plan(root,
+ best_path,
+ tlist,
+ scan_clauses);
+ break;
+
case T_WorkTableScan:
plan = (Plan *) create_worktablescan_plan(root,
best_path,
@@ -3124,6 +3136,45 @@ create_ctescan_plan(PlannerInfo *root, Path *best_path,
}
/*
+ * create_spirelationscan_plan
+ * Returns an SpiRelationScan plan for the base relation scanned by
+ * 'best_path' with restriction clauses 'scan_clauses' and targetlist
+ * 'tlist'.
+ */
+static SpiRelationScan *
+create_spirelationscan_plan(PlannerInfo *root, Path *best_path,
+ List *tlist, List *scan_clauses)
+{
+ SpiRelationScan *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_SPIRELATION);
+
+ /* 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_spirelationscan(tlist, scan_clauses, scan_relid,
+ rte->srname);
+
+ 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'
* with restriction clauses 'scan_clauses' and targetlist 'tlist'.
@@ -4887,6 +4938,26 @@ make_ctescan(List *qptlist,
return node;
}
+static SpiRelationScan *
+make_spirelationscan(List *qptlist,
+ List *qpqual,
+ Index scanrelid,
+ char *srname)
+{
+ SpiRelationScan *node = makeNode(SpiRelationScan);
+ 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->srname = srname;
+
+ return node;
+}
+
static WorkTableScan *
make_worktablescan(List *qptlist,
List *qpqual,
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index d91bc3b..935af31 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -580,6 +580,17 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
fix_scan_list(root, splan->scan.plan.qual, rtoffset);
}
break;
+ case T_SpiRelationScan:
+ {
+ SpiRelationScan *splan = (SpiRelationScan *) 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;
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 263ba45..bd51c7b 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -2459,6 +2459,10 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
context.paramids = bms_add_members(context.paramids, scan_params);
break;
+ case T_SpiRelationScan:
+ context.paramids = bms_add_members(context.paramids, scan_params);
+ break;
+
case T_ForeignScan:
{
ForeignScan *fscan = (ForeignScan *) plan;
diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c
index b681230..114c319 100644
--- a/src/backend/optimizer/prep/prepjointree.c
+++ b/src/backend/optimizer/prep/prepjointree.c
@@ -1121,6 +1121,7 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
break;
case RTE_JOIN:
case RTE_CTE:
+ case RTE_SPIRELATION:
/* these can't contain any lateral references */
break;
}
@@ -1973,6 +1974,7 @@ replace_vars_in_jointree(Node *jtnode,
break;
case RTE_JOIN:
case RTE_CTE:
+ case RTE_SPIRELATION:
/* these shouldn't be marked LATERAL */
Assert(false);
break;
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 1688310..8c336a2 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -4974,7 +4974,8 @@ 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);
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 6d3ccfd..190c432 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -1805,6 +1805,31 @@ create_ctescan_path(PlannerInfo *root, RelOptInfo *rel, Relids required_outer)
}
/*
+ * create_spirelationscan_path
+ * Creates a path corresponding to a scan of tuples supplied by SPI code,
+ * returning the pathnode.
+ */
+Path *
+create_spirelationscan_path(PlannerInfo *root, RelOptInfo *rel, Relids required_outer)
+{
+ Path *pathnode = makeNode(Path);
+
+ pathnode->pathtype = T_SpiRelationScan;
+ 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_spirelationscan(pathnode, root, rel, pathnode->param_info);
+
+ return pathnode;
+}
+
+/*
* create_worktablescan_path
* Creates a path corresponding to a scan of a self-reference CTE,
* returning the pathnode.
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index ad07baa..f5935c1 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -1360,7 +1360,8 @@ relation_excluded_by_constraints(PlannerInfo *root,
*
* We also support building a "physical" tlist for subqueries, functions,
* values lists, and CTEs, since the same optimization can occur in
- * SubqueryScan, FunctionScan, ValuesScan, CteScan, and WorkTableScan nodes.
+ * SubqueryScan, FunctionScan, ValuesScan, CteScan, SpiRelationScan, and
+ * WorkTableScan nodes.
*/
List *
build_physical_tlist(PlannerInfo *root, RelOptInfo *rel)
@@ -1434,6 +1435,7 @@ build_physical_tlist(PlannerInfo *root, RelOptInfo *rel)
case RTE_FUNCTION:
case RTE_VALUES:
case RTE_CTE:
+ case RTE_SPIRELATION:
/* Not all of these can have dropped cols, but share code anyway */
expandRTE(rte, varno, 0, -1, true /* include dropped */ ,
NULL, &colvars);
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index deef560..16aadc7 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -149,6 +149,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
case RTE_FUNCTION:
case RTE_VALUES:
case RTE_CTE:
+ case RTE_SPIRELATION:
/*
* Subquery, function, or values list --- set up attr range and
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 6901e08..79c4644 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -93,7 +93,8 @@ static bool test_raw_expression_coverage(Node *node, void *context);
*/
Query *
parse_analyze(Node *parseTree, const char *sourceText,
- Oid *paramTypes, int numParams)
+ Oid *paramTypes, int numParams,
+ QueryEnvironment *queryEnv)
{
ParseState *pstate = make_parsestate(NULL);
Query *query;
@@ -105,6 +106,9 @@ parse_analyze(Node *parseTree, const char *sourceText,
if (numParams > 0)
parse_fixed_parameters(pstate, paramTypes, numParams);
+ if (queryEnv)
+ pstate->p_spirelationnamespace = queryEnv->spi_relations;
+
query = transformTopLevelStmt(pstate, parseTree);
if (post_parse_analyze_hook)
@@ -124,7 +128,8 @@ parse_analyze(Node *parseTree, const char *sourceText,
*/
Query *
parse_analyze_varparams(Node *parseTree, const char *sourceText,
- Oid **paramTypes, int *numParams)
+ Oid **paramTypes, int *numParams,
+ QueryEnvironment *queryEnv)
{
ParseState *pstate = make_parsestate(NULL);
Query *query;
@@ -135,6 +140,9 @@ parse_analyze_varparams(Node *parseTree, const char *sourceText,
parse_variable_parameters(pstate, paramTypes, numParams);
+ if (queryEnv)
+ pstate->p_spirelationnamespace = queryEnv->spi_relations;
+
query = transformTopLevelStmt(pstate, parseTree);
/* make sure all is well with parameter types */
@@ -2716,6 +2724,15 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc,
LCS_asString(lc->strength)),
parser_errposition(pstate, thisrel->location)));
break;
+ case RTE_SPIRELATION:
+ 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 an SPI relation",
+ LCS_asString(lc->strength)),
+ parser_errposition(pstate, thisrel->location)));
+ break;
default:
elog(ERROR, "unrecognized RTE type: %d",
(int) rte->rtekind);
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 751de4b..67bedb4 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -458,6 +458,21 @@ transformCTEReference(ParseState *pstate, RangeVar *r,
}
/*
+ * transformSpiRelationReference --- transform a RangeVar that references an
+ * SPI relation
+ */
+static RangeTblEntry *
+transformSpiRelationReference(ParseState *pstate, RangeVar *r,
+ SpiRelation *sr)
+{
+ RangeTblEntry *rte;
+
+ rte = addRangeTableEntryForSpiRelation(pstate, sr, r, true);
+
+ return rte;
+}
+
+/*
* transformRangeSubselect --- transform a sub-SELECT appearing in FROM
*/
static RangeTblEntry *
@@ -850,7 +865,7 @@ transformFromClauseItem(ParseState *pstate, Node *n,
{
if (IsA(n, RangeVar))
{
- /* Plain relation reference, or perhaps a CTE reference */
+ /* Plain relation reference, or perhaps a CTE or SPI reference */
RangeVar *rv = (RangeVar *) n;
RangeTblRef *rtr;
RangeTblEntry *rte = NULL;
@@ -867,7 +882,18 @@ transformFromClauseItem(ParseState *pstate, Node *n,
rte = transformCTEReference(pstate, rv, cte, levelsup);
}
- /* if not found as a CTE, must be a table reference */
+ /* or it might be an SPI relation reference */
+ if (!rte && !rv->schemaname)
+ {
+ SpiRelation *sr;
+
+ sr = scanNameSpaceForSpiRelation(pstate, rv->relname);
+
+ if (sr)
+ rte = transformSpiRelationReference(pstate, rv, sr);
+ }
+
+ /* if not found above, must be a table reference */
if (!rte)
rte = transformTableEntry(pstate, rv);
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 1e3ecbc..3fa2d56 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -281,6 +281,27 @@ isFutureCTE(ParseState *pstate, const char *refname)
}
/*
+ * Search for an SPI relation.
+ */
+SpiRelation *
+scanNameSpaceForSpiRelation(ParseState *pstate, const char *refname)
+{
+ for (; pstate != NULL; pstate = pstate->parentParseState)
+ {
+ ListCell *lc;
+
+ foreach(lc, pstate->p_spirelationnamespace)
+ {
+ SpiRelation *sr = (SpiRelation *) lfirst(lc);
+
+ if (strcmp(sr->name, refname) == 0)
+ return sr;
+ }
+ }
+ return NULL;
+}
+
+/*
* searchRangeTableForRel
* See if any RangeTblEntry could possibly match the RangeVar.
* If so, return a pointer to the RangeTblEntry; else return NULL.
@@ -301,6 +322,7 @@ searchRangeTableForRel(ParseState *pstate, RangeVar *relation)
const char *refname = relation->relname;
Oid relId = InvalidOid;
CommonTableExpr *cte = NULL;
+ bool is_spi_relation = false;
Index ctelevelsup = 0;
Index levelsup;
@@ -317,8 +339,14 @@ searchRangeTableForRel(ParseState *pstate, RangeVar *relation)
* unlocked.
*/
if (!relation->schemaname)
+ {
cte = scanNameSpaceForCTE(pstate, refname, &ctelevelsup);
- if (!cte)
+ if (!cte)
+ is_spi_relation =
+ scanNameSpaceForSpiRelation(pstate, refname) != NULL;
+ }
+
+ if (!cte && !is_spi_relation)
relId = RangeVarGetRelid(relation, NoLock, true);
/* Now look for RTEs matching either the relation/CTE or the alias */
@@ -341,6 +369,9 @@ searchRangeTableForRel(ParseState *pstate, RangeVar *relation)
rte->ctelevelsup + levelsup == ctelevelsup &&
strcmp(rte->ctename, refname) == 0)
return rte;
+ if (rte->rtekind == RTE_SPIRELATION &&
+ strcmp(rte->srname, refname) == 0)
+ return rte;
if (strcmp(rte->eref->aliasname, refname) == 0)
return rte;
}
@@ -1873,6 +1904,81 @@ addRangeTableEntryForCTE(ParseState *pstate,
return rte;
}
+/*
+ * Add an entry for an SPI relation reference to the pstate's range
+ * table (p_rtable).
+ *
+ * This is much like addRangeTableEntry() except that it makes a RTE
+ * representing data to be supplied by a client of the SPI interface,
+ * including PLs such as plpgsql.
+ */
+RangeTblEntry *
+addRangeTableEntryForSpiRelation(ParseState *pstate,
+ SpiRelation *sr,
+ RangeVar *rv,
+ bool inFromCl)
+{
+ RangeTblEntry *rte = makeNode(RangeTblEntry);
+ Alias *alias = rv->alias;
+ char *refname = alias ? alias->aliasname : rv->relname;
+ int attno;
+ TupleDesc tupdesc = sr->tupdesc;
+
+ Assert(tupdesc != NULL);
+
+ rte->rtekind = RTE_SPIRELATION;
+
+ /*
+ * Build the list of effective column names using user-supplied aliases
+ * and/or actual column names. Also build the cannibalized fields.
+ */
+ rte->eref = makeAlias(refname, NIL);
+ buildRelationAliases(tupdesc, alias, rte->eref);
+ rte->srname = rv->relname;
+ rte->srtuples = sr->tuples;
+
+ rte->ctecoltypes = NIL;
+ rte->ctecoltypmods = NIL;
+ rte->ctecolcollations = 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->ctecoltypes =
+ lappend_oid(rte->ctecoltypes,
+ tupdesc->attrs[attno - 1]->atttypid);
+ rte->ctecoltypmods =
+ lappend_int(rte->ctecoltypmods,
+ tupdesc->attrs[attno - 1]->atttypmod);
+ rte->ctecolcollations =
+ lappend_oid(rte->ctecolcollations,
+ tupdesc->attrs[attno - 1]->attcollation);
+ }
+
+ /*
+ * Set flags and access permissions.
+ *
+ * Tuplestores are never checked for access rights.
+ */
+ rte->lateral = false;
+ rte->inh = false; /* never true for tuplestores */
+ 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?
@@ -2263,6 +2369,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
}
break;
case RTE_CTE:
+ case RTE_SPIRELATION:
{
ListCell *aliasp_item = list_head(rte->eref->colnames);
ListCell *lct;
@@ -2686,8 +2793,10 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
}
break;
case RTE_CTE:
+ case RTE_SPIRELATION:
{
/* CTE RTE --- get type info from lists in the RTE */
+ /* Also used by SPI relation RTE */
Assert(attnum > 0 && attnum <= list_length(rte->ctecoltypes));
*vartype = list_nth_oid(rte->ctecoltypes, attnum - 1);
*vartypmod = list_nth_int(rte->ctecoltypmods, attnum - 1);
@@ -2735,6 +2844,15 @@ get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum)
/* Subselect, Values, CTE RTEs never have dropped columns */
result = false;
break;
+ case RTE_SPIRELATION:
+ /*
+ * 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.
+ */
+ Assert(rte->srname != NULL);
+ result = (list_nth(rte->ctecoltypes, attnum - 1) != InvalidOid);
+ break;
case RTE_JOIN:
{
/*
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index a76c33f..b836097 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -347,6 +347,7 @@ markTargetListOrigin(ParseState *pstate, TargetEntry *tle,
break;
case RTE_FUNCTION:
case RTE_VALUES:
+ case RTE_SPIRELATION:
/* not a simple relation, leave it unmarked */
break;
case RTE_CTE:
@@ -1455,6 +1456,7 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup)
{
case RTE_RELATION:
case RTE_VALUES:
+ case RTE_SPIRELATION:
/*
* This case should not occur: a column of a table or values list
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index cc84754..d096cdd 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -642,7 +642,8 @@ pg_parse_query(const char *query_string)
*/
List *
pg_analyze_and_rewrite(Node *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(Node *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(Node *parsetree,
const char *query_string,
ParserSetupHook parserSetup,
- void *parserSetupArg)
+ void *parserSetupArg,
+ QueryEnvironment *queryEnv)
{
ParseState *pstate;
Query *query;
@@ -699,6 +702,9 @@ pg_analyze_and_rewrite_params(Node *parsetree,
pstate->p_sourcetext = query_string;
(*parserSetup) (pstate, parserSetupArg);
+ if (queryEnv)
+ pstate->p_spirelationnamespace = queryEnv->spi_relations;
+
query = transformTopLevelStmt(pstate, parsetree);
if (post_parse_analyze_hook)
@@ -1017,7 +1023,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);
@@ -1328,7 +1334,8 @@ exec_parse_message(const char *query_string, /* string to execute */
query = parse_analyze_varparams(raw_parse_tree,
query_string,
¶mTypes,
- &numParams);
+ &numParams,
+ NULL);
/*
* Check all parameter types got determined.
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 5443d0b..3a31bf3 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -36,6 +36,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);
@@ -67,6 +68,7 @@ CreateQueryDesc(PlannedStmt *plannedstmt,
Snapshot crosscheck_snapshot,
DestReceiver *dest,
ParamListInfo params,
+ QueryEnvironment *queryEnv,
int instrument_options)
{
QueryDesc *qd = (QueryDesc *) palloc(sizeof(QueryDesc));
@@ -80,6 +82,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? */
@@ -100,7 +103,8 @@ CreateUtilityQueryDesc(Node *utilitystmt,
const char *sourceText,
Snapshot snapshot,
DestReceiver *dest,
- ParamListInfo params)
+ ParamListInfo params,
+ QueryEnvironment *queryEnv)
{
QueryDesc *qd = (QueryDesc *) palloc(sizeof(QueryDesc));
@@ -112,6 +116,7 @@ CreateUtilityQueryDesc(Node *utilitystmt,
qd->crosscheck_snapshot = InvalidSnapshot; /* RI check snapshot */
qd->dest = dest; /* output dest */
qd->params = params; /* parameter values passed into query */
+ qd->queryEnv = queryEnv;
qd->instrument_options = false; /* uninteresting for utilities */
/* null these fields until set by ExecutorStart */
@@ -162,6 +167,7 @@ static void
ProcessQuery(PlannedStmt *plan,
const char *sourceText,
ParamListInfo params,
+ QueryEnvironment *queryEnv,
DestReceiver *dest,
char *completionTag)
{
@@ -172,7 +178,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
@@ -519,6 +525,7 @@ PortalStart(Portal portal, ParamListInfo params,
InvalidSnapshot,
None_Receiver,
params,
+ portal->queryEnv,
0);
/*
@@ -1190,6 +1197,7 @@ PortalRunUtility(Portal portal, Node *utilityStmt,
portal->sourceText,
isTopLevel ? PROCESS_UTILITY_TOPLEVEL : PROCESS_UTILITY_QUERY,
portal->portalParams,
+ portal->queryEnv,
dest,
completionTag);
@@ -1299,6 +1307,7 @@ PortalRunMulti(Portal portal,
ProcessQuery(pstmt,
portal->sourceText,
portal->portalParams,
+ portal->queryEnv,
dest, completionTag);
}
else
@@ -1307,6 +1316,7 @@ PortalRunMulti(Portal portal,
ProcessQuery(pstmt,
portal->sourceText,
portal->portalParams,
+ portal->queryEnv,
altdest, NULL);
}
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index f50ce40..3618f7c 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -76,6 +76,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);
@@ -319,6 +320,7 @@ ProcessUtility(Node *parsetree,
const char *queryString,
ProcessUtilityContext context,
ParamListInfo params,
+ QueryEnvironment *queryEnv,
DestReceiver *dest,
char *completionTag)
{
@@ -331,11 +333,11 @@ ProcessUtility(Node *parsetree,
*/
if (ProcessUtility_hook)
(*ProcessUtility_hook) (parsetree, queryString,
- context, params,
+ context, params, queryEnv,
dest, completionTag);
else
standard_ProcessUtility(parsetree, queryString,
- context, params,
+ context, params, queryEnv,
dest, completionTag);
}
@@ -355,6 +357,7 @@ standard_ProcessUtility(Node *parsetree,
const char *queryString,
ProcessUtilityContext context,
ParamListInfo params,
+ QueryEnvironment *queryEnv,
DestReceiver *dest,
char *completionTag)
{
@@ -554,7 +557,7 @@ standard_ProcessUtility(Node *parsetree,
case T_PrepareStmt:
CheckRestrictedOperation("PREPARE");
- PrepareQuery((PrepareStmt *) parsetree, queryString);
+ PrepareQuery((PrepareStmt *) parsetree, queryString, queryEnv);
break;
case T_ExecuteStmt:
@@ -662,7 +665,8 @@ standard_ProcessUtility(Node *parsetree,
break;
case T_ExplainStmt:
- ExplainQuery(pstate, (ExplainStmt *) parsetree, queryString, params, dest);
+ ExplainQuery(pstate, (ExplainStmt *) parsetree, queryString, params,
+ queryEnv, dest);
break;
case T_AlterSystemStmt:
@@ -809,7 +813,7 @@ standard_ProcessUtility(Node *parsetree,
if (EventTriggerSupportsGrantObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, parsetree, queryString,
- context, params,
+ context, params, queryEnv,
dest, completionTag);
else
ExecuteGrantStmt((GrantStmt *) parsetree);
@@ -822,7 +826,7 @@ standard_ProcessUtility(Node *parsetree,
if (EventTriggerSupportsObjectType(stmt->removeType))
ProcessUtilitySlow(pstate, parsetree, queryString,
- context, params,
+ context, params, queryEnv,
dest, completionTag);
else
ExecDropStmt(stmt, isTopLevel);
@@ -835,7 +839,7 @@ standard_ProcessUtility(Node *parsetree,
if (EventTriggerSupportsObjectType(stmt->renameType))
ProcessUtilitySlow(pstate, parsetree, queryString,
- context, params,
+ context, params, queryEnv,
dest, completionTag);
else
ExecRenameStmt(stmt);
@@ -848,7 +852,7 @@ standard_ProcessUtility(Node *parsetree,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, parsetree, queryString,
- context, params,
+ context, params, queryEnv,
dest, completionTag);
else
ExecAlterObjectDependsStmt(stmt, NULL);
@@ -861,7 +865,7 @@ standard_ProcessUtility(Node *parsetree,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, parsetree, queryString,
- context, params,
+ context, params, queryEnv,
dest, completionTag);
else
ExecAlterObjectSchemaStmt(stmt, NULL);
@@ -874,7 +878,7 @@ standard_ProcessUtility(Node *parsetree,
if (EventTriggerSupportsObjectType(stmt->objectType))
ProcessUtilitySlow(pstate, parsetree, queryString,
- context, params,
+ context, params, queryEnv,
dest, completionTag);
else
ExecAlterOwnerStmt(stmt);
@@ -887,7 +891,7 @@ standard_ProcessUtility(Node *parsetree,
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, parsetree, queryString,
- context, params,
+ context, params, queryEnv,
dest, completionTag);
else
CommentObject((CommentStmt *) parsetree);
@@ -900,7 +904,7 @@ standard_ProcessUtility(Node *parsetree,
if (EventTriggerSupportsObjectType(stmt->objtype))
ProcessUtilitySlow(pstate, parsetree, queryString,
- context, params,
+ context, params, queryEnv,
dest, completionTag);
else
ExecSecLabelStmt(stmt);
@@ -910,7 +914,7 @@ standard_ProcessUtility(Node *parsetree,
default:
/* All other statement types have event trigger support */
ProcessUtilitySlow(pstate, parsetree, queryString,
- context, params,
+ context, params, queryEnv,
dest, completionTag);
break;
}
@@ -929,6 +933,7 @@ ProcessUtilitySlow(ParseState *pstate,
const char *queryString,
ProcessUtilityContext context,
ParamListInfo params,
+ QueryEnvironment *queryEnv,
DestReceiver *dest,
char *completionTag)
{
@@ -1038,6 +1043,7 @@ ProcessUtilitySlow(ParseState *pstate,
queryString,
PROCESS_UTILITY_SUBCOMMAND,
params,
+ queryEnv,
None_Receiver,
NULL);
}
@@ -1108,6 +1114,7 @@ ProcessUtilitySlow(ParseState *pstate,
queryString,
PROCESS_UTILITY_SUBCOMMAND,
params,
+ queryEnv,
None_Receiver,
NULL);
EventTriggerAlterTableStart(parsetree);
@@ -1404,7 +1411,8 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateTableAsStmt:
address = ExecCreateTableAs((CreateTableAsStmt *) parsetree,
- queryString, params, completionTag);
+ queryString, params, queryEnv,
+ completionTag);
break;
case T_RefreshMatViewStmt:
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index a3a4174..1b5a30c 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -6454,6 +6454,7 @@ get_name_for_var_field(Var *var, int fieldno,
{
case RTE_RELATION:
case RTE_VALUES:
+ case RTE_SPIRELATION:
/*
* This case should not occur: a column of a table or values list
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index 884cdab..4739c54 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -682,12 +682,14 @@ RevalidateCachedQuery(CachedPlanSource *plansource)
tlist = pg_analyze_and_rewrite_params(rawtree,
plansource->query_string,
plansource->parserSetup,
- plansource->parserSetupArg);
+ plansource->parserSetupArg,
+ NULL);
else
tlist = pg_analyze_and_rewrite(rawtree,
plansource->query_string,
plansource->param_types,
- plansource->num_params);
+ plansource->num_params,
+ NULL);
/* Release snapshot if we got one */
if (snapshot_set)
diff --git a/src/include/commands/createas.h b/src/include/commands/createas.h
index 3c42864..d0b52ef 100644
--- a/src/include/commands/createas.h
+++ b/src/include/commands/createas.h
@@ -17,11 +17,13 @@
#include "catalog/objectaddress.h"
#include "nodes/params.h"
#include "nodes/parsenodes.h"
+#include "parser/query_environment.h"
#include "tcop/dest.h"
extern ObjectAddress ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
- ParamListInfo params, char *completionTag);
+ ParamListInfo params, QueryEnvironment *queryEnv,
+ char *completionTag);
extern int GetIntoRelEFlags(IntoClause *intoClause);
diff --git a/src/include/commands/explain.h b/src/include/commands/explain.h
index 8b3acab..bc20af6 100644
--- a/src/include/commands/explain.h
+++ b/src/include/commands/explain.h
@@ -61,7 +61,7 @@ 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);
@@ -69,11 +69,13 @@ extern TupleDesc ExplainResultDesc(ExplainStmt *stmt);
extern void ExplainOneUtility(Node *utilityStmt, IntoClause *into,
ExplainState *es,
- const char *queryString, ParamListInfo params);
+ 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);
diff --git a/src/include/commands/prepare.h b/src/include/commands/prepare.h
index ba1247a..cffc934 100644
--- a/src/include/commands/prepare.h
+++ b/src/include/commands/prepare.h
@@ -35,14 +35,16 @@ typedef struct
/* Utility statements PREPARE, EXECUTE, DEALLOCATE, EXPLAIN EXECUTE */
-extern void PrepareQuery(PrepareStmt *stmt, const char *queryString);
+extern void PrepareQuery(PrepareStmt *stmt, const char *queryString,
+ QueryEnvironment *queryEnv);
extern void ExecuteQuery(ExecuteStmt *stmt, IntoClause *intoClause,
const char *queryString, ParamListInfo params,
DestReceiver *dest, char *completionTag);
extern void DeallocateQuery(DeallocateStmt *stmt);
extern void ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into,
ExplainState *es,
- const char *queryString, ParamListInfo params);
+ const char *queryString, ParamListInfo params,
+ QueryEnvironment *queryEnv);
/* Low-level access to stored prepared statements */
extern void StorePreparedStatement(const char *stmt_name,
diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h
index c6e3e2c..828d9c5 100644
--- a/src/include/commands/trigger.h
+++ b/src/include/commands/trigger.h
@@ -16,6 +16,7 @@
#include "catalog/objectaddress.h"
#include "nodes/execnodes.h"
#include "nodes/parsenodes.h"
+#include "utils/tuplestore.h"
/*
* TriggerData is the node type that is passed as fmgr "context" info
diff --git a/src/include/executor/execdesc.h b/src/include/executor/execdesc.h
index d5b3fc8..dddcfcd 100644
--- a/src/include/executor/execdesc.h
+++ b/src/include/executor/execdesc.h
@@ -41,6 +41,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; /* transient namespaces */
int instrument_options; /* OR of InstrumentOption flags */
/* These fields are set by ExecutorStart */
@@ -59,13 +60,15 @@ extern QueryDesc *CreateQueryDesc(PlannedStmt *plannedstmt,
Snapshot crosscheck_snapshot,
DestReceiver *dest,
ParamListInfo params,
+ QueryEnvironment *queryEnv,
int instrument_options);
extern QueryDesc *CreateUtilityQueryDesc(Node *utilitystmt,
const char *sourceText,
Snapshot snapshot,
DestReceiver *dest,
- ParamListInfo params);
+ ParamListInfo params,
+ QueryEnvironment *queryEnv);
extern void FreeQueryDesc(QueryDesc *qdesc);
diff --git a/src/include/executor/nodeSpirelationscan.h b/src/include/executor/nodeSpirelationscan.h
new file mode 100644
index 0000000..9893dee
--- /dev/null
+++ b/src/include/executor/nodeSpirelationscan.h
@@ -0,0 +1,22 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeSpirelationscan.h
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/executor/nodeSpirelationscan.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NODESPIRELATIONSCAN_H
+#define NODESPIRELATIONSCAN_H
+
+#include "nodes/execnodes.h"
+
+extern SpiRelationScanState *ExecInitSpiRelationScan(SpiRelationScan *node, EState *estate, int eflags);
+extern TupleTableSlot *ExecSpiRelationScan(SpiRelationScanState *node);
+extern void ExecEndSpiRelationScan(SpiRelationScanState *node);
+extern void ExecReScanSpiRelationScan(SpiRelationScanState *node);
+
+#endif /* NODESPIRELATIONSCAN_H */
diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h
index 76ba394..149cdb8 100644
--- a/src/include/executor/spi.h
+++ b/src/include/executor/spi.h
@@ -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_DUPLICATE (-12)
+#define SPI_ERROR_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_REGISTER 15
+#define SPI_OK_UNREGISTER 16
/* These used to be functions, now just no-ops for backwards compatibility */
#define SPI_push() ((void) 0)
@@ -145,6 +149,9 @@ extern void SPI_cursor_move(Portal portal, bool forward, long count);
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(const char *name, TupleDesc tupdesc,
+ int tuples, Tuplestorestate *tuplestore);
+extern int SPI_unregister_relation(const char *name);
extern void AtEOXact_SPI(bool isCommit);
extern void AtEOSubXact_SPI(bool isCommit, SubTransactionId mySubid);
diff --git a/src/include/executor/spi_priv.h b/src/include/executor/spi_priv.h
index e8084df..e3fd7c1 100644
--- a/src/include/executor/spi_priv.h
+++ b/src/include/executor/spi_priv.h
@@ -1,5 +1,5 @@
/*-------------------------------------------------------------------------
- *
+c *
* spi_priv.h
* Server Programming Interface private declarations
*
@@ -14,6 +14,7 @@
#define SPI_PRIV_H
#include "executor/spi.h"
+#include "parser/query_environment.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; /* objects exposed to SQL */
} _SPI_connection;
/*
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index f6f73f3..08cee3b 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -20,6 +20,7 @@
#include "lib/pairingheap.h"
#include "nodes/params.h"
#include "nodes/plannodes.h"
+#include "parser/query_environment.h"
#include "utils/hsearch.h"
#include "utils/reltrigger.h"
#include "utils/sortsupport.h"
@@ -383,6 +384,9 @@ typedef struct EState
ParamListInfo es_param_list_info; /* values of external params */
ParamExecData *es_param_exec_vals; /* values of internal params */
+ /* Relations made available via SPI. */
+ List *es_spi_relations;
+
/* Other working state: */
MemoryContext es_query_cxt; /* per-query context in which EState lives */
@@ -1571,6 +1575,24 @@ typedef struct CteScanState
} CteScanState;
/* ----------------
+ * SpiRelationScanState information
+ *
+ * SpiRelationScan nodes are used to scan a tuplestore created and named
+ * by SPI client code prior to execution of the query. An example is a
+ * transition table for an AFTER trigger.
+ *
+ * Multiple SpiRelationScan nodes can read out from the same Tuplestore.
+ * ----------------
+ */
+typedef struct SpiRelationScanState
+{
+ 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 *table; /* the rows */
+} SpiRelationScanState;
+
+/* ----------------
* WorkTableScanState information
*
* WorkTableScan nodes are used to scan the work table created by
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index cb9307c..e2a3716 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -61,6 +61,7 @@ typedef enum NodeTag
T_FunctionScan,
T_ValuesScan,
T_CteScan,
+ T_SpiRelationScan,
T_WorkTableScan,
T_ForeignScan,
T_CustomScan,
@@ -109,6 +110,7 @@ typedef enum NodeTag
T_FunctionScanState,
T_ValuesScanState,
T_CteScanState,
+ T_SpiRelationScanState,
T_WorkTableScanState,
T_ForeignScanState,
T_CustomScanState,
diff --git a/src/include/nodes/params.h b/src/include/nodes/params.h
index a8aa530..2d66292 100644
--- a/src/include/nodes/params.h
+++ b/src/include/nodes/params.h
@@ -17,6 +17,7 @@
/* Forward declarations, to avoid including other headers */
struct Bitmapset;
struct ParseState;
+struct EState;
/* ----------------
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 04b1c2f..51d2d14 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -791,7 +791,8 @@ typedef enum RTEKind
RTE_JOIN, /* join */
RTE_FUNCTION, /* function in FROM */
RTE_VALUES, /* VALUES (), (), ... */
- RTE_CTE /* common table expr (WITH list element) */
+ RTE_CTE, /* common table expr (WITH list element) */
+ RTE_SPIRELATION /* relation supplied via SPI */
} RTEKind;
typedef struct RangeTblEntry
@@ -867,6 +868,15 @@ typedef struct RangeTblEntry
List *ctecolcollations; /* OID list of column collation OIDs */
/*
+ * Needed for SPI relation RTE...
+ *
+ * ... include the cte* List fields from CTE and relid. We could
+ * duplicate them here with slightly different names, but why?
+ */
+ char *srname; /* name by which relation will be referenced */
+ int srtuples; /* estimated number of rows */
+
+ /*
* Fields valid in all RTEs:
*/
Alias *alias; /* user-written alias clause, if any */
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index e2fbc7d..1bcbcab 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -487,6 +487,16 @@ typedef struct CteScan
} CteScan;
/* ----------------
+ * SpiRelationScan node
+ * ----------------
+ */
+typedef struct SpiRelationScan
+{
+ Scan scan;
+ char *srname;
+} SpiRelationScan;
+
+/* ----------------
* WorkTableScan node
* ----------------
*/
diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h
index 2a4df2f..78722cb 100644
--- a/src/include/optimizer/cost.h
+++ b/src/include/optimizer/cost.h
@@ -93,6 +93,8 @@ extern void cost_valuesscan(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_spirelationscan(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,
@@ -181,6 +183,7 @@ extern void set_function_size_estimates(PlannerInfo *root, RelOptInfo *rel);
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_spirelation_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);
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 71d9154..14fbfe2 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -85,6 +85,8 @@ extern Path *create_valuesscan_path(PlannerInfo *root, RelOptInfo *rel,
Relids required_outer);
extern Path *create_ctescan_path(PlannerInfo *root, RelOptInfo *rel,
Relids required_outer);
+extern Path *create_spirelationscan_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,
diff --git a/src/include/parser/analyze.h b/src/include/parser/analyze.h
index 5ba322a..e76b4a3 100644
--- a/src/include/parser/analyze.h
+++ b/src/include/parser/analyze.h
@@ -23,9 +23,11 @@ extern PGDLLIMPORT post_parse_analyze_hook_type post_parse_analyze_hook;
extern Query *parse_analyze(Node *parseTree, const char *sourceText,
- Oid *paramTypes, int numParams);
+ Oid *paramTypes, int numParams,
+ QueryEnvironment *queryEnv);
extern Query *parse_analyze_varparams(Node *parseTree, const char *sourceText,
- Oid **paramTypes, int *numParams);
+ Oid **paramTypes, int *numParams,
+ QueryEnvironment *queryEnv);
extern Query *parse_sub_analyze(Node *parseTree, ParseState *parentParseState,
CommonTableExpr *parentCTE,
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 6633586..5cd0330 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -15,6 +15,7 @@
#define PARSE_NODE_H
#include "nodes/parsenodes.h"
+#include "parser/query_environment.h"
#include "utils/relcache.h"
@@ -68,6 +69,7 @@ typedef enum ParseExprKind
} ParseExprKind;
+
/*
* Function signatures for parser hooks
*/
@@ -80,7 +82,6 @@ typedef Node *(*CoerceParamHook) (ParseState *pstate, Param *param,
Oid targetTypeId, int32 targetTypeMod,
int location);
-
/*
* State information used during parse analysis
*
@@ -142,6 +143,7 @@ struct ParseState
List *p_ctenamespace; /* current namespace for common table exprs */
List *p_future_ctes; /* common table exprs not yet in namespace */
CommonTableExpr *p_parent_cte; /* this query's containing CTE */
+ List *p_spirelationnamespace; /* SPI relations */
List *p_windowdefs; /* raw representations of window clauses */
ParseExprKind p_expr_kind; /* what kind of expression we're parsing */
int p_next_resno; /* next targetlist resno to assign */
diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h
index 3ef3d7b..df56efd 100644
--- a/src/include/parser/parse_relation.h
+++ b/src/include/parser/parse_relation.h
@@ -15,6 +15,7 @@
#define PARSE_RELATION_H
#include "parser/parse_node.h"
+#include "parser/query_environment.h"
/*
@@ -42,6 +43,8 @@ extern RangeTblEntry *refnameRangeTblEntry(ParseState *pstate,
extern CommonTableExpr *scanNameSpaceForCTE(ParseState *pstate,
const char *refname,
Index *ctelevelsup);
+extern SpiRelation *scanNameSpaceForSpiRelation(ParseState *pstate,
+ const char *refname);
extern void checkNameSpaceConflicts(ParseState *pstate, List *namespace1,
List *namespace2);
extern int RTERangeTablePosn(ParseState *pstate,
@@ -71,6 +74,10 @@ extern RangeTblEntry *addRangeTableEntryForRelation(ParseState *pstate,
Alias *alias,
bool inh,
bool inFromCl);
+extern RangeTblEntry *addRangeTableEntryForSpiRelation(ParseState *pstate,
+ SpiRelation *sr,
+ RangeVar *rv,
+ bool inFromCl);
extern RangeTblEntry *addRangeTableEntryForSubquery(ParseState *pstate,
Query *subquery,
Alias *alias,
diff --git a/src/include/parser/query_environment.h b/src/include/parser/query_environment.h
new file mode 100644
index 0000000..70fa49f
--- /dev/null
+++ b/src/include/parser/query_environment.h
@@ -0,0 +1,41 @@
+/*-------------------------------------------------------------------------
+ *
+ * query_environment.h
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ *
+ * src/include/parser/query_environment.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef QUERY_ENVIRONMENT_H
+#define QUERY_ENVIRONMENT_H
+
+#include "access/tupdesc.h"
+#include "nodes/pg_list.h"
+
+struct Tuplestorestate;
+
+/*
+ * A relation that SPI client code has exposed to the parser, planner and
+ * executor.
+ */
+typedef struct SpiRelation
+{
+ char *name; /* Name of the relation */
+ TupleDesc tupdesc; /* Descriptor for tuples */
+ int tuples; /* Estimated number of tuples. */
+ struct Tuplestorestate *tuplestore; /* The tuples. */
+} SpiRelation;
+
+/*
+ * Users of the SPI interface such as PLs can make transient database objects
+ * visible to the SQL parser, planner and executor. Currently the only example
+ * is SPI relations.
+ */
+typedef struct QueryEnvironment
+{
+ List *spi_relations; /* A list of pointers to SpiRelation */
+} QueryEnvironment;
+
+#endif /* QUERY_ENVIRONMENT_H */
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index 7254355..07935b9 100644
--- a/src/include/tcop/tcopprot.h
+++ b/src/include/tcop/tcopprot.h
@@ -22,6 +22,7 @@
#include "nodes/params.h"
#include "nodes/parsenodes.h"
#include "nodes/plannodes.h"
+#include "parser/query_environment.h"
#include "storage/procsignal.h"
#include "utils/guc.h"
@@ -48,11 +49,13 @@ extern int log_statement;
extern List *pg_parse_query(const char *query_string);
extern List *pg_analyze_and_rewrite(Node *parsetree, const char *query_string,
- Oid *paramTypes, int numParams);
+ Oid *paramTypes, int numParams,
+ QueryEnvironment *queryEnv);
extern List *pg_analyze_and_rewrite_params(Node *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,
diff --git a/src/include/tcop/utility.h b/src/include/tcop/utility.h
index dad8246..1b8589c 100644
--- a/src/include/tcop/utility.h
+++ b/src/include/tcop/utility.h
@@ -27,14 +27,17 @@ typedef enum
typedef void (*ProcessUtility_hook_type) (Node *parsetree,
const char *queryString, ProcessUtilityContext context,
ParamListInfo params,
+ QueryEnvironment *queryEnv,
DestReceiver *dest, char *completionTag);
extern PGDLLIMPORT ProcessUtility_hook_type ProcessUtility_hook;
extern void ProcessUtility(Node *parsetree, const char *queryString,
ProcessUtilityContext context, ParamListInfo params,
+ QueryEnvironment *queryEnv,
DestReceiver *dest, char *completionTag);
extern void standard_ProcessUtility(Node *parsetree, const char *queryString,
ProcessUtilityContext context, ParamListInfo params,
+ QueryEnvironment *queryEnv,
DestReceiver *dest, char *completionTag);
extern bool UtilityReturnsTuples(Node *parsetree);
diff --git a/src/include/utils/portal.h b/src/include/utils/portal.h
index c1d93a9..7b38af2 100644
--- a/src/include/utils/portal.h
+++ b/src/include/utils/portal.h
@@ -137,6 +137,7 @@ typedef struct PortalData
CachedPlan *cplan; /* CachedPlan, if stmts are from one */
ParamListInfo portalParams; /* params to pass to query */
+ QueryEnvironment *queryEnv; /* transient objects introduced by SPI */
/* Features/options */
PortalStrategy strategy; /* see above */