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 */