]> granicus.if.org Git - postgresql/commitdiff
Add infrastructure to support EphemeralNamedRelation references.
authorKevin Grittner <kgrittn@postgresql.org>
Sat, 1 Apr 2017 04:17:18 +0000 (23:17 -0500)
committerKevin Grittner <kgrittn@postgresql.org>
Sat, 1 Apr 2017 04:17:18 +0000 (23:17 -0500)
A QueryEnvironment concept is added, which allows new types of
objects to be passed into queries from parsing on through
execution.  At this point, the only thing implemented is a
collection of EphemeralNamedRelation objects -- relations which
can be referenced by name in queries, but do not exist in the
catalogs.  The only type of ENR implemented is NamedTuplestore, but
provision is made to add more types fairly easily.

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

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

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

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

Kevin Grittner and Thomas Munro
Reviewed by Heikki Linnakangas, David Fetter, and Thomas Munro
with valuable comments and suggestions from many others

78 files changed:
contrib/pg_stat_statements/pg_stat_statements.c
doc/src/sgml/spi.sgml
src/backend/catalog/pg_proc.c
src/backend/commands/copy.c
src/backend/commands/createas.c
src/backend/commands/explain.c
src/backend/commands/extension.c
src/backend/commands/foreigncmds.c
src/backend/commands/matview.c
src/backend/commands/prepare.c
src/backend/commands/schemacmds.c
src/backend/commands/trigger.c
src/backend/commands/view.c
src/backend/executor/Makefile
src/backend/executor/execAmi.c
src/backend/executor/execMain.c
src/backend/executor/execParallel.c
src/backend/executor/execProcnode.c
src/backend/executor/execUtils.c
src/backend/executor/functions.c
src/backend/executor/nodeNamedtuplestorescan.c [new file with mode: 0644]
src/backend/executor/spi.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/nodeFuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/print.c
src/backend/nodes/readfuncs.c
src/backend/optimizer/path/allpaths.c
src/backend/optimizer/path/costsize.c
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/plan/setrefs.c
src/backend/optimizer/plan/subselect.c
src/backend/optimizer/prep/prepjointree.c
src/backend/optimizer/util/clauses.c
src/backend/optimizer/util/pathnode.c
src/backend/optimizer/util/plancat.c
src/backend/optimizer/util/relnode.c
src/backend/parser/Makefile
src/backend/parser/analyze.c
src/backend/parser/parse_clause.c
src/backend/parser/parse_enr.c [new file with mode: 0644]
src/backend/parser/parse_node.c
src/backend/parser/parse_relation.c
src/backend/parser/parse_target.c
src/backend/tcop/postgres.c
src/backend/tcop/pquery.c
src/backend/tcop/utility.c
src/backend/utils/adt/ruleutils.c
src/backend/utils/cache/plancache.c
src/backend/utils/misc/Makefile
src/backend/utils/misc/queryenvironment.c [new file with mode: 0644]
src/backend/utils/sort/tuplestore.c
src/include/catalog/catversion.h
src/include/commands/createas.h
src/include/commands/explain.h
src/include/commands/prepare.h
src/include/executor/execdesc.h
src/include/executor/nodeNamedtuplestorescan.h [new file with mode: 0644]
src/include/executor/spi.h
src/include/executor/spi_priv.h
src/include/nodes/execnodes.h
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/nodes/plannodes.h
src/include/optimizer/cost.h
src/include/optimizer/pathnode.h
src/include/parser/analyze.h
src/include/parser/parse_enr.h [new file with mode: 0644]
src/include/parser/parse_node.h
src/include/parser/parse_relation.h
src/include/tcop/tcopprot.h
src/include/tcop/utility.h
src/include/utils/plancache.h
src/include/utils/portal.h
src/include/utils/queryenvironment.h [new file with mode: 0644]
src/include/utils/tuplestore.h
src/test/regress/expected/with.out
src/test/regress/sql/with.sql

index c3002618524a5735fdf2dab864c1128adb0911a2..6b7503df42b70074bf3a6241d3cd52b7b5d6ceb1 100644 (file)
@@ -299,6 +299,7 @@ static void pgss_ExecutorFinish(QueryDesc *queryDesc);
 static void pgss_ExecutorEnd(QueryDesc *queryDesc);
 static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
                                        ProcessUtilityContext context, ParamListInfo params,
+                                       QueryEnvironment *queryEnv,
                                        DestReceiver *dest, char *completionTag);
 static uint32 pgss_hash_fn(const void *key, Size keysize);
 static int     pgss_match_fn(const void *key1, const void *key2, Size keysize);
@@ -956,7 +957,8 @@ pgss_ExecutorEnd(QueryDesc *queryDesc)
  */
 static void
 pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
-                                       ProcessUtilityContext context, ParamListInfo params,
+                                       ProcessUtilityContext context,
+                                       ParamListInfo params, QueryEnvironment *queryEnv,
                                        DestReceiver *dest, char *completionTag)
 {
        Node       *parsetree = pstmt->utilityStmt;
@@ -994,11 +996,11 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
                {
                        if (prev_ProcessUtility)
                                prev_ProcessUtility(pstmt, queryString,
-                                                                       context, params,
+                                                                       context, params, queryEnv,
                                                                        dest, completionTag);
                        else
                                standard_ProcessUtility(pstmt, queryString,
-                                                                               context, params,
+                                                                               context, params, queryEnv,
                                                                                dest, completionTag);
                        nested_level--;
                }
@@ -1058,11 +1060,11 @@ pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
        {
                if (prev_ProcessUtility)
                        prev_ProcessUtility(pstmt, queryString,
-                                                               context, params,
+                                                               context, params, queryEnv,
                                                                dest, completionTag);
                else
                        standard_ProcessUtility(pstmt, queryString,
-                                                                       context, params,
+                                                                       context, params, queryEnv,
                                                                        dest, completionTag);
        }
 }
@@ -2424,6 +2426,9 @@ JumbleRangeTable(pgssJumbleState *jstate, List *rtable)
                                APP_JUMB_STRING(rte->ctename);
                                APP_JUMB(rte->ctelevelsup);
                                break;
+                       case RTE_NAMEDTUPLESTORE:
+                               APP_JUMB_STRING(rte->enrname);
+                               break;
                        default:
                                elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
                                break;
index 836ce0822f3edfe78e632a3b848f0d381e86f689..af7eada2e1b1d4c80bf9e33968665b4e0c2bc84d 100644 (file)
@@ -2639,6 +2639,210 @@ SPIPlanPtr SPI_saveplan(SPIPlanPtr <parameter>plan</parameter>)
  </refsect1>
 </refentry>
 
+<!-- *********************************************** -->
+
+<refentry id="spi-spi-register-relation">
+ <indexterm><primary>SPI_register_relation</primary></indexterm>
+
+ <refmeta>
+  <refentrytitle>SPI_register_relation</refentrytitle>
+  <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+  <refname>SPI_register_relation</refname>
+  <refpurpose>make a ephemeral named relation available by name in SPI queries</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+int SPI_register_relation(EphemeralNamedRelation <parameter>enr</parameter>)
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <function>SPI_register_relation</function> makes an ephemeral named
+   relation, with associated information, available to queries planned and
+   executed through the current SPI connection.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Arguments</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><literal>EphemeralNamedRelation <parameter>enr</parameter></literal></term>
+    <listitem>
+     <para>
+      the ephemeral named relation registry entry
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>Return Value</title>
+
+  <para>
+   If the execution of the command was successful then the following
+   (nonnegative) value will be returned:
+
+   <variablelist>
+    <varlistentry>
+     <term><symbol>SPI_OK_REL_REGISTER</symbol></term>
+     <listitem>
+      <para>
+       if the relation has been successfully registered by name
+      </para>
+     </listitem>
+    </varlistentry>
+   </variablelist>
+  </para>
+
+  <para>
+   On error, one of the following negative values is returned:
+
+   <variablelist>
+    <varlistentry>
+     <term><symbol>SPI_ERROR_ARGUMENT</symbol></term>
+     <listitem>
+      <para>
+       if <parameter>enr</parameter> is <symbol>NULL</symbol> or its
+       <varname>name</varname> field is <symbol>NULL</symbol>
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><symbol>SPI_ERROR_UNCONNECTED</symbol></term>
+     <listitem>
+      <para>
+       if called from an unconnected procedure
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><symbol>SPI_ERROR_REL_DUPLICATE</symbol></term>
+     <listitem>
+      <para>
+       if the name specified in the <varname>name</varname> field of
+       <parameter>enr</parameter> is already registered for this connection
+      </para>
+     </listitem>
+    </varlistentry>
+   </variablelist>
+  </para>
+ </refsect1>
+</refentry>
+
+<!-- *********************************************** -->
+
+<refentry id="spi-spi-unregister-relation">
+ <indexterm><primary>SPI_unregister_relation</primary></indexterm>
+
+ <refmeta>
+  <refentrytitle>SPI_unregister_relation</refentrytitle>
+  <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+  <refname>SPI_unregister_relation</refname>
+  <refpurpose>remove an ephemeral named relation from the registry</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+int SPI_unregister_relation(const char * <parameter>name</parameter>)
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <function>SPI_unregister_relation</function> removes an ephemeral named
+   relation from the registry for the current connection.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Arguments</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><literal>const char * <parameter>name</parameter></literal></term>
+    <listitem>
+     <para>
+      the relation registry entry name
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>Return Value</title>
+
+  <para>
+   If the execution of the command was successful then the following
+   (nonnegative) value will be returned:
+
+   <variablelist>
+    <varlistentry>
+     <term><symbol>SPI_OK_REL_UNREGISTER</symbol></term>
+     <listitem>
+      <para>
+       if the tuplestore has been successfully removed from the registry
+      </para>
+     </listitem>
+    </varlistentry>
+   </variablelist>
+  </para>
+
+  <para>
+   On error, one of the following negative values is returned:
+
+   <variablelist>
+    <varlistentry>
+     <term><symbol>SPI_ERROR_ARGUMENT</symbol></term>
+     <listitem>
+      <para>
+       if <parameter>name</parameter> is <symbol>NULL</symbol>
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><symbol>SPI_ERROR_UNCONNECTED</symbol></term>
+     <listitem>
+      <para>
+       if called from an unconnected procedure
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><symbol>SPI_ERROR_REL_NOT_FOUND</symbol></term>
+     <listitem>
+      <para>
+       if <parameter>name</parameter> is not found in the registry for the
+       current connection
+      </para>
+     </listitem>
+    </varlistentry>
+   </variablelist>
+  </para>
+ </refsect1>
+</refentry>
+
+<!-- *********************************************** -->
+
 </sect1>
 
 <sect1 id="spi-interface-support">
index ae278481163e7fb1c535d691dc9857aa2c3a8288..f058d1274f4f445154c77d30a66ebf5551784067 100644 (file)
@@ -934,7 +934,8 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
                                querytree_sublist = pg_analyze_and_rewrite_params(parsetree,
                                                                                                                                  prosrc,
                                                                           (ParserSetupHook) sql_fn_parser_setup,
-                                                                                                                                 pinfo);
+                                                                                                                                 pinfo,
+                                                                                                                                 NULL);
                                querytree_list = list_concat(querytree_list,
                                                                                         querytree_sublist);
                        }
index 0158eda591772434aacab9d38248077fc225df25..8c5880868635fca3f5a07b231253ffb31a802ada 100644 (file)
@@ -1471,7 +1471,8 @@ BeginCopy(ParseState *pstate,
                 * DECLARE CURSOR and PREPARE.)  XXX FIXME someday.
                 */
                rewritten = pg_analyze_and_rewrite(copyObject(raw_query),
-                                                                                  pstate->p_sourcetext, NULL, 0);
+                                                                                  pstate->p_sourcetext, NULL, 0,
+                                                                                  NULL);
 
                /* check that we got back something we can work with */
                if (rewritten == NIL)
@@ -1574,7 +1575,7 @@ BeginCopy(ParseState *pstate,
                cstate->queryDesc = CreateQueryDesc(plan, pstate->p_sourcetext,
                                                                                        GetActiveSnapshot(),
                                                                                        InvalidSnapshot,
-                                                                                       dest, NULL, 0);
+                                                                                       dest, NULL, NULL, 0);
 
                /*
                 * Call ExecutorStart to prepare the plan for execution.
index 20cb64661a6944b31e3d7a29fbc7e3e8ed5381de..f49b391505bfd77e8fdda554d807d53941be4fce 100644 (file)
@@ -222,7 +222,8 @@ create_ctas_nodata(List *tlist, IntoClause *into)
  */
 ObjectAddress
 ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
-                                 ParamListInfo params, char *completionTag)
+                                 ParamListInfo params, QueryEnvironment *queryEnv,
+                                 char *completionTag)
 {
        Query      *query = castNode(Query, stmt->query);
        IntoClause *into = stmt->into;
@@ -341,7 +342,7 @@ ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
                /* Create a QueryDesc, redirecting output to our tuple receiver */
                queryDesc = CreateQueryDesc(plan, queryString,
                                                                        GetActiveSnapshot(), InvalidSnapshot,
-                                                                       dest, params, 0);
+                                                                       dest, params, queryEnv, 0);
 
                /* call ExecutorStart to prepare the plan for execution */
                ExecutorStart(queryDesc, GetIntoRelEFlags(into));
index ea19ba60c59efb33854d017d06d634b5bc48f6d0..a18ab436167f7125cb0e07189cddee8e5dd3ec2d 100644 (file)
@@ -55,7 +55,8 @@ explain_get_index_name_hook_type explain_get_index_name_hook = NULL;
 
 static void ExplainOneQuery(Query *query, int cursorOptions,
                                IntoClause *into, ExplainState *es,
-                               const char *queryString, ParamListInfo params);
+                               const char *queryString, ParamListInfo params,
+                               QueryEnvironment *queryEnv);
 static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
                                ExplainState *es);
 static double elapsed_time(instr_time *starttime);
@@ -142,7 +143,8 @@ static void escape_yaml(StringInfo buf, const char *str);
  */
 void
 ExplainQuery(ParseState *pstate, ExplainStmt *stmt, const char *queryString,
-                        ParamListInfo params, DestReceiver *dest)
+                        ParamListInfo params, QueryEnvironment *queryEnv,
+                        DestReceiver *dest)
 {
        ExplainState *es = NewExplainState();
        TupOutputState *tstate;
@@ -253,7 +255,7 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt, const char *queryString,
                {
                        ExplainOneQuery(castNode(Query, lfirst(l)),
                                                        CURSOR_OPT_PARALLEL_OK, NULL, es,
-                                                       queryString, params);
+                                                       queryString, params, queryEnv);
 
                        /* Separate plans with an appropriate separator */
                        if (lnext(l) != NULL)
@@ -338,12 +340,14 @@ ExplainResultDesc(ExplainStmt *stmt)
 static void
 ExplainOneQuery(Query *query, int cursorOptions,
                                IntoClause *into, ExplainState *es,
-                               const char *queryString, ParamListInfo params)
+                               const char *queryString, ParamListInfo params,
+                               QueryEnvironment *queryEnv)
 {
        /* planner will not cope with utility statements */
        if (query->commandType == CMD_UTILITY)
        {
-               ExplainOneUtility(query->utilityStmt, into, es, queryString, params);
+               ExplainOneUtility(query->utilityStmt, into, es, queryString, params,
+                                                 queryEnv);
                return;
        }
 
@@ -366,7 +370,8 @@ ExplainOneQuery(Query *query, int cursorOptions,
                INSTR_TIME_SUBTRACT(planduration, planstart);
 
                /* run it (if needed) and produce output */
-               ExplainOnePlan(plan, into, es, queryString, params, &planduration);
+               ExplainOnePlan(plan, into, es, queryString, params, queryEnv,
+                                          &planduration);
        }
 }
 
@@ -383,7 +388,8 @@ ExplainOneQuery(Query *query, int cursorOptions,
  */
 void
 ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es,
-                                 const char *queryString, ParamListInfo params)
+                                 const char *queryString, ParamListInfo params,
+                                 QueryEnvironment *queryEnv)
 {
        if (utilityStmt == NULL)
                return;
@@ -404,7 +410,7 @@ ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es,
                Assert(list_length(rewritten) == 1);
                ExplainOneQuery(castNode(Query, linitial(rewritten)),
                                                0, ctas->into, es,
-                                               queryString, params);
+                                               queryString, params, queryEnv);
        }
        else if (IsA(utilityStmt, DeclareCursorStmt))
        {
@@ -423,11 +429,11 @@ ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es,
                Assert(list_length(rewritten) == 1);
                ExplainOneQuery(castNode(Query, linitial(rewritten)),
                                                dcs->options, NULL, es,
-                                               queryString, params);
+                                               queryString, params, queryEnv);
        }
        else if (IsA(utilityStmt, ExecuteStmt))
                ExplainExecuteQuery((ExecuteStmt *) utilityStmt, into, es,
-                                                       queryString, params);
+                                                       queryString, params, queryEnv);
        else if (IsA(utilityStmt, NotifyStmt))
        {
                if (es->format == EXPLAIN_FORMAT_TEXT)
@@ -460,7 +466,7 @@ ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es,
 void
 ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
                           const char *queryString, ParamListInfo params,
-                          const instr_time *planduration)
+                          QueryEnvironment *queryEnv, const instr_time *planduration)
 {
        DestReceiver *dest;
        QueryDesc  *queryDesc;
@@ -505,7 +511,7 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
        /* Create a QueryDesc for the query */
        queryDesc = CreateQueryDesc(plannedstmt, queryString,
                                                                GetActiveSnapshot(), InvalidSnapshot,
-                                                               dest, params, instrument_option);
+                                                               dest, params, queryEnv, instrument_option);
 
        /* Select execution options */
        if (es->analyze)
@@ -796,6 +802,7 @@ ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
                case T_TableFuncScan:
                case T_ValuesScan:
                case T_CteScan:
+               case T_NamedTuplestoreScan:
                case T_WorkTableScan:
                        *rels_used = bms_add_member(*rels_used,
                                                                                ((Scan *) plan)->scanrelid);
@@ -951,6 +958,9 @@ ExplainNode(PlanState *planstate, List *ancestors,
                case T_CteScan:
                        pname = sname = "CTE Scan";
                        break;
+               case T_NamedTuplestoreScan:
+                       pname = sname = "Named Tuplestore Scan";
+                       break;
                case T_WorkTableScan:
                        pname = sname = "WorkTable Scan";
                        break;
@@ -1389,6 +1399,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
                case T_SeqScan:
                case T_ValuesScan:
                case T_CteScan:
+               case T_NamedTuplestoreScan:
                case T_WorkTableScan:
                case T_SubqueryScan:
                        show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
@@ -2679,6 +2690,11 @@ ExplainTargetRel(Plan *plan, Index rti, ExplainState *es)
                        objectname = rte->ctename;
                        objecttag = "CTE Name";
                        break;
+               case T_NamedTuplestoreScan:
+                       Assert(rte->rtekind == RTE_NAMEDTUPLESTORE);
+                       objectname = rte->enrname;
+                       objecttag = "Tuplestore Name";
+                       break;
                case T_WorkTableScan:
                        /* Assert it's on a self-reference CTE */
                        Assert(rte->rtekind == RTE_CTE);
index 5a84bedf467de67231a498f7ddf51fea2658785e..6be9bc457c2c7a1a0b93041fb36f9415eea71a07 100644 (file)
@@ -721,7 +721,8 @@ execute_sql_string(const char *sql, const char *filename)
                stmt_list = pg_analyze_and_rewrite(parsetree,
                                                                                   sql,
                                                                                   NULL,
-                                                                                  0);
+                                                                                  0,
+                                                                                  NULL);
                stmt_list = pg_plan_queries(stmt_list, CURSOR_OPT_PARALLEL_OK, NULL);
 
                foreach(lc2, stmt_list)
@@ -739,7 +740,7 @@ execute_sql_string(const char *sql, const char *filename)
                                qdesc = CreateQueryDesc(stmt,
                                                                                sql,
                                                                                GetActiveSnapshot(), NULL,
-                                                                               dest, NULL, 0);
+                                                                               dest, NULL, NULL, 0);
 
                                ExecutorStart(qdesc, 0);
                                ExecutorRun(qdesc, ForwardScanDirection, 0, true);
@@ -759,6 +760,7 @@ execute_sql_string(const char *sql, const char *filename)
                                                           sql,
                                                           PROCESS_UTILITY_QUERY,
                                                           NULL,
+                                                          NULL,
                                                           dest,
                                                           NULL);
                        }
index 68100df0830abf62d69197a2b6b081c3fcb7718a..4ffe1bca75bf464147dceba455189e4c6526c964 100644 (file)
@@ -1623,7 +1623,7 @@ ImportForeignSchema(ImportForeignSchemaStmt *stmt)
                        /* Execute statement */
                        ProcessUtility(pstmt,
                                                   cmd,
-                                                  PROCESS_UTILITY_SUBCOMMAND, NULL,
+                                                  PROCESS_UTILITY_SUBCOMMAND, NULL, NULL,
                                                   None_Receiver, NULL);
 
                        /* Be sure to advance the command counter between subcommands */
index 9d41ad8fad262d52201008e3d38fda421153085f..2f933283186c49ba677a0a9166d1b5675d70393b 100644 (file)
@@ -418,7 +418,7 @@ refresh_matview_datafill(DestReceiver *dest, Query *query,
        /* Create a QueryDesc, redirecting output to our tuple receiver */
        queryDesc = CreateQueryDesc(plan, queryString,
                                                                GetActiveSnapshot(), InvalidSnapshot,
-                                                               dest, NULL, 0);
+                                                               dest, NULL, NULL, 0);
 
        /* call ExecutorStart to prepare the plan for execution */
        ExecutorStart(queryDesc, EXEC_FLAG_WITHOUT_OIDS);
index dc6d43ec6d688c22820424cf814968749f735a25..5b3f777f2c3277af2f5c9d68f32c8d9efd80ba00 100644 (file)
@@ -91,7 +91,7 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString,
         * to see the unmodified raw parse tree.
         */
        plansource = CreateCachedPlan(rawstmt, queryString,
-                                                                 CreateCommandTag(stmt->query));
+                                                                 CreateCommandTag(stmt->query), NULL);
 
        /* Transform list of TypeNames to array of type OIDs */
        nargs = list_length(stmt->argtypes);
@@ -243,7 +243,7 @@ ExecuteQuery(ExecuteStmt *stmt, IntoClause *intoClause,
                                                                           entry->plansource->query_string);
 
        /* Replan if needed, and increment plan refcount for portal */
-       cplan = GetCachedPlan(entry->plansource, paramLI, false);
+       cplan = GetCachedPlan(entry->plansource, paramLI, false, NULL);
        plan_list = cplan->stmt_list;
 
        /*
@@ -551,7 +551,7 @@ FetchPreparedStatementTargetList(PreparedStatement *stmt)
        List       *tlist;
 
        /* Get the plan's primary targetlist */
-       tlist = CachedPlanGetTargetList(stmt->plansource);
+       tlist = CachedPlanGetTargetList(stmt->plansource, NULL);
 
        /* Copy into caller's context in case plan gets invalidated */
        return copyObject(tlist);
@@ -629,7 +629,8 @@ DropAllPreparedStatements(void)
  */
 void
 ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es,
-                                       const char *queryString, ParamListInfo params)
+                                       const char *queryString, ParamListInfo params,
+                                       QueryEnvironment *queryEnv)
 {
        PreparedStatement *entry;
        const char *query_string;
@@ -668,7 +669,7 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es,
        }
 
        /* Replan if needed, and acquire a transient refcount */
-       cplan = GetCachedPlan(entry->plansource, paramLI, true);
+       cplan = GetCachedPlan(entry->plansource, paramLI, true, queryEnv);
 
        INSTR_TIME_SET_CURRENT(planduration);
        INSTR_TIME_SUBTRACT(planduration, planstart);
@@ -681,9 +682,11 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es,
                PlannedStmt *pstmt = castNode(PlannedStmt, lfirst(p));
 
                if (pstmt->commandType != CMD_UTILITY)
-                       ExplainOnePlan(pstmt, into, es, query_string, paramLI, &planduration);
+                       ExplainOnePlan(pstmt, into, es, query_string, paramLI, queryEnv,
+                                                  &planduration);
                else
-                       ExplainOneUtility(pstmt->utilityStmt, into, es, query_string, paramLI);
+                       ExplainOneUtility(pstmt->utilityStmt, into, es, query_string,
+                                                         paramLI, queryEnv);
 
                /* No need for CommandCounterIncrement, as ExplainOnePlan did it */
 
index 722b965d65db6a739fe8aafa3d630230ff6a3524..93425babbedb14b7a3c54e8c2d757210f9870a04 100644 (file)
@@ -194,6 +194,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString,
                                           queryString,
                                           PROCESS_UTILITY_SUBCOMMAND,
                                           NULL,
+                                          NULL,
                                           None_Receiver,
                                           NULL);
 
index f3b1a5268267b10b0ce3307017dda205c6fe059b..ebf23a0d94b119e40aa0964024cbd3fddbba4e75 100644 (file)
@@ -354,6 +354,13 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
                         * adjustments will be needed below.
                         */
 
+                       if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                                errmsg("\"%s\" is a partitioned table",
+                                                               RelationGetRelationName(rel)),
+                                        errdetail("Triggers on partitioned tables cannot have transition tables.")));
+
                        if (stmt->timing != TRIGGER_TYPE_AFTER)
                                ereport(ERROR,
                                                (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -1173,7 +1180,7 @@ ConvertTriggerToFK(CreateTrigStmt *stmt, Oid funcoid)
                /* ... and execute it */
                ProcessUtility(wrapper,
                                           "(generated ALTER TABLE ADD FOREIGN KEY command)",
-                                          PROCESS_UTILITY_SUBCOMMAND, NULL,
+                                          PROCESS_UTILITY_SUBCOMMAND, NULL, NULL,
                                           None_Receiver, NULL);
 
                /* Remove the matched item from the list */
index 35e25db7dca6f686e442a9a718376d8c1d052e72..6909a67e77b651b5c8a5e1e3e05c593ffac62257 100644 (file)
@@ -436,7 +436,7 @@ DefineView(ViewStmt *stmt, const char *queryString,
        rawstmt->stmt_location = stmt_location;
        rawstmt->stmt_len = stmt_len;
 
-       viewParse = parse_analyze(rawstmt, queryString, NULL, 0);
+       viewParse = parse_analyze(rawstmt, queryString, NULL, 0, NULL);
 
        /*
         * The grammar should ensure that the result is a single SELECT Query.
index d1c1324399a05e9020df6c6b1fd927a402d06726..083b20f3fee0cb957f59d754e5128e153952a58b 100644 (file)
@@ -25,7 +25,8 @@ OBJS = execAmi.o execCurrent.o execExpr.o execExprInterp.o \
        nodeMaterial.o nodeMergeAppend.o nodeMergejoin.o nodeModifyTable.o \
        nodeNestloop.o nodeProjectSet.o nodeRecursiveunion.o nodeResult.o \
        nodeSamplescan.o nodeSeqscan.o nodeSetOp.o nodeSort.o nodeUnique.o \
-       nodeValuesscan.o nodeCtescan.o nodeWorktablescan.o \
+       nodeValuesscan.o \
+       nodeCtescan.o nodeNamedtuplestorescan.o nodeWorktablescan.o \
        nodeGroup.o nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o \
        nodeForeignscan.o nodeWindowAgg.o tstoreReceiver.o tqueue.o spi.o \
        nodeTableFuncscan.o
index 5d59f95a916891d83d8b77e4533e728b86b9543c..7e85c66da38b1f20bd5845ea502256621837de17 100644 (file)
@@ -38,6 +38,7 @@
 #include "executor/nodeMergeAppend.h"
 #include "executor/nodeMergejoin.h"
 #include "executor/nodeModifyTable.h"
+#include "executor/nodeNamedtuplestorescan.h"
 #include "executor/nodeNestloop.h"
 #include "executor/nodeProjectSet.h"
 #include "executor/nodeRecursiveunion.h"
@@ -211,6 +212,10 @@ ExecReScan(PlanState *node)
                        ExecReScanCteScan((CteScanState *) node);
                        break;
 
+               case T_NamedTuplestoreScanState:
+                       ExecReScanNamedTuplestoreScan((NamedTuplestoreScanState *) node);
+                       break;
+
                case T_WorkTableScanState:
                        ExecReScanWorkTableScan((WorkTableScanState *) node);
                        break;
@@ -571,6 +576,7 @@ ExecMaterializesOutput(NodeTag plantype)
                case T_FunctionScan:
                case T_TableFuncScan:
                case T_CteScan:
+               case T_NamedTuplestoreScan:
                case T_WorkTableScan:
                case T_Sort:
                        return true;
index f2995f2e7ba2242eb1c7e8246a09745fe53757c1..920b12072fb56eedf203f6c30fee865c78a2d9c3 100644 (file)
@@ -198,6 +198,11 @@ standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
 
        estate->es_sourceText = queryDesc->sourceText;
 
+       /*
+        * Fill in the query environment, if any, from queryDesc.
+        */
+       estate->es_queryEnv = queryDesc->queryEnv;
+
        /*
         * If non-read-only query, set the command ID to mark output tuples with
         */
index b91b663c46f6b7495ca357d710376ea997a22292..469a32c7b0d2e2cf8f19158d5c1b9ebeb1707a73 100644 (file)
@@ -710,7 +710,7 @@ ExecParallelGetQueryDesc(shm_toc *toc, DestReceiver *receiver,
        return CreateQueryDesc(pstmt,
                                                   queryString,
                                                   GetActiveSnapshot(), InvalidSnapshot,
-                                                  receiver, paramLI, instrument_options);
+                                                  receiver, paramLI, NULL, instrument_options);
 }
 
 /*
index 80c77addb8e60311783109dc3675332e883ba342..486ddf1762856a1f83a53927087e8a05d7920682 100644 (file)
 #include "executor/nodeMergeAppend.h"
 #include "executor/nodeMergejoin.h"
 #include "executor/nodeModifyTable.h"
+#include "executor/nodeNamedtuplestorescan.h"
 #include "executor/nodeNestloop.h"
 #include "executor/nodeProjectSet.h"
 #include "executor/nodeRecursiveunion.h"
@@ -256,6 +257,11 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
                                                                                                   estate, eflags);
                        break;
 
+               case T_NamedTuplestoreScan:
+                       result = (PlanState *) ExecInitNamedTuplestoreScan((NamedTuplestoreScan *) node,
+                                                                                                  estate, eflags);
+                       break;
+
                case T_WorkTableScan:
                        result = (PlanState *) ExecInitWorkTableScan((WorkTableScan *) node,
                                                                                                                 estate, eflags);
@@ -483,6 +489,10 @@ ExecProcNode(PlanState *node)
                        result = ExecCteScan((CteScanState *) node);
                        break;
 
+               case T_NamedTuplestoreScanState:
+                       result = ExecNamedTuplestoreScan((NamedTuplestoreScanState *) node);
+                       break;
+
                case T_WorkTableScanState:
                        result = ExecWorkTableScan((WorkTableScanState *) node);
                        break;
@@ -751,6 +761,10 @@ ExecEndNode(PlanState *node)
                        ExecEndCteScan((CteScanState *) node);
                        break;
 
+               case T_NamedTuplestoreScanState:
+                       ExecEndNamedTuplestoreScan((NamedTuplestoreScanState *) node);
+                       break;
+
                case T_WorkTableScanState:
                        ExecEndWorkTableScan((WorkTableScanState *) node);
                        break;
index 2613ffbb718a6fc9ddbbbfb77da791648b2aacc8..ce7b0642172689f040ed692b8412908a5db00577 100644 (file)
@@ -120,6 +120,8 @@ CreateExecutorState(void)
        estate->es_param_list_info = NULL;
        estate->es_param_exec_vals = NULL;
 
+       estate->es_queryEnv = NULL;
+
        estate->es_query_cxt = qcontext;
 
        estate->es_tupleTable = NIL;
index 3e4b0191c7ecdef3005ee2bd590875dbe54db8a3..3cadf9530403d5a9a9180dd7366e5232f0a208f8 100644 (file)
@@ -713,7 +713,8 @@ init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK)
                queryTree_sublist = pg_analyze_and_rewrite_params(parsetree,
                                                                                                                  fcache->src,
                                                                           (ParserSetupHook) sql_fn_parser_setup,
-                                                                                                                 fcache->pinfo);
+                                                                                                                 fcache->pinfo,
+                                                                                                                 NULL);
                queryTree_list = lappend(queryTree_list, queryTree_sublist);
                flat_query_list = list_concat(flat_query_list,
                                                                          list_copy(queryTree_sublist));
@@ -809,7 +810,9 @@ postquel_start(execution_state *es, SQLFunctionCachePtr fcache)
                                                         GetActiveSnapshot(),
                                                         InvalidSnapshot,
                                                         dest,
-                                                        fcache->paramLI, 0);
+                                                        fcache->paramLI,
+                                                        es->qd ? es->qd->queryEnv : NULL,
+                                                        0);
 
        /* Utility commands don't need Executor. */
        if (es->qd->operation != CMD_UTILITY)
@@ -846,6 +849,7 @@ postquel_getnext(execution_state *es, SQLFunctionCachePtr fcache)
                                           fcache->src,
                                           PROCESS_UTILITY_QUERY,
                                           es->qd->params,
+                                          es->qd->queryEnv,
                                           es->qd->dest,
                                           NULL);
                result = true;                  /* never stops early */
diff --git a/src/backend/executor/nodeNamedtuplestorescan.c b/src/backend/executor/nodeNamedtuplestorescan.c
new file mode 100644 (file)
index 0000000..917b051
--- /dev/null
@@ -0,0 +1,198 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeNamedtuplestorescan.c
+ *       routines to handle NamedTuplestoreScan nodes.
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *       src/backend/executor/nodeNamedtuplestorescan.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "executor/execdebug.h"
+#include "executor/nodeNamedtuplestorescan.h"
+#include "miscadmin.h"
+#include "utils/queryenvironment.h"
+
+static TupleTableSlot *NamedTuplestoreScanNext(NamedTuplestoreScanState *node);
+
+/* ----------------------------------------------------------------
+ *             NamedTuplestoreScanNext
+ *
+ *             This is a workhorse for ExecNamedTuplestoreScan
+ * ----------------------------------------------------------------
+ */
+static TupleTableSlot *
+NamedTuplestoreScanNext(NamedTuplestoreScanState *node)
+{
+       TupleTableSlot *slot;
+
+       /* We intentionally do not support backward scan. */
+       Assert(ScanDirectionIsForward(node->ss.ps.state->es_direction));
+
+       /*
+        * Get the next tuple from tuplestore. Return NULL if no more tuples.
+        */
+       slot = node->ss.ss_ScanTupleSlot;
+       (void) tuplestore_gettupleslot(node->relation, true, false, slot);
+       return slot;
+}
+
+/*
+ * NamedTuplestoreScanRecheck -- access method routine to recheck a tuple in
+ * EvalPlanQual
+ */
+static bool
+NamedTuplestoreScanRecheck(NamedTuplestoreScanState *node, TupleTableSlot *slot)
+{
+       /* nothing to check */
+       return true;
+}
+
+/* ----------------------------------------------------------------
+ *             ExecNamedTuplestoreScan(node)
+ *
+ *             Scans the CTE sequentially and returns the next qualifying tuple.
+ *             We call the ExecScan() routine and pass it the appropriate
+ *             access method functions.
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *
+ExecNamedTuplestoreScan(NamedTuplestoreScanState *node)
+{
+       return ExecScan(&node->ss,
+                                       (ExecScanAccessMtd) NamedTuplestoreScanNext,
+                                       (ExecScanRecheckMtd) NamedTuplestoreScanRecheck);
+}
+
+
+/* ----------------------------------------------------------------
+ *             ExecInitNamedTuplestoreScan
+ * ----------------------------------------------------------------
+ */
+NamedTuplestoreScanState *
+ExecInitNamedTuplestoreScan(NamedTuplestoreScan *node, EState *estate, int eflags)
+{
+       NamedTuplestoreScanState *scanstate;
+       EphemeralNamedRelation enr;
+
+       /* check for unsupported flags */
+       Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
+
+       /*
+        * NamedTuplestoreScan should not have any children.
+        */
+       Assert(outerPlan(node) == NULL);
+       Assert(innerPlan(node) == NULL);
+
+       /*
+        * create new NamedTuplestoreScanState for node
+        */
+       scanstate = makeNode(NamedTuplestoreScanState);
+       scanstate->ss.ps.plan = (Plan *) node;
+       scanstate->ss.ps.state = estate;
+
+       enr = get_ENR(estate->es_queryEnv, node->enrname);
+       if (!enr)
+               elog(ERROR, "executor could not find named tuplestore \"%s\"",
+                        node->enrname);
+
+       Assert(enr->reldata);
+       scanstate->relation = (Tuplestorestate *) enr->reldata;
+       scanstate->tupdesc = ENRMetadataGetTupDesc(&(enr->md));
+       scanstate->readptr =
+               tuplestore_alloc_read_pointer(scanstate->relation, 0);
+
+       /*
+        * The new read pointer copies its position from read pointer 0, which
+        * could be anywhere, so explicitly rewind it.
+        */
+       tuplestore_rescan(scanstate->relation);
+
+       /*
+        * XXX: Should we add a function to free that read pointer when done?
+        * This was attempted, but it did not improve performance or memory usage
+        * in any tested cases.
+        */
+
+       /*
+        * Miscellaneous initialization
+        *
+        * create expression context for node
+        */
+       ExecAssignExprContext(estate, &scanstate->ss.ps);
+
+       /*
+        * initialize child expressions
+        */
+       scanstate->ss.ps.qual =
+               ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
+
+       /*
+        * tuple table initialization
+        */
+       ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
+       ExecInitScanTupleSlot(estate, &scanstate->ss);
+
+       /*
+        * The scan tuple type is specified for the tuplestore.
+        */
+       ExecAssignScanType(&scanstate->ss, scanstate->tupdesc);
+
+       /*
+        * Initialize result tuple type and projection info.
+        */
+       ExecAssignResultTypeFromTL(&scanstate->ss.ps);
+       ExecAssignScanProjectionInfo(&scanstate->ss);
+
+       return scanstate;
+}
+
+/* ----------------------------------------------------------------
+ *             ExecEndNamedTuplestoreScan
+ *
+ *             frees any storage allocated through C routines.
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndNamedTuplestoreScan(NamedTuplestoreScanState *node)
+{
+       /*
+        * Free exprcontext
+        */
+       ExecFreeExprContext(&node->ss.ps);
+
+       /*
+        * clean out the tuple table
+        */
+       ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
+       ExecClearTuple(node->ss.ss_ScanTupleSlot);
+}
+
+/* ----------------------------------------------------------------
+ *             ExecReScanNamedTuplestoreScan
+ *
+ *             Rescans the relation.
+ * ----------------------------------------------------------------
+ */
+void
+ExecReScanNamedTuplestoreScan(NamedTuplestoreScanState *node)
+{
+       Tuplestorestate *tuplestorestate = node->relation;
+
+       ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
+
+       ExecScanReScan(&node->ss);
+
+       /*
+        * Rewind my own pointer.
+        */
+       tuplestore_select_read_pointer(tuplestorestate, node->readptr);
+       tuplestore_rescan(tuplestorestate);
+}
index eeaa4805e4d28db678158c25768b677198adc956..54c022d0132c72b89ccc4d6c56584b87a784e65b 100644 (file)
@@ -122,6 +122,7 @@ SPI_connect(void)
        _SPI_current->procCxt = NULL;           /* in case we fail to create 'em */
        _SPI_current->execCxt = NULL;
        _SPI_current->connectSubid = GetCurrentSubTransactionId();
+       _SPI_current->queryEnv = NULL;
 
        /*
         * Create memory contexts for this procedure
@@ -1193,7 +1194,7 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
         */
 
        /* Replan if needed, and increment plan refcount for portal */
-       cplan = GetCachedPlan(plansource, paramLI, false);
+       cplan = GetCachedPlan(plansource, paramLI, false, _SPI_current->queryEnv);
        stmt_list = cplan->stmt_list;
 
        /* Pop the error context stack */
@@ -1532,6 +1533,10 @@ SPI_result_code_string(int code)
                        return "SPI_ERROR_NOOUTFUNC";
                case SPI_ERROR_TYPUNKNOWN:
                        return "SPI_ERROR_TYPUNKNOWN";
+               case SPI_ERROR_REL_DUPLICATE:
+                       return "SPI_ERROR_REL_DUPLICATE";
+               case SPI_ERROR_REL_NOT_FOUND:
+                       return "SPI_ERROR_REL_NOT_FOUND";
                case SPI_OK_CONNECT:
                        return "SPI_OK_CONNECT";
                case SPI_OK_FINISH:
@@ -1560,6 +1565,10 @@ SPI_result_code_string(int code)
                        return "SPI_OK_UPDATE_RETURNING";
                case SPI_OK_REWRITTEN:
                        return "SPI_OK_REWRITTEN";
+               case SPI_OK_REL_REGISTER:
+                       return "SPI_OK_REL_REGISTER";
+               case SPI_OK_REL_UNREGISTER:
+                       return "SPI_OK_REL_UNREGISTER";
        }
        /* Unrecognized code ... return something useful ... */
        sprintf(buf, "Unrecognized SPI code %d", code);
@@ -1615,7 +1624,8 @@ SPI_plan_get_cached_plan(SPIPlanPtr plan)
        error_context_stack = &spierrcontext;
 
        /* Get the generic plan for the query */
-       cplan = GetCachedPlan(plansource, NULL, plan->saved);
+       cplan = GetCachedPlan(plansource, NULL, plan->saved,
+                                                 _SPI_current->queryEnv);
        Assert(cplan == plansource->gplan);
 
        /* Pop the error context stack */
@@ -1767,7 +1777,8 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan)
                 */
                plansource = CreateCachedPlan(parsetree,
                                                                          src,
-                                                                         CreateCommandTag(parsetree->stmt));
+                                                                         CreateCommandTag(parsetree->stmt),
+                                                                         _SPI_current->queryEnv);
 
                /*
                 * Parameter datatypes are driven by parserSetup hook if provided,
@@ -1779,14 +1790,16 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan)
                        stmt_list = pg_analyze_and_rewrite_params(parsetree,
                                                                                                          src,
                                                                                                          plan->parserSetup,
-                                                                                                         plan->parserSetupArg);
+                                                                                                         plan->parserSetupArg,
+                                                                                                         _SPI_current->queryEnv);
                }
                else
                {
                        stmt_list = pg_analyze_and_rewrite(parsetree,
                                                                                           src,
                                                                                           plan->argtypes,
-                                                                                          plan->nargs);
+                                                                                          plan->nargs,
+                                                                                          _SPI_current->queryEnv);
                }
 
                /* Finish filling in the CachedPlanSource */
@@ -1975,14 +1988,16 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
                                stmt_list = pg_analyze_and_rewrite_params(parsetree,
                                                                                                                  src,
                                                                                                                  plan->parserSetup,
-                                                                                                          plan->parserSetupArg);
+                                                                                                          plan->parserSetupArg,
+                                                                                                         _SPI_current->queryEnv);
                        }
                        else
                        {
                                stmt_list = pg_analyze_and_rewrite(parsetree,
                                                                                                   src,
                                                                                                   plan->argtypes,
-                                                                                                  plan->nargs);
+                                                                                                  plan->nargs,
+                                                                                                  _SPI_current->queryEnv);
                        }
 
                        /* Finish filling in the CachedPlanSource */
@@ -2001,7 +2016,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
                 * Replan if needed, and increment plan refcount.  If it's a saved
                 * plan, the refcount must be backed by the CurrentResourceOwner.
                 */
-               cplan = GetCachedPlan(plansource, paramLI, plan->saved);
+               cplan = GetCachedPlan(plansource, paramLI, plan->saved, _SPI_current->queryEnv);
                stmt_list = cplan->stmt_list;
 
                /*
@@ -2081,7 +2096,8 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
                                                                                plansource->query_string,
                                                                                snap, crosscheck_snapshot,
                                                                                dest,
-                                                                               paramLI, 0);
+                                                                               paramLI, _SPI_current->queryEnv,
+                                                                               0);
                                res = _SPI_pquery(qdesc, fire_triggers,
                                                                  canSetTag ? tcount : 0);
                                FreeQueryDesc(qdesc);
@@ -2094,6 +2110,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
                                                           plansource->query_string,
                                                           PROCESS_UTILITY_QUERY,
                                                           paramLI,
+                                                          _SPI_current->queryEnv,
                                                           dest,
                                                           completionTag);
 
@@ -2619,3 +2636,84 @@ _SPI_save_plan(SPIPlanPtr plan)
 
        return newplan;
 }
+
+/*
+ * Internal lookup of ephemeral named relation by name.
+ */
+static EphemeralNamedRelation
+_SPI_find_ENR_by_name(const char *name)
+{
+       /* internal static function; any error is bug in SPI itself */
+       Assert(name != NULL);
+
+       /* fast exit if no tuplestores have been added */
+       if (_SPI_current->queryEnv == NULL)
+               return NULL;
+
+       return get_ENR(_SPI_current->queryEnv, name);
+}
+
+/*
+ * Register an ephemeral named relation for use by the planner and executor on
+ * subsequent calls using this SPI connection.
+ */
+int
+SPI_register_relation(EphemeralNamedRelation enr)
+{
+       EphemeralNamedRelation match;
+       int                     res;
+
+       if (enr == NULL || enr->md.name == NULL)
+               return SPI_ERROR_ARGUMENT;
+
+       res = _SPI_begin_call(false);   /* keep current memory context */
+       if (res < 0)
+               return res;
+
+       match = _SPI_find_ENR_by_name(enr->md.name);
+       if (match)
+               res = SPI_ERROR_REL_DUPLICATE;
+       else
+       {
+               if (_SPI_current->queryEnv == NULL)
+                       _SPI_current->queryEnv = create_queryEnv();
+
+               register_ENR(_SPI_current->queryEnv, enr);
+               res = SPI_OK_REL_REGISTER;
+       }
+
+       _SPI_end_call(false);
+
+       return res;
+}
+
+/*
+ * Unregister an ephemeral named relation by name.  This will probably be a
+ * rarely used function, since SPI_finish will clear it automatically.
+ */
+int
+SPI_unregister_relation(const char *name)
+{
+       EphemeralNamedRelation match;
+       int                     res;
+
+       if (name == NULL)
+               return SPI_ERROR_ARGUMENT;
+
+       res = _SPI_begin_call(false);   /* keep current memory context */
+       if (res < 0)
+               return res;
+
+       match = _SPI_find_ENR_by_name(name);
+       if (match)
+       {
+               unregister_ENR(_SPI_current->queryEnv, match->md.name);
+               res = SPI_OK_REL_UNREGISTER;
+       }
+       else
+               res = SPI_ERROR_REL_NOT_FOUND;
+
+       _SPI_end_call(false);
+
+       return res;
+}
index 1c88d601bd1c30479699badb3e33a6c7fcc7baa8..61bc5025e200fbbb4bc8c858cf48fb9b0244b815 100644 (file)
@@ -682,6 +682,27 @@ _copyCteScan(const CteScan *from)
        return newnode;
 }
 
+/*
+ * _copyNamedTuplestoreScan
+ */
+static NamedTuplestoreScan *
+_copyNamedTuplestoreScan(const NamedTuplestoreScan *from)
+{
+       NamedTuplestoreScan    *newnode = makeNode(NamedTuplestoreScan);
+
+       /*
+        * copy node superclass fields
+        */
+       CopyScanFields((const Scan *) from, (Scan *) newnode);
+
+       /*
+        * copy remainder of node
+        */
+       COPY_STRING_FIELD(enrname);
+
+       return newnode;
+}
+
 /*
  * _copyWorkTableScan
  */
@@ -2265,6 +2286,7 @@ _copyRangeTblEntry(const RangeTblEntry *from)
        COPY_STRING_FIELD(ctename);
        COPY_SCALAR_FIELD(ctelevelsup);
        COPY_SCALAR_FIELD(self_reference);
+       COPY_STRING_FIELD(enrname);
        COPY_NODE_FIELD(coltypes);
        COPY_NODE_FIELD(coltypmods);
        COPY_NODE_FIELD(colcollations);
@@ -4706,6 +4728,9 @@ copyObjectImpl(const void *from)
                case T_CteScan:
                        retval = _copyCteScan(from);
                        break;
+               case T_NamedTuplestoreScan:
+                       retval = _copyNamedTuplestoreScan(from);
+                       break;
                case T_WorkTableScan:
                        retval = _copyWorkTableScan(from);
                        break;
index 6e52eb7231d73b3258da57f072f053a2dac9ff0b..d5293a1a7816fe7ce4ed46813d73394e64df1506 100644 (file)
@@ -2321,6 +2321,7 @@ range_table_walker(List *rtable,
                                        return true;
                                break;
                        case RTE_CTE:
+                       case RTE_NAMEDTUPLESTORE:
                                /* nothing to do */
                                break;
                        case RTE_SUBQUERY:
@@ -3135,6 +3136,7 @@ range_table_mutator(List *rtable,
                                /* we don't bother to copy eref, aliases, etc; OK? */
                                break;
                        case RTE_CTE:
+                       case RTE_NAMEDTUPLESTORE:
                                /* nothing to do */
                                break;
                        case RTE_SUBQUERY:
index 0b45c25a49951a925dac2e26c35b3f9b31041996..766ca49216f0761b3edd985a78b992fec072cf27 100644 (file)
@@ -631,6 +631,16 @@ _outCteScan(StringInfo str, const CteScan *node)
        WRITE_INT_FIELD(cteParam);
 }
 
+static void
+_outNamedTuplestoreScan(StringInfo str, const NamedTuplestoreScan *node)
+{
+       WRITE_NODE_TYPE("NAMEDTUPLESTORESCAN");
+
+       _outScanInfo(str, (const Scan *) node);
+
+       WRITE_STRING_FIELD(enrname);
+}
+
 static void
 _outWorkTableScan(StringInfo str, const WorkTableScan *node)
 {
@@ -3024,6 +3034,13 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
                        WRITE_NODE_FIELD(coltypmods);
                        WRITE_NODE_FIELD(colcollations);
                        break;
+               case RTE_NAMEDTUPLESTORE:
+                       WRITE_STRING_FIELD(enrname);
+                       WRITE_OID_FIELD(relid);
+                       WRITE_NODE_FIELD(coltypes);
+                       WRITE_NODE_FIELD(coltypmods);
+                       WRITE_NODE_FIELD(colcollations);
+                       break;
                default:
                        elog(ERROR, "unrecognized RTE kind: %d", (int) node->rtekind);
                        break;
@@ -3621,6 +3638,9 @@ outNode(StringInfo str, const void *obj)
                        case T_CteScan:
                                _outCteScan(str, obj);
                                break;
+                       case T_NamedTuplestoreScan:
+                               _outNamedTuplestoreScan(str, obj);
+                               break;
                        case T_WorkTableScan:
                                _outWorkTableScan(str, obj);
                                break;
index dfb8bfa8034f44baaa3bf290c44fd3d00352d3ff..380e8b71f26426c7bf572a47ea84b0a9c03eab55 100644 (file)
@@ -291,6 +291,10 @@ print_rt(const List *rtable)
                                printf("%d\t%s\t[cte]",
                                           i, rte->eref->aliasname);
                                break;
+                       case RTE_NAMEDTUPLESTORE:
+                               printf("%d\t%s\t[tuplestore]",
+                                          i, rte->eref->aliasname);
+                               break;
                        default:
                                printf("%d\t%s\t[unknown rtekind]",
                                           i, rte->eref->aliasname);
index 474f221a75f7be7e4e547c742bbdd565ea906477..766f2d8db15ff3a7570aea8182325b6c11731ddc 100644 (file)
@@ -1355,6 +1355,13 @@ _readRangeTblEntry(void)
                        READ_NODE_FIELD(coltypmods);
                        READ_NODE_FIELD(colcollations);
                        break;
+               case RTE_NAMEDTUPLESTORE:
+                       READ_STRING_FIELD(enrname);
+                       READ_OID_FIELD(relid);
+                       READ_NODE_FIELD(coltypes);
+                       READ_NODE_FIELD(coltypmods);
+                       READ_NODE_FIELD(colcollations);
+                       break;
                default:
                        elog(ERROR, "unrecognized RTE kind: %d",
                                 (int) local_node->rtekind);
index a1e1a87c29338714bfed6cea905faba7584a900b..343b35aa326a40e741c8970662f275d14c4ced64 100644 (file)
@@ -111,6 +111,8 @@ static void set_tablefunc_pathlist(PlannerInfo *root, RelOptInfo *rel,
                                           RangeTblEntry *rte);
 static void set_cte_pathlist(PlannerInfo *root, RelOptInfo *rel,
                                 RangeTblEntry *rte);
+static void set_namedtuplestore_pathlist(PlannerInfo *root, RelOptInfo *rel,
+                                RangeTblEntry *rte);
 static void set_worktable_pathlist(PlannerInfo *root, RelOptInfo *rel,
                                           RangeTblEntry *rte);
 static RelOptInfo *make_rel_from_joinlist(PlannerInfo *root, List *joinlist);
@@ -396,6 +398,9 @@ set_rel_size(PlannerInfo *root, RelOptInfo *rel,
                                else
                                        set_cte_pathlist(root, rel, rte);
                                break;
+                       case RTE_NAMEDTUPLESTORE:
+                               set_namedtuplestore_pathlist(root, rel, rte);
+                               break;
                        default:
                                elog(ERROR, "unexpected rtekind: %d", (int) rel->rtekind);
                                break;
@@ -464,6 +469,9 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
                        case RTE_CTE:
                                /* CTE reference --- fully handled during set_rel_size */
                                break;
+                       case RTE_NAMEDTUPLESTORE:
+                               /* tuplestore reference --- fully handled during set_rel_size */
+                               break;
                        default:
                                elog(ERROR, "unexpected rtekind: %d", (int) rel->rtekind);
                                break;
@@ -639,6 +647,13 @@ set_rel_consider_parallel(PlannerInfo *root, RelOptInfo *rel,
                         * executed only once.
                         */
                        return;
+
+               case RTE_NAMEDTUPLESTORE:
+                       /*
+                        * tuplestore cannot be shared, at least without more
+                        * infrastructure to support that.
+                        */
+                       return;
        }
 
        /*
@@ -2089,6 +2104,36 @@ set_cte_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
        add_path(rel, create_ctescan_path(root, rel, required_outer));
 }
 
+/*
+ * set_namedtuplestore_pathlist
+ *             Build the (single) access path for a named tuplestore RTE
+ *
+ * There's no need for a separate set_namedtuplestore_size phase, since we
+ * don't support join-qual-parameterized paths for tuplestores.
+ */
+static void
+set_namedtuplestore_pathlist(PlannerInfo *root, RelOptInfo *rel,
+                                                        RangeTblEntry *rte)
+{
+       Relids          required_outer;
+
+       /* Mark rel with estimated output rows, width, etc */
+       set_namedtuplestore_size_estimates(root, rel);
+
+       /*
+        * We don't support pushing join clauses into the quals of a tuplestore
+        * scan, but it could still have required parameterization due to LATERAL
+        * refs in its tlist.
+        */
+       required_outer = rel->lateral_relids;
+
+       /* Generate appropriate path */
+       add_path(rel, create_namedtuplestorescan_path(root, rel, required_outer));
+
+       /* Select cheapest path (pretty easy in this case...) */
+       set_cheapest(rel);
+}
+
 /*
  * set_worktable_pathlist
  *             Build the (single) access path for a self-reference CTE RTE
index 92de2b7d4808e145e0b44dbea9d2a5cad204bfcb..ed07e2f6559b8a9986eaf97ed516e4b894cd567f 100644 (file)
@@ -1516,6 +1516,43 @@ cost_ctescan(Path *path, PlannerInfo *root,
        path->total_cost = startup_cost + run_cost;
 }
 
+/*
+ * cost_namedtuplestorescan
+ *       Determines and returns the cost of scanning a named tuplestore.
+ */
+void
+cost_namedtuplestorescan(Path *path, PlannerInfo *root,
+                                                RelOptInfo *baserel, ParamPathInfo *param_info)
+{
+       Cost            startup_cost = 0;
+       Cost            run_cost = 0;
+       QualCost        qpqual_cost;
+       Cost            cpu_per_tuple;
+
+       /* Should only be applied to base relations that are Tuplestores */
+       Assert(baserel->relid > 0);
+       Assert(baserel->rtekind == RTE_NAMEDTUPLESTORE);
+
+       /* Mark the path with the correct row estimate */
+       if (param_info)
+               path->rows = param_info->ppi_rows;
+       else
+               path->rows = baserel->rows;
+
+       /* Charge one CPU tuple cost per row for tuplestore manipulation */
+       cpu_per_tuple = cpu_tuple_cost;
+
+       /* Add scanning CPU costs */
+       get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost);
+
+       startup_cost += qpqual_cost.startup;
+       cpu_per_tuple += cpu_tuple_cost + qpqual_cost.per_tuple;
+       run_cost += cpu_per_tuple * baserel->tuples;
+
+       path->startup_cost = startup_cost;
+       path->total_cost = startup_cost + run_cost;
+}
+
 /*
  * cost_recursive_union
  *       Determines and returns the cost of performing a recursive union,
@@ -4684,6 +4721,39 @@ set_cte_size_estimates(PlannerInfo *root, RelOptInfo *rel, double cte_rows)
        set_baserel_size_estimates(root, rel);
 }
 
+/*
+ * set_namedtuplestore_size_estimates
+ *             Set the size estimates for a base relation that is a tuplestore reference.
+ *
+ * The rel's targetlist and restrictinfo list must have been constructed
+ * already.
+ *
+ * We set the same fields as set_baserel_size_estimates.
+ */
+void
+set_namedtuplestore_size_estimates(PlannerInfo *root, RelOptInfo *rel)
+{
+       RangeTblEntry *rte;
+
+       /* Should only be applied to base relations that are tuplestore references */
+       Assert(rel->relid > 0);
+       rte = planner_rt_fetch(rel->relid, root);
+       Assert(rte->rtekind == RTE_NAMEDTUPLESTORE);
+
+       /*
+        * Use the estimate provided by the code which is generating the named
+        * tuplestore.  In some cases, the actual number might be available; in
+        * others the same plan will be re-used, so a "typical" value might be
+        * estimated and used.
+        */
+       rel->tuples = rte->enrtuples;
+       if (rel->tuples < 0)
+               rel->tuples = 1000;
+
+       /* Now estimate number of output rows, etc */
+       set_baserel_size_estimates(root, rel);
+}
+
 /*
  * set_foreign_size_estimates
  *             Set the size estimates for a base relation that is a foreign table.
index ed06a8de7873ed71abeaa03d0454699f3ad27928..2a78595e1f2cc76c8f0f0342972d752225a6ae6b 100644 (file)
@@ -139,6 +139,8 @@ static TableFuncScan *create_tablefuncscan_plan(PlannerInfo *root, Path *best_pa
                                                  List *tlist, List *scan_clauses);
 static CteScan *create_ctescan_plan(PlannerInfo *root, Path *best_path,
                                        List *tlist, List *scan_clauses);
+static NamedTuplestoreScan *create_namedtuplestorescan_plan(PlannerInfo *root,
+                                       Path *best_path, List *tlist, List *scan_clauses);
 static WorkTableScan *create_worktablescan_plan(PlannerInfo *root, Path *best_path,
                                                  List *tlist, List *scan_clauses);
 static ForeignScan *create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
@@ -197,6 +199,8 @@ static TableFuncScan *make_tablefuncscan(List *qptlist, List *qpqual,
                                   Index scanrelid, TableFunc *tablefunc);
 static CteScan *make_ctescan(List *qptlist, List *qpqual,
                         Index scanrelid, int ctePlanId, int cteParam);
+static NamedTuplestoreScan *make_namedtuplestorescan(List *qptlist, List *qpqual,
+                        Index scanrelid, char *enrname);
 static WorkTableScan *make_worktablescan(List *qptlist, List *qpqual,
                                   Index scanrelid, int wtParam);
 static Append *make_append(List *appendplans, List *tlist, List *partitioned_rels);
@@ -366,6 +370,7 @@ create_plan_recurse(PlannerInfo *root, Path *best_path, int flags)
                case T_ValuesScan:
                case T_CteScan:
                case T_WorkTableScan:
+               case T_NamedTuplestoreScan:
                case T_ForeignScan:
                case T_CustomScan:
                        plan = create_scan_plan(root, best_path, flags);
@@ -668,6 +673,13 @@ create_scan_plan(PlannerInfo *root, Path *best_path, int flags)
                                                                                                scan_clauses);
                        break;
 
+               case T_NamedTuplestoreScan:
+                       plan = (Plan *) create_namedtuplestorescan_plan(root,
+                                                                                                                       best_path,
+                                                                                                                       tlist,
+                                                                                                                       scan_clauses);
+                       break;
+
                case T_WorkTableScan:
                        plan = (Plan *) create_worktablescan_plan(root,
                                                                                                          best_path,
@@ -3285,6 +3297,45 @@ create_ctescan_plan(PlannerInfo *root, Path *best_path,
        return scan_plan;
 }
 
+/*
+ * create_namedtuplestorescan_plan
+ *      Returns a tuplestorescan plan for the base relation scanned by
+ *     'best_path' with restriction clauses 'scan_clauses' and targetlist
+ *     'tlist'.
+ */
+static NamedTuplestoreScan *
+create_namedtuplestorescan_plan(PlannerInfo *root, Path *best_path,
+                                                               List *tlist, List *scan_clauses)
+{
+       NamedTuplestoreScan *scan_plan;
+       Index           scan_relid = best_path->parent->relid;
+       RangeTblEntry *rte;
+
+       Assert(scan_relid > 0);
+       rte = planner_rt_fetch(scan_relid, root);
+       Assert(rte->rtekind == RTE_NAMEDTUPLESTORE);
+
+       /* Sort clauses into best execution order */
+       scan_clauses = order_qual_clauses(root, scan_clauses);
+
+       /* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
+       scan_clauses = extract_actual_clauses(scan_clauses, false);
+
+       /* Replace any outer-relation variables with nestloop params */
+       if (best_path->param_info)
+       {
+               scan_clauses = (List *)
+                       replace_nestloop_params(root, (Node *) scan_clauses);
+       }
+
+       scan_plan = make_namedtuplestorescan(tlist, scan_clauses, scan_relid,
+                                                                                rte->enrname);
+
+       copy_generic_path_info(&scan_plan->scan.plan, best_path);
+
+       return scan_plan;
+}
+
 /*
  * create_worktablescan_plan
  *      Returns a worktablescan plan for the base relation scanned by 'best_path'
@@ -5120,6 +5171,26 @@ make_ctescan(List *qptlist,
        return node;
 }
 
+static NamedTuplestoreScan *
+make_namedtuplestorescan(List *qptlist,
+                                                List *qpqual,
+                                                Index scanrelid,
+                                                char *enrname)
+{
+       NamedTuplestoreScan *node = makeNode(NamedTuplestoreScan);
+       Plan       *plan = &node->scan.plan;
+
+       /* cost should be inserted by caller */
+       plan->targetlist = qptlist;
+       plan->qual = qpqual;
+       plan->lefttree = NULL;
+       plan->righttree = NULL;
+       node->scan.scanrelid = scanrelid;
+       node->enrname = enrname;
+
+       return node;
+}
+
 static WorkTableScan *
 make_worktablescan(List *qptlist,
                                   List *qpqual,
index 4e3f6ee960f469d4487b9df6f295b07480c1e6b4..cdb8e95deb7fb5bac97e8eb11121375c95371e5e 100644 (file)
@@ -591,6 +591,17 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
                                        fix_scan_list(root, splan->scan.plan.qual, rtoffset);
                        }
                        break;
+               case T_NamedTuplestoreScan:
+                       {
+                               NamedTuplestoreScan *splan = (NamedTuplestoreScan *) plan;
+
+                               splan->scan.scanrelid += rtoffset;
+                               splan->scan.plan.targetlist =
+                                       fix_scan_list(root, splan->scan.plan.targetlist, rtoffset);
+                               splan->scan.plan.qual =
+                                       fix_scan_list(root, splan->scan.plan.qual, rtoffset);
+                       }
+                       break;
                case T_WorkTableScan:
                        {
                                WorkTableScan *splan = (WorkTableScan *) plan;
@@ -2571,6 +2582,11 @@ extract_query_dependencies_walker(Node *node, PlannerInfo *context)
                        if (rte->rtekind == RTE_RELATION)
                                context->glob->relationOids =
                                        lappend_oid(context->glob->relationOids, rte->relid);
+                       else if (rte->rtekind == RTE_NAMEDTUPLESTORE &&
+                                        OidIsValid(rte->relid))
+                               context->glob->relationOids =
+                                       lappend_oid(context->glob->relationOids,
+                                                               rte->relid);
                }
 
                /* And recurse into the query's subexpressions */
index db0e5b31e2e78d3bf4f19abf04bf0d1c08ceb1c7..87cc44d6780ffe66921d9ec27d18fce3e021e846 100644 (file)
@@ -2476,6 +2476,10 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
                        context.paramids = bms_add_members(context.paramids, scan_params);
                        break;
 
+               case T_NamedTuplestoreScan:
+                       context.paramids = bms_add_members(context.paramids, scan_params);
+                       break;
+
                case T_ForeignScan:
                        {
                                ForeignScan *fscan = (ForeignScan *) plan;
index 348c6b791f42c93ad099b40cdb2ac125b8df7474..749ea805f824a717943267cb58b2ce97852af682 100644 (file)
@@ -1123,6 +1123,7 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
                                        break;
                                case RTE_JOIN:
                                case RTE_CTE:
+                               case RTE_NAMEDTUPLESTORE:
                                        /* these can't contain any lateral references */
                                        break;
                        }
@@ -1977,6 +1978,7 @@ replace_vars_in_jointree(Node *jtnode,
                                                break;
                                        case RTE_JOIN:
                                        case RTE_CTE:
+                                       case RTE_NAMEDTUPLESTORE:
                                                /* these shouldn't be marked LATERAL */
                                                Assert(false);
                                                break;
index a578867cced4bf2f6c3f5811a1e68271b128ace1..59d71c1b3203e02a978bff3d4984f9b708b79a4b 100644 (file)
@@ -4910,7 +4910,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
        querytree_list = pg_analyze_and_rewrite_params(linitial(raw_parsetree_list),
                                                                                                   src,
                                                                           (ParserSetupHook) sql_fn_parser_setup,
-                                                                                                  pinfo);
+                                                                                                  pinfo, NULL);
        if (list_length(querytree_list) != 1)
                goto fail;
        querytree = linitial(querytree_list);
index c6298072c9b5f3c28b8ef82c713b87e6b9bef620..85362121774ac8dfdad25f85c03efb1bb870e63e 100644 (file)
@@ -1892,6 +1892,32 @@ create_ctescan_path(PlannerInfo *root, RelOptInfo *rel, Relids required_outer)
        return pathnode;
 }
 
+/*
+ * create_namedtuplestorescan_path
+ *       Creates a path corresponding to a scan of a named tuplestore, returning
+ *       the pathnode.
+ */
+Path *
+create_namedtuplestorescan_path(PlannerInfo *root, RelOptInfo *rel,
+                                                               Relids required_outer)
+{
+       Path       *pathnode = makeNode(Path);
+
+       pathnode->pathtype = T_NamedTuplestoreScan;
+       pathnode->parent = rel;
+       pathnode->pathtarget = rel->reltarget;
+       pathnode->param_info = get_baserel_parampathinfo(root, rel,
+                                                                                                        required_outer);
+       pathnode->parallel_aware = false;
+       pathnode->parallel_safe = rel->consider_parallel;
+       pathnode->parallel_workers = 0;
+       pathnode->pathkeys = NIL;       /* result is always unordered */
+
+       cost_namedtuplestorescan(pathnode, root, rel, pathnode->param_info);
+
+       return pathnode;
+}
+
 /*
  * create_worktablescan_path
  *       Creates a path corresponding to a scan of a self-reference CTE,
index cc88dcc28e4a837ed6e6e075ae2bc1f00a9d26a7..1cd21c0fdcb1b10e30b108d2159d3dd3ddc483f5 100644 (file)
@@ -1446,9 +1446,9 @@ relation_excluded_by_constraints(PlannerInfo *root,
  * dropped cols.
  *
  * We also support building a "physical" tlist for subqueries, functions,
- * values lists, table expressions and CTEs, since the same optimization can
- * occur in SubqueryScan, FunctionScan, ValuesScan, CteScan, TableFunc
- * and WorkTableScan nodes.
+ * values lists, table expressions, and CTEs, since the same optimization can
+ * occur in SubqueryScan, FunctionScan, ValuesScan, CteScan, TableFunc,
+ * NamedTuplestoreScan, and WorkTableScan nodes.
  */
 List *
 build_physical_tlist(PlannerInfo *root, RelOptInfo *rel)
@@ -1523,6 +1523,7 @@ build_physical_tlist(PlannerInfo *root, RelOptInfo *rel)
                case RTE_TABLEFUNC:
                case RTE_VALUES:
                case RTE_CTE:
+               case RTE_NAMEDTUPLESTORE:
                        /* Not all of these can have dropped cols, but share code anyway */
                        expandRTE(rte, varno, 0, -1, true /* include dropped */ ,
                                          NULL, &colvars);
index 6ab78545c36d8b8fb3c3733979746bb69bb0a0f6..7912df0baaae5a3c3295864d3fdb284590df602f 100644 (file)
@@ -156,6 +156,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
                case RTE_TABLEFUNC:
                case RTE_VALUES:
                case RTE_CTE:
+               case RTE_NAMEDTUPLESTORE:
 
                        /*
                         * Subquery, function, tablefunc, or values list --- set up attr
index df9a9fbb35e4e460e4ba28689c9e87c5f1916847..4b97f838036ef05c7cab6ef570ac124c4bc69a5a 100644 (file)
@@ -14,8 +14,9 @@ override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
 
 OBJS= analyze.o gram.o scan.o parser.o \
       parse_agg.o parse_clause.o parse_coerce.o parse_collate.o parse_cte.o \
-      parse_expr.o parse_func.o parse_node.o parse_oper.o parse_param.o \
-      parse_relation.o parse_target.o parse_type.o parse_utilcmd.o scansup.o
+      parse_enr.o parse_expr.o parse_func.o parse_node.o parse_oper.o \
+      parse_param.o parse_relation.o parse_target.o parse_type.o \
+      parse_utilcmd.o scansup.o
 
 include $(top_srcdir)/src/backend/common.mk
 
index 8f11c46621e188c585c89ae826ea4413139c367e..811fccaec97191dd81c03e13e2aecda29dbf624b 100644 (file)
@@ -94,7 +94,8 @@ static bool test_raw_expression_coverage(Node *node, void *context);
  */
 Query *
 parse_analyze(RawStmt *parseTree, const char *sourceText,
-                         Oid *paramTypes, int numParams)
+                         Oid *paramTypes, int numParams,
+                         QueryEnvironment *queryEnv)
 {
        ParseState *pstate = make_parsestate(NULL);
        Query      *query;
@@ -106,6 +107,8 @@ parse_analyze(RawStmt *parseTree, const char *sourceText,
        if (numParams > 0)
                parse_fixed_parameters(pstate, paramTypes, numParams);
 
+       pstate->p_queryEnv = queryEnv;
+
        query = transformTopLevelStmt(pstate, parseTree);
 
        if (post_parse_analyze_hook)
@@ -2799,6 +2802,15 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc,
                                                                          LCS_asString(lc->strength)),
                                                         parser_errposition(pstate, thisrel->location)));
                                                        break;
+                                               case RTE_NAMEDTUPLESTORE:
+                                                       ereport(ERROR,
+                                                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                                       /*------
+                                                         translator: %s is a SQL row locking clause such as FOR UPDATE */
+                                                          errmsg("%s cannot be applied to a named tuplestore",
+                                                                         LCS_asString(lc->strength)),
+                                                        parser_errposition(pstate, thisrel->location)));
+                                                       break;
                                                default:
                                                        elog(ERROR, "unrecognized RTE type: %d",
                                                                 (int) rte->rtekind);
index 4f391d2d4110fba6f77b07da3cd86965842f4973..e268a127d130644fefe342c29acabedfa4b5648c 100644 (file)
@@ -59,9 +59,12 @@ static Node *transformJoinUsingClause(ParseState *pstate,
                                                 List *leftVars, List *rightVars);
 static Node *transformJoinOnClause(ParseState *pstate, JoinExpr *j,
                                          List *namespace);
+static RangeTblEntry *getRTEForSpecialRelationTypes(ParseState *pstate,
+                                               RangeVar *rv);
 static RangeTblEntry *transformTableEntry(ParseState *pstate, RangeVar *r);
 static RangeTblEntry *transformCTEReference(ParseState *pstate, RangeVar *r,
                                          CommonTableExpr *cte, Index levelsup);
+static RangeTblEntry *transformENRReference(ParseState *pstate, RangeVar *r);
 static RangeTblEntry *transformRangeSubselect(ParseState *pstate,
                                                RangeSubselect *r);
 static RangeTblEntry *transformRangeFunction(ParseState *pstate,
@@ -181,6 +184,14 @@ setTargetTable(ParseState *pstate, RangeVar *relation,
        RangeTblEntry *rte;
        int                     rtindex;
 
+       /* So far special relations are immutable; so they cannot be targets. */
+       rte = getRTEForSpecialRelationTypes(pstate, relation);
+       if (rte != NULL)
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("relation \"%s\" cannot be the target of a modifying statement",
+                                               relation->relname)));
+
        /* Close old target; this could only happen for multi-action rules */
        if (pstate->p_target_relation != NULL)
                heap_close(pstate->p_target_relation, NoLock);
@@ -434,6 +445,20 @@ transformCTEReference(ParseState *pstate, RangeVar *r,
        return rte;
 }
 
+/*
+ * transformENRReference --- transform a RangeVar that references an ephemeral
+ * named relation
+ */
+static RangeTblEntry *
+transformENRReference(ParseState *pstate, RangeVar *r)
+{
+       RangeTblEntry *rte;
+
+       rte = addRangeTableEntryForENR(pstate, r, true);
+
+       return rte;
+}
+
 /*
  * transformRangeSubselect --- transform a sub-SELECT appearing in FROM
  */
@@ -1021,6 +1046,24 @@ transformRangeTableSample(ParseState *pstate, RangeTableSample *rts)
        return tablesample;
 }
 
+
+static RangeTblEntry *
+getRTEForSpecialRelationTypes(ParseState *pstate, RangeVar *rv)
+{
+
+       CommonTableExpr *cte;
+       Index           levelsup;
+       RangeTblEntry *rte = NULL;
+
+       cte = scanNameSpaceForCTE(pstate, rv->relname, &levelsup);
+       if (cte)
+               rte = transformCTEReference(pstate, rv, cte, levelsup);
+       if (!rte && scanNameSpaceForENR(pstate, rv->relname))
+               rte = transformENRReference(pstate, rv);
+
+       return rte;
+}
+
 /*
  * transformFromClauseItem -
  *       Transform a FROM-clause item, adding any required entries to the
@@ -1055,18 +1098,14 @@ transformFromClauseItem(ParseState *pstate, Node *n,
                RangeTblEntry *rte = NULL;
                int                     rtindex;
 
-               /* if it is an unqualified name, it might be a CTE reference */
+               /*
+                * if it is an unqualified name, it might be a CTE or tuplestore
+                * reference
+                */
                if (!rv->schemaname)
-               {
-                       CommonTableExpr *cte;
-                       Index           levelsup;
-
-                       cte = scanNameSpaceForCTE(pstate, rv->relname, &levelsup);
-                       if (cte)
-                               rte = transformCTEReference(pstate, rv, cte, levelsup);
-               }
+                       rte = getRTEForSpecialRelationTypes(pstate, rv);
 
-               /* if not found as a CTE, must be a table reference */
+               /* if not found above, must be a table reference */
                if (!rte)
                        rte = transformTableEntry(pstate, rv);
 
diff --git a/src/backend/parser/parse_enr.c b/src/backend/parser/parse_enr.c
new file mode 100644 (file)
index 0000000..1cfcf65
--- /dev/null
@@ -0,0 +1,29 @@
+/*-------------------------------------------------------------------------
+ *
+ * parse_enr.c
+ *       parser support routines dealing with ephemeral named relations
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *       src/backend/parser/parse_enr.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "parser/parse_enr.h"
+
+bool
+name_matches_visible_ENR(ParseState *pstate, const char *refname)
+{
+       return (get_visible_ENR_metadata(pstate->p_queryEnv, refname) != NULL);
+}
+
+EphemeralNamedRelationMetadata
+get_visible_ENR(ParseState *pstate, const char *refname)
+{
+       return get_visible_ENR_metadata(pstate->p_queryEnv, refname);
+}
index 30cc7dadca333202a7f28f60ae187993a26fc117..34006c70cd675cfd4bad53753c8bf39b07f04617 100644 (file)
@@ -62,6 +62,8 @@ make_parsestate(ParseState *parentParseState)
                pstate->p_paramref_hook = parentParseState->p_paramref_hook;
                pstate->p_coerce_param_hook = parentParseState->p_coerce_param_hook;
                pstate->p_ref_hook_state = parentParseState->p_ref_hook_state;
+               /* query environment stays in context for the whole parse analysis */
+               pstate->p_queryEnv = parentParseState->p_queryEnv;
        }
 
        return pstate;
index 2c19e0cbf583ff0d8c540506cdb11f9601c2d5ed..7db13f37f729511cd73381be257871b54ba9572b 100644 (file)
@@ -25,6 +25,7 @@
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
 #include "parser/parsetree.h"
+#include "parser/parse_enr.h"
 #include "parser/parse_relation.h"
 #include "parser/parse_type.h"
 #include "utils/builtins.h"
@@ -281,6 +282,16 @@ isFutureCTE(ParseState *pstate, const char *refname)
        return false;
 }
 
+/*
+ * Search the query's ephemeral named relation namespace for a relation
+ * matching the given unqualified refname.
+ */
+bool
+scanNameSpaceForENR(ParseState *pstate, const char *refname)
+{
+       return name_matches_visible_ENR(pstate, refname);
+}
+
 /*
  * searchRangeTableForRel
  *       See if any RangeTblEntry could possibly match the RangeVar.
@@ -302,6 +313,7 @@ searchRangeTableForRel(ParseState *pstate, RangeVar *relation)
        const char *refname = relation->relname;
        Oid                     relId = InvalidOid;
        CommonTableExpr *cte = NULL;
+       bool            isenr = false;
        Index           ctelevelsup = 0;
        Index           levelsup;
 
@@ -318,11 +330,16 @@ searchRangeTableForRel(ParseState *pstate, RangeVar *relation)
         * unlocked.
         */
        if (!relation->schemaname)
+       {
                cte = scanNameSpaceForCTE(pstate, refname, &ctelevelsup);
-       if (!cte)
+               if (!cte)
+                       isenr = scanNameSpaceForENR(pstate, refname);
+       }
+
+       if (!cte && !isenr)
                relId = RangeVarGetRelid(relation, NoLock, true);
 
-       /* Now look for RTEs matching either the relation/CTE or the alias */
+       /* Now look for RTEs matching either the relation/CTE/ENR or the alias */
        for (levelsup = 0;
                 pstate != NULL;
                 pstate = pstate->parentParseState, levelsup++)
@@ -342,6 +359,10 @@ searchRangeTableForRel(ParseState *pstate, RangeVar *relation)
                                rte->ctelevelsup + levelsup == ctelevelsup &&
                                strcmp(rte->ctename, refname) == 0)
                                return rte;
+                       if (rte->rtekind == RTE_NAMEDTUPLESTORE &&
+                               isenr &&
+                               strcmp(rte->enrname, refname) == 0)
+                               return rte;
                        if (strcmp(rte->eref->aliasname, refname) == 0)
                                return rte;
                }
@@ -1138,13 +1159,18 @@ parserOpenTable(ParseState *pstate, const RangeVar *relation, int lockmode)
                                                        relation->schemaname, relation->relname)));
                else
                {
+                       /*
+                        * An unqualified name might be a named ephemeral relation.
+                        */
+                       if (get_visible_ENR_metadata(pstate->p_queryEnv, relation->relname))
+                               rel = NULL;
                        /*
                         * An unqualified name might have been meant as a reference to
                         * some not-yet-in-scope CTE.  The bare "does not exist" message
                         * has proven remarkably unhelpful for figuring out such problems,
                         * so we take pains to offer a specific hint.
                         */
-                       if (isFutureCTE(pstate, relation->relname))
+                       else if (isFutureCTE(pstate, relation->relname))
                                ereport(ERROR,
                                                (errcode(ERRCODE_UNDEFINED_TABLE),
                                                 errmsg("relation \"%s\" does not exist",
@@ -1940,6 +1966,102 @@ addRangeTableEntryForCTE(ParseState *pstate,
        return rte;
 }
 
+/*
+ * Add an entry for an ephemeral named relation reference to the pstate's
+ * range table (p_rtable).
+ *
+ * It is expected that the RangeVar, which up until now is only known to be an
+ * ephemeral named relation, will (in conjunction with the QueryEnvironment in
+ * the ParseState), create a RangeTblEntry for a specific *kind* of ephemeral
+ * named relation, based on enrtype.
+ *
+ * This is much like addRangeTableEntry() except that it makes an RTE for an
+ * ephemeral named relation.
+ */
+RangeTblEntry *
+addRangeTableEntryForENR(ParseState *pstate,
+                                                RangeVar *rv,
+                                                bool inFromCl)
+{
+       RangeTblEntry *rte = makeNode(RangeTblEntry);
+       Alias      *alias = rv->alias;
+       char       *refname = alias ? alias->aliasname : rv->relname;
+       EphemeralNamedRelationMetadata enrmd =
+         get_visible_ENR(pstate, rv->relname);
+       TupleDesc       tupdesc;
+       int                     attno;
+
+       Assert(enrmd != NULL);
+
+       switch (enrmd->enrtype)
+       {
+               case ENR_NAMED_TUPLESTORE:
+                       rte->rtekind = RTE_NAMEDTUPLESTORE;
+                       break;
+
+               default:
+                       elog(ERROR, "unexpected enrtype of %i", enrmd->enrtype);
+                       return NULL;  /* for fussy compilers */
+       }
+
+       /*
+        * Record dependency on a relation.  This allows plans to be invalidated
+        * if they access transition tables linked to a table that is altered.
+        */
+       rte->relid = enrmd->reliddesc;
+
+       /*
+        * Build the list of effective column names using user-supplied aliases
+        * and/or actual column names.  Also build the cannibalized fields.
+        */
+       tupdesc = ENRMetadataGetTupDesc(enrmd);
+       rte->eref = makeAlias(refname, NIL);
+       buildRelationAliases(tupdesc, alias, rte->eref);
+       rte->enrname = enrmd->name;
+       rte->enrtuples = enrmd->enrtuples;
+       rte->coltypes = NIL;
+       rte->coltypmods = NIL;
+       rte->colcollations = NIL;
+       for (attno = 1; attno <= tupdesc->natts; ++attno)
+       {
+               if (tupdesc->attrs[attno - 1]->atttypid == InvalidOid &&
+                       !(tupdesc->attrs[attno - 1]->attisdropped))
+                       elog(ERROR, "atttypid was invalid for column which has not been dropped from \"%s\"",
+                                rv->relname);
+               rte->coltypes =
+                       lappend_oid(rte->coltypes,
+                                               tupdesc->attrs[attno - 1]->atttypid);
+               rte->coltypmods =
+                       lappend_int(rte->coltypmods,
+                                               tupdesc->attrs[attno - 1]->atttypmod);
+               rte->colcollations =
+                       lappend_oid(rte->colcollations,
+                                               tupdesc->attrs[attno - 1]->attcollation);
+       }
+
+       /*
+        * Set flags and access permissions.
+        *
+        * ENRs are never checked for access rights.
+        */
+       rte->lateral = false;
+       rte->inh = false;                       /* never true for ENRs */
+       rte->inFromCl = inFromCl;
+
+       rte->requiredPerms = 0;
+       rte->checkAsUser = InvalidOid;
+       rte->selectedCols = NULL;
+
+       /*
+        * Add completed RTE to pstate's range table list, but not to join list
+        * nor namespace --- caller must do that if appropriate.
+        */
+       if (pstate != NULL)
+               pstate->p_rtable = lappend(pstate->p_rtable, rte);
+
+       return rte;
+}
+
 
 /*
  * Has the specified refname been selected FOR UPDATE/FOR SHARE?
@@ -2292,6 +2414,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
                case RTE_TABLEFUNC:
                case RTE_VALUES:
                case RTE_CTE:
+               case RTE_NAMEDTUPLESTORE:
                        {
                                /* Tablefunc, Values or CTE RTE */
                                ListCell   *aliasp_item = list_head(rte->eref->colnames);
@@ -2705,6 +2828,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
                case RTE_TABLEFUNC:
                case RTE_VALUES:
                case RTE_CTE:
+               case RTE_NAMEDTUPLESTORE:
                        {
                                /*
                                 * tablefunc, VALUES or CTE RTE --- get type info from lists
@@ -2762,6 +2886,19 @@ get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum)
                         */
                        result = false;
                        break;
+               case RTE_NAMEDTUPLESTORE:
+                       {
+                               Assert(rte->enrname);
+
+                               /*
+                                * We checked when we loaded ctecoltypes for the tuplestore
+                                * that InvalidOid was only used for dropped columns, so it is
+                                * safe to count on that here.
+                                */
+                               result =
+                                       (list_nth(rte->coltypes, attnum - 1) != InvalidOid);
+                       }
+                       break;
                case RTE_JOIN:
                        {
                                /*
index 3b84140a9bef6d504a369687a35b286209dbaf18..c46c3b38a49c3aeda0a166a71abedeb65fae05cb 100644 (file)
@@ -397,6 +397,7 @@ markTargetListOrigin(ParseState *pstate, TargetEntry *tle,
                case RTE_FUNCTION:
                case RTE_VALUES:
                case RTE_TABLEFUNC:
+               case RTE_NAMEDTUPLESTORE:
                        /* not a simple relation, leave it unmarked */
                        break;
                case RTE_CTE:
@@ -1505,6 +1506,7 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup)
        {
                case RTE_RELATION:
                case RTE_VALUES:
+               case RTE_NAMEDTUPLESTORE:
 
                        /*
                         * This case should not occur: a column of a table or values list
index 3055b483b1ab415c95d76fc95390cd8b8f2c6365..139c4c0f68cc6afab0e2f0e96506afde0a618dfd 100644 (file)
@@ -642,7 +642,8 @@ pg_parse_query(const char *query_string)
  */
 List *
 pg_analyze_and_rewrite(RawStmt *parsetree, const char *query_string,
-                                          Oid *paramTypes, int numParams)
+                                          Oid *paramTypes, int numParams,
+                                          QueryEnvironment *queryEnv)
 {
        Query      *query;
        List       *querytree_list;
@@ -655,7 +656,8 @@ pg_analyze_and_rewrite(RawStmt *parsetree, const char *query_string,
        if (log_parser_stats)
                ResetUsage();
 
-       query = parse_analyze(parsetree, query_string, paramTypes, numParams);
+       query = parse_analyze(parsetree, query_string, paramTypes, numParams,
+                                                 queryEnv);
 
        if (log_parser_stats)
                ShowUsage("PARSE ANALYSIS STATISTICS");
@@ -679,7 +681,8 @@ List *
 pg_analyze_and_rewrite_params(RawStmt *parsetree,
                                                          const char *query_string,
                                                          ParserSetupHook parserSetup,
-                                                         void *parserSetupArg)
+                                                         void *parserSetupArg,
+                                                         QueryEnvironment *queryEnv)
 {
        ParseState *pstate;
        Query      *query;
@@ -697,6 +700,7 @@ pg_analyze_and_rewrite_params(RawStmt *parsetree,
 
        pstate = make_parsestate(NULL);
        pstate->p_sourcetext = query_string;
+       pstate->p_queryEnv = queryEnv;
        (*parserSetup) (pstate, parserSetupArg);
 
        query = transformTopLevelStmt(pstate, parsetree);
@@ -1024,7 +1028,7 @@ exec_simple_query(const char *query_string)
                oldcontext = MemoryContextSwitchTo(MessageContext);
 
                querytree_list = pg_analyze_and_rewrite(parsetree, query_string,
-                                                                                               NULL, 0);
+                                                                                               NULL, 0, NULL);
 
                plantree_list = pg_plan_queries(querytree_list,
                                                                                CURSOR_OPT_PARALLEL_OK, NULL);
@@ -1314,7 +1318,8 @@ exec_parse_message(const char *query_string,      /* string to execute */
                 * Create the CachedPlanSource before we do parse analysis, since it
                 * needs to see the unmodified raw parse tree.
                 */
-               psrc = CreateCachedPlan(raw_parse_tree, query_string, commandTag);
+               psrc = CreateCachedPlan(raw_parse_tree, query_string, commandTag,
+                                                               NULL);
 
                /*
                 * Set up a snapshot if parse analysis will need one.
@@ -1366,7 +1371,8 @@ exec_parse_message(const char *query_string,      /* string to execute */
                /* Empty input string.  This is legal. */
                raw_parse_tree = NULL;
                commandTag = NULL;
-               psrc = CreateCachedPlan(raw_parse_tree, query_string, commandTag);
+               psrc = CreateCachedPlan(raw_parse_tree, query_string, commandTag,
+                                                               NULL);
                querytree_list = NIL;
        }
 
@@ -1769,7 +1775,7 @@ exec_bind_message(StringInfo input_message)
         * will be generated in MessageContext.  The plan refcount will be
         * assigned to the Portal, so it will be released at portal destruction.
         */
-       cplan = GetCachedPlan(psrc, params, false);
+       cplan = GetCachedPlan(psrc, params, false, NULL);
 
        /*
         * Now we can define the portal.
@@ -2367,7 +2373,7 @@ exec_describe_statement_message(const char *stmt_name)
                List       *tlist;
 
                /* Get the plan's primary targetlist */
-               tlist = CachedPlanGetTargetList(psrc);
+               tlist = CachedPlanGetTargetList(psrc, NULL);
 
                SendRowDescriptionMessage(psrc->resultDesc, tlist, NULL);
        }
index f538b7787c380b9670b0b9743dbca2f485633134..988c9ff43cd538d179c9add661c32fd14468d702 100644 (file)
@@ -38,6 +38,7 @@ Portal                ActivePortal = NULL;
 static void ProcessQuery(PlannedStmt *plan,
                         const char *sourceText,
                         ParamListInfo params,
+                        QueryEnvironment *queryEnv,
                         DestReceiver *dest,
                         char *completionTag);
 static void FillPortalStore(Portal portal, bool isTopLevel);
@@ -69,6 +70,7 @@ CreateQueryDesc(PlannedStmt *plannedstmt,
                                Snapshot crosscheck_snapshot,
                                DestReceiver *dest,
                                ParamListInfo params,
+                               QueryEnvironment *queryEnv,
                                int instrument_options)
 {
        QueryDesc  *qd = (QueryDesc *) palloc(sizeof(QueryDesc));
@@ -81,6 +83,7 @@ CreateQueryDesc(PlannedStmt *plannedstmt,
        qd->crosscheck_snapshot = RegisterSnapshot(crosscheck_snapshot);
        qd->dest = dest;                        /* output dest */
        qd->params = params;            /* parameter values passed into query */
+       qd->queryEnv = queryEnv;
        qd->instrument_options = instrument_options;            /* instrumentation
                                                                                                                 * wanted? */
 
@@ -135,6 +138,7 @@ static void
 ProcessQuery(PlannedStmt *plan,
                         const char *sourceText,
                         ParamListInfo params,
+                        QueryEnvironment *queryEnv,
                         DestReceiver *dest,
                         char *completionTag)
 {
@@ -145,7 +149,7 @@ ProcessQuery(PlannedStmt *plan,
         */
        queryDesc = CreateQueryDesc(plan, sourceText,
                                                                GetActiveSnapshot(), InvalidSnapshot,
-                                                               dest, params, 0);
+                                                               dest, params, queryEnv, 0);
 
        /*
         * Call ExecutorStart to prepare the plan for execution
@@ -498,6 +502,7 @@ PortalStart(Portal portal, ParamListInfo params,
                                                                                        InvalidSnapshot,
                                                                                        None_Receiver,
                                                                                        params,
+                                                                                       portal->queryEnv,
                                                                                        0);
 
                                /*
@@ -1175,6 +1180,7 @@ PortalRunUtility(Portal portal, PlannedStmt *pstmt,
                                   portal->sourceText,
                           isTopLevel ? PROCESS_UTILITY_TOPLEVEL : PROCESS_UTILITY_QUERY,
                                   portal->portalParams,
+                                  portal->queryEnv,
                                   dest,
                                   completionTag);
 
@@ -1281,6 +1287,7 @@ PortalRunMulti(Portal portal,
                                ProcessQuery(pstmt,
                                                         portal->sourceText,
                                                         portal->portalParams,
+                                                        portal->queryEnv,
                                                         dest, completionTag);
                        }
                        else
@@ -1289,6 +1296,7 @@ PortalRunMulti(Portal portal,
                                ProcessQuery(pstmt,
                                                         portal->sourceText,
                                                         portal->portalParams,
+                                                        portal->queryEnv,
                                                         altdest, NULL);
                        }
 
index 584f4f13ccc77c436253472bce5e8dc249ff5d23..b610c8e7ceef61dd2713bee3303b33fb23abc9af 100644 (file)
@@ -78,6 +78,7 @@ static void ProcessUtilitySlow(ParseState *pstate,
                                   const char *queryString,
                                   ProcessUtilityContext context,
                                   ParamListInfo params,
+                                  QueryEnvironment *queryEnv,
                                   DestReceiver *dest,
                                   char *completionTag);
 static void ExecDropStmt(DropStmt *stmt, bool isTopLevel);
@@ -333,6 +334,7 @@ ProcessUtility(PlannedStmt *pstmt,
                           const char *queryString,
                           ProcessUtilityContext context,
                           ParamListInfo params,
+                          QueryEnvironment *queryEnv,
                           DestReceiver *dest,
                           char *completionTag)
 {
@@ -347,11 +349,11 @@ ProcessUtility(PlannedStmt *pstmt,
         */
        if (ProcessUtility_hook)
                (*ProcessUtility_hook) (pstmt, queryString,
-                                                               context, params,
+                                                               context, params, queryEnv,
                                                                dest, completionTag);
        else
                standard_ProcessUtility(pstmt, queryString,
-                                                               context, params,
+                                                               context, params, queryEnv,
                                                                dest, completionTag);
 }
 
@@ -371,6 +373,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
                                                const char *queryString,
                                                ProcessUtilityContext context,
                                                ParamListInfo params,
+                                               QueryEnvironment *queryEnv,
                                                DestReceiver *dest,
                                                char *completionTag)
 {
@@ -672,7 +675,8 @@ standard_ProcessUtility(PlannedStmt *pstmt,
                        break;
 
                case T_ExplainStmt:
-                       ExplainQuery(pstate, (ExplainStmt *) parsetree, queryString, params, dest);
+                       ExplainQuery(pstate, (ExplainStmt *) parsetree, queryString, params,
+                                                queryEnv, dest);
                        break;
 
                case T_AlterSystemStmt:
@@ -819,7 +823,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
 
                                if (EventTriggerSupportsGrantObjectType(stmt->objtype))
                                        ProcessUtilitySlow(pstate, pstmt, queryString,
-                                                                          context, params,
+                                                                          context, params, queryEnv,
                                                                           dest, completionTag);
                                else
                                        ExecuteGrantStmt(stmt);
@@ -832,7 +836,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
 
                                if (EventTriggerSupportsObjectType(stmt->removeType))
                                        ProcessUtilitySlow(pstate, pstmt, queryString,
-                                                                          context, params,
+                                                                          context, params, queryEnv,
                                                                           dest, completionTag);
                                else
                                        ExecDropStmt(stmt, isTopLevel);
@@ -845,7 +849,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
 
                                if (EventTriggerSupportsObjectType(stmt->renameType))
                                        ProcessUtilitySlow(pstate, pstmt, queryString,
-                                                                          context, params,
+                                                                          context, params, queryEnv,
                                                                           dest, completionTag);
                                else
                                        ExecRenameStmt(stmt);
@@ -858,7 +862,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
 
                                if (EventTriggerSupportsObjectType(stmt->objectType))
                                        ProcessUtilitySlow(pstate, pstmt, queryString,
-                                                                          context, params,
+                                                                          context, params, queryEnv,
                                                                           dest, completionTag);
                                else
                                        ExecAlterObjectDependsStmt(stmt, NULL);
@@ -871,7 +875,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
 
                                if (EventTriggerSupportsObjectType(stmt->objectType))
                                        ProcessUtilitySlow(pstate, pstmt, queryString,
-                                                                          context, params,
+                                                                          context, params, queryEnv,
                                                                           dest, completionTag);
                                else
                                        ExecAlterObjectSchemaStmt(stmt, NULL);
@@ -884,7 +888,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
 
                                if (EventTriggerSupportsObjectType(stmt->objectType))
                                        ProcessUtilitySlow(pstate, pstmt, queryString,
-                                                                          context, params,
+                                                                          context, params, queryEnv,
                                                                           dest, completionTag);
                                else
                                        ExecAlterOwnerStmt(stmt);
@@ -897,7 +901,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
 
                                if (EventTriggerSupportsObjectType(stmt->objtype))
                                        ProcessUtilitySlow(pstate, pstmt, queryString,
-                                                                          context, params,
+                                                                          context, params, queryEnv,
                                                                           dest, completionTag);
                                else
                                        CommentObject(stmt);
@@ -910,7 +914,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
 
                                if (EventTriggerSupportsObjectType(stmt->objtype))
                                        ProcessUtilitySlow(pstate, pstmt, queryString,
-                                                                          context, params,
+                                                                          context, params, queryEnv,
                                                                           dest, completionTag);
                                else
                                        ExecSecLabelStmt(stmt);
@@ -920,7 +924,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
                default:
                        /* All other statement types have event trigger support */
                        ProcessUtilitySlow(pstate, pstmt, queryString,
-                                                          context, params,
+                                                          context, params, queryEnv,
                                                           dest, completionTag);
                        break;
        }
@@ -939,6 +943,7 @@ ProcessUtilitySlow(ParseState *pstate,
                                   const char *queryString,
                                   ProcessUtilityContext context,
                                   ParamListInfo params,
+                                  QueryEnvironment *queryEnv,
                                   DestReceiver *dest,
                                   char *completionTag)
 {
@@ -1062,6 +1067,7 @@ ProcessUtilitySlow(ParseState *pstate,
                                                                                   queryString,
                                                                                   PROCESS_UTILITY_SUBCOMMAND,
                                                                                   params,
+                                                                                  NULL,
                                                                                   None_Receiver,
                                                                                   NULL);
                                                }
@@ -1140,6 +1146,7 @@ ProcessUtilitySlow(ParseState *pstate,
                                                                                           queryString,
                                                                                           PROCESS_UTILITY_SUBCOMMAND,
                                                                                           params,
+                                                                                          NULL,
                                                                                           None_Receiver,
                                                                                           NULL);
                                                                EventTriggerAlterTableStart(parsetree);
@@ -1438,7 +1445,8 @@ ProcessUtilitySlow(ParseState *pstate,
 
                        case T_CreateTableAsStmt:
                                address = ExecCreateTableAs((CreateTableAsStmt *) parsetree,
-                                                                                queryString, params, completionTag);
+                                                                                       queryString, params, queryEnv,
+                                                                                       completionTag);
                                break;
 
                        case T_RefreshMatViewStmt:
index c2681ced2afb01d98c3aaed4292a3a08df466fa2..0c1a201ecb598d50c33dac2e279abef3ad80f462 100644 (file)
@@ -6710,6 +6710,7 @@ get_name_for_var_field(Var *var, int fieldno,
        {
                case RTE_RELATION:
                case RTE_VALUES:
+               case RTE_NAMEDTUPLESTORE:
 
                        /*
                         * This case should not occur: a column of a table or values list
index 043085d3a76ccd8f79a355d7531bbd153731ac2a..a116d5ed63ee12d2463ba513afe3f4166cb25021 100644 (file)
 static CachedPlanSource *first_saved_plan = NULL;
 
 static void ReleaseGenericPlan(CachedPlanSource *plansource);
-static List *RevalidateCachedQuery(CachedPlanSource *plansource);
+static List *RevalidateCachedQuery(CachedPlanSource *plansource,
+                                                                  QueryEnvironment *queryEnv);
 static bool CheckCachedPlan(CachedPlanSource *plansource);
 static CachedPlan *BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
-                               ParamListInfo boundParams);
+                               ParamListInfo boundParams, QueryEnvironment *queryEnv);
 static bool choose_custom_plan(CachedPlanSource *plansource,
                                   ParamListInfo boundParams);
 static double cached_plan_cost(CachedPlan *plan, bool include_planner);
@@ -150,7 +151,8 @@ InitPlanCache(void)
 CachedPlanSource *
 CreateCachedPlan(RawStmt *raw_parse_tree,
                                 const char *query_string,
-                                const char *commandTag)
+                                const char *commandTag,
+                                QueryEnvironment *queryEnv)
 {
        CachedPlanSource *plansource;
        MemoryContext source_context;
@@ -553,7 +555,8 @@ ReleaseGenericPlan(CachedPlanSource *plansource)
  * a tree copying step in a subsequent BuildCachedPlan call.)
  */
 static List *
-RevalidateCachedQuery(CachedPlanSource *plansource)
+RevalidateCachedQuery(CachedPlanSource *plansource,
+                                         QueryEnvironment *queryEnv)
 {
        bool            snapshot_set;
        RawStmt    *rawtree;
@@ -685,12 +688,14 @@ RevalidateCachedQuery(CachedPlanSource *plansource)
                tlist = pg_analyze_and_rewrite_params(rawtree,
                                                                                          plansource->query_string,
                                                                                          plansource->parserSetup,
-                                                                                         plansource->parserSetupArg);
+                                                                                         plansource->parserSetupArg,
+                                                                                         queryEnv);
        else
                tlist = pg_analyze_and_rewrite(rawtree,
                                                                           plansource->query_string,
                                                                           plansource->param_types,
-                                                                          plansource->num_params);
+                                                                          plansource->num_params,
+                                                                          queryEnv);
 
        /* Release snapshot if we got one */
        if (snapshot_set)
@@ -875,7 +880,7 @@ CheckCachedPlan(CachedPlanSource *plansource)
  */
 static CachedPlan *
 BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
-                               ParamListInfo boundParams)
+                               ParamListInfo boundParams, QueryEnvironment *queryEnv)
 {
        CachedPlan *plan;
        List       *plist;
@@ -899,7 +904,7 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
         * safety, let's treat it as real and redo the RevalidateCachedQuery call.
         */
        if (!plansource->is_valid)
-               qlist = RevalidateCachedQuery(plansource);
+               qlist = RevalidateCachedQuery(plansource, queryEnv);
 
        /*
         * If we don't already have a copy of the querytree list that can be
@@ -1129,7 +1134,7 @@ cached_plan_cost(CachedPlan *plan, bool include_planner)
  */
 CachedPlan *
 GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams,
-                         bool useResOwner)
+                         bool useResOwner, QueryEnvironment *queryEnv)
 {
        CachedPlan *plan = NULL;
        List       *qlist;
@@ -1143,7 +1148,7 @@ GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams,
                elog(ERROR, "cannot apply ResourceOwner to non-saved cached plan");
 
        /* Make sure the querytree list is valid and we have parse-time locks */
-       qlist = RevalidateCachedQuery(plansource);
+       qlist = RevalidateCachedQuery(plansource, queryEnv);
 
        /* Decide whether to use a custom plan */
        customplan = choose_custom_plan(plansource, boundParams);
@@ -1159,7 +1164,7 @@ GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams,
                else
                {
                        /* Build a new generic plan */
-                       plan = BuildCachedPlan(plansource, qlist, NULL);
+                       plan = BuildCachedPlan(plansource, qlist, NULL, queryEnv);
                        /* Just make real sure plansource->gplan is clear */
                        ReleaseGenericPlan(plansource);
                        /* Link the new generic plan into the plansource */
@@ -1204,7 +1209,7 @@ GetCachedPlan(CachedPlanSource *plansource, ParamListInfo boundParams,
        if (customplan)
        {
                /* Build a custom plan */
-               plan = BuildCachedPlan(plansource, qlist, boundParams);
+               plan = BuildCachedPlan(plansource, qlist, boundParams, queryEnv);
                /* Accumulate total costs of custom plans, but 'ware overflow */
                if (plansource->num_custom_plans < INT_MAX)
                {
@@ -1418,7 +1423,8 @@ CachedPlanIsValid(CachedPlanSource *plansource)
  * within the cached plan, and may disappear next time the plan is updated.
  */
 List *
-CachedPlanGetTargetList(CachedPlanSource *plansource)
+CachedPlanGetTargetList(CachedPlanSource *plansource,
+                                               QueryEnvironment *queryEnv)
 {
        Query      *pstmt;
 
@@ -1434,7 +1440,7 @@ CachedPlanGetTargetList(CachedPlanSource *plansource)
                return NIL;
 
        /* Make sure the querytree list is valid and we have parse-time locks */
-       RevalidateCachedQuery(plansource);
+       RevalidateCachedQuery(plansource, queryEnv);
 
        /* Get the primary statement and find out what it returns */
        pstmt = QueryListGetPrimaryStmt(plansource->query_list);
index 45cdf76ec2c032bfdc953aec9e8e5f50d4c90280..a53fcdf1889c6ae146e47aa85c5bb8e5a429d949 100644 (file)
@@ -15,8 +15,8 @@ include $(top_builddir)/src/Makefile.global
 override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
 
 OBJS = backend_random.o guc.o help_config.o pg_config.o pg_controldata.o \
-       pg_rusage.o ps_status.o rls.o sampling.o superuser.o timeout.o \
-       tzparser.o
+       pg_rusage.o ps_status.o queryenvironment.o rls.o sampling.o \
+       superuser.o timeout.o tzparser.o
 
 # This location might depend on the installation directories. Therefore
 # we can't substitute it into pg_config.h.
diff --git a/src/backend/utils/misc/queryenvironment.c b/src/backend/utils/misc/queryenvironment.c
new file mode 100644 (file)
index 0000000..a0b10d4
--- /dev/null
@@ -0,0 +1,144 @@
+/*-------------------------------------------------------------------------
+ *
+ * queryenvironment.c
+ *       Query environment, to store context-specific values like ephemeral named
+ *       relations.  Initial use is for named tuplestores for delta information
+ *       from "normal" relations.
+ *
+ * The initial implementation uses a list because the number of such relations
+ * in any one context is expected to be very small.  If that becomes a
+ * performance problem, the implementation can be changed with no other impact
+ * on callers, since this is an opaque structure.  This is the reason to
+ * require a create function.
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *       src/backend/backend/utils/misc/queryenvironment.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "utils/queryenvironment.h"
+#include "utils/rel.h"
+
+/*
+ * Private state of a query environment.
+ */
+struct QueryEnvironment
+{
+       List       *namedRelList;
+};
+
+
+QueryEnvironment *
+create_queryEnv()
+{
+       return (QueryEnvironment *) palloc0(sizeof(QueryEnvironment));
+}
+
+EphemeralNamedRelationMetadata
+get_visible_ENR_metadata(QueryEnvironment *queryEnv, const char *refname)
+{
+       EphemeralNamedRelation enr;
+
+       Assert(refname != NULL);
+
+       if (queryEnv == NULL)
+               return NULL;
+
+       enr = get_ENR(queryEnv, refname);
+
+       if (enr)
+               return &(enr->md);
+
+       return NULL;
+}
+
+/*
+ * Register a named relation for use in the given environment.
+ *
+ * If this is intended exclusively for planning purposes, the tstate field can
+ * be left NULL;
+ */
+void
+register_ENR(QueryEnvironment *queryEnv, EphemeralNamedRelation enr)
+{
+       Assert(enr != NULL);
+       Assert(get_ENR(queryEnv, enr->md.name) == NULL);
+
+       queryEnv->namedRelList = lappend(queryEnv->namedRelList, enr);
+}
+
+/*
+ * Unregister an ephemeral relation by name.  This will probably be a rarely
+ * used function, but seems like it should be provided "just in case".
+ */
+void
+unregister_ENR(QueryEnvironment *queryEnv, const char *name)
+{
+       EphemeralNamedRelation match;
+
+       match = get_ENR(queryEnv, name);
+       if (match)
+               queryEnv->namedRelList = list_delete(queryEnv->namedRelList, match);
+}
+
+/*
+ * This returns an ENR if there is a name match in the given collection.  It
+ * must quietly return NULL if no match is found.
+ */
+EphemeralNamedRelation
+get_ENR(QueryEnvironment *queryEnv, const char *name)
+{
+       ListCell   *lc;
+
+       Assert(name != NULL);
+
+       if (queryEnv == NULL)
+               return NULL;
+
+       foreach(lc, queryEnv->namedRelList)
+       {
+               EphemeralNamedRelation enr = (EphemeralNamedRelation) lfirst(lc);
+
+               if (strcmp(enr->md.name, name) == 0)
+                       return enr;
+       }
+
+       return NULL;
+}
+
+/*
+ * Gets the TupleDesc for a Ephemeral Named Relation, based on which field was
+ * filled.
+ *
+ * When the TupleDesc is based on a relation from the catalogs, we count on
+ * that relation being used at the same time, so that appropriate locks will
+ * already be held.  Locking here would be too late anyway.
+ */
+TupleDesc
+ENRMetadataGetTupDesc(EphemeralNamedRelationMetadata enrmd)
+{
+       TupleDesc       tupdesc;
+
+       /* One, and only one, of these fields must be filled. */
+       Assert((enrmd->reliddesc == InvalidOid) != (enrmd->tupdesc == NULL));
+
+       if (enrmd->tupdesc != NULL)
+               tupdesc = enrmd->tupdesc;
+       else
+       {
+               Relation        relation;
+
+               relation = heap_open(enrmd->reliddesc, NoLock);
+               tupdesc = relation->rd_att;
+               heap_close(relation, NoLock);
+       }
+
+       return tupdesc;
+}
index 84abf5f67e06e2423ea38be03c6c5089d8d5495e..b3f6be74573807308bf6022c762238266c66e454 100644 (file)
@@ -109,6 +109,7 @@ struct Tuplestorestate
        bool            truncated;              /* tuplestore_trim has removed tuples? */
        int64           availMem;               /* remaining memory available, in bytes */
        int64           allowedMem;             /* total memory allowed, in bytes */
+       int64           tuples;                 /* number of tuples added */
        BufFile    *myfile;                     /* underlying file, or NULL if none */
        MemoryContext context;          /* memory context for holding tuples */
        ResourceOwner resowner;         /* resowner for holding temp files */
@@ -267,6 +268,7 @@ tuplestore_begin_common(int eflags, bool interXact, int maxKBytes)
 
        state->memtupdeleted = 0;
        state->memtupcount = 0;
+       state->tuples = 0;
 
        /*
         * Initial size of array must be more than ALLOCSET_SEPARATE_THRESHOLD;
@@ -433,6 +435,7 @@ tuplestore_clear(Tuplestorestate *state)
        state->truncated = false;
        state->memtupdeleted = 0;
        state->memtupcount = 0;
+       state->tuples = 0;
        readptr = state->readptrs;
        for (i = 0; i < state->readptrcount; readptr++, i++)
        {
@@ -533,6 +536,18 @@ tuplestore_select_read_pointer(Tuplestorestate *state, int ptr)
        state->activeptr = ptr;
 }
 
+/*
+ * tuplestore_tuple_count
+ *
+ * Returns the number of tuples added since creation or the last
+ * tuplestore_clear().
+ */
+int64
+tuplestore_tuple_count(Tuplestorestate *state)
+{
+       return state->tuples;
+}
+
 /*
  * tuplestore_ateof
  *
@@ -753,6 +768,8 @@ tuplestore_puttuple_common(Tuplestorestate *state, void *tuple)
        int                     i;
        ResourceOwner oldowner;
 
+       state->tuples++;
+
        switch (state->status)
        {
                case TSS_INMEM:
index d067b757b0f9c09649fe741f2dc7684ff41ed14d..48c5a570a0835dc6b4441d93ddc1fea296360f2f 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     201703311
+#define CATALOG_VERSION_NO     201703312
 
 #endif
index 7b78cc4a13a72a9ec42aea5443fba11ceeb9cdd0..0fb9990e0437c6ae053c497d80d3bdbaa77fbc25 100644 (file)
 #include "nodes/params.h"
 #include "nodes/parsenodes.h"
 #include "tcop/dest.h"
+#include "utils/queryenvironment.h"
 
 
 extern ObjectAddress ExecCreateTableAs(CreateTableAsStmt *stmt, const char *queryString,
-                                 ParamListInfo params, char *completionTag);
+                                 ParamListInfo params, QueryEnvironment *queryEnv, char *completionTag);
 
 extern int     GetIntoRelEFlags(IntoClause *intoClause);
 
index 9191e186c15456bdc200d14f2beee298811d6b1a..b77f81db97eafbf09abbde36205f96927f42b703 100644 (file)
@@ -62,19 +62,20 @@ extern PGDLLIMPORT explain_get_index_name_hook_type explain_get_index_name_hook;
 
 
 extern void ExplainQuery(ParseState *pstate, ExplainStmt *stmt, const char *queryString,
-                        ParamListInfo params, DestReceiver *dest);
+                        ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest);
 
 extern ExplainState *NewExplainState(void);
 
 extern TupleDesc ExplainResultDesc(ExplainStmt *stmt);
 
 extern void ExplainOneUtility(Node *utilityStmt, IntoClause *into,
-                                 ExplainState *es,
-                                 const char *queryString, ParamListInfo params);
+                                 ExplainState *es, const char *queryString,
+                                 ParamListInfo params, QueryEnvironment *queryEnv);
 
 extern void ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into,
                           ExplainState *es, const char *queryString,
-                          ParamListInfo params, const instr_time *planduration);
+                          ParamListInfo params, QueryEnvironment *queryEnv,
+                          const instr_time *planduration);
 
 extern void ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc);
 extern void ExplainPrintTriggers(ExplainState *es, QueryDesc *queryDesc);
index d8d22edbbcd82545116be1a19722913780fc555d..c60e6f30b88ddfed2976b5763e7f2e8a497fcb15 100644 (file)
@@ -42,8 +42,8 @@ extern void ExecuteQuery(ExecuteStmt *stmt, IntoClause *intoClause,
                         DestReceiver *dest, char *completionTag);
 extern void DeallocateQuery(DeallocateStmt *stmt);
 extern void ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into,
-                                       ExplainState *es,
-                                       const char *queryString, ParamListInfo params);
+                                       ExplainState *es, const char *queryString,
+                                       ParamListInfo params, QueryEnvironment *queryEnv);
 
 /* Low-level access to stored prepared statements */
 extern void StorePreparedStatement(const char *stmt_name,
index 87e7ca8508234660fbd6debaade9a22a29e2cba7..37de6f2011332c80e7f30738da9cc3b14af02b14 100644 (file)
@@ -40,6 +40,7 @@ typedef struct QueryDesc
        Snapshot        crosscheck_snapshot;    /* crosscheck for RI update/delete */
        DestReceiver *dest;                     /* the destination for tuple output */
        ParamListInfo params;           /* param values being passed in */
+       QueryEnvironment *queryEnv; /* query environment passed in */
        int                     instrument_options;             /* OR of InstrumentOption flags */
 
        /* These fields are set by ExecutorStart */
@@ -61,6 +62,7 @@ extern QueryDesc *CreateQueryDesc(PlannedStmt *plannedstmt,
                                Snapshot crosscheck_snapshot,
                                DestReceiver *dest,
                                ParamListInfo params,
+                               QueryEnvironment *queryEnv,
                                int instrument_options);
 
 extern void FreeQueryDesc(QueryDesc *qdesc);
diff --git a/src/include/executor/nodeNamedtuplestorescan.h b/src/include/executor/nodeNamedtuplestorescan.h
new file mode 100644 (file)
index 0000000..9ef477e
--- /dev/null
@@ -0,0 +1,24 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeNamedtuplestorescan.h
+ *
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/executor/nodeNamedtuplestorescan.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NODENAMEDTUPLESTORESCAN_H
+#define NODENAMEDTUPLESTORESCAN_H
+
+#include "nodes/execnodes.h"
+
+extern NamedTuplestoreScanState *ExecInitNamedTuplestoreScan(NamedTuplestoreScan *node, EState *estate, int eflags);
+extern TupleTableSlot *ExecNamedTuplestoreScan(NamedTuplestoreScanState *node);
+extern void ExecEndNamedTuplestoreScan(NamedTuplestoreScanState *node);
+extern void ExecReScanNamedTuplestoreScan(NamedTuplestoreScanState *node);
+
+#endif   /* NODENAMEDTUPLESTORESCAN_H */
index a18ae63245d8e28e7d59d676779e56e1b2ea3dd8..e2e8bb955396d5654de4077705e0416ebc3a86e1 100644 (file)
@@ -43,6 +43,8 @@ typedef struct _SPI_plan *SPIPlanPtr;
 #define SPI_ERROR_NOATTRIBUTE  (-9)
 #define SPI_ERROR_NOOUTFUNC            (-10)
 #define SPI_ERROR_TYPUNKNOWN   (-11)
+#define SPI_ERROR_REL_DUPLICATE        (-12)
+#define SPI_ERROR_REL_NOT_FOUND        (-13)
 
 #define SPI_OK_CONNECT                 1
 #define SPI_OK_FINISH                  2
@@ -58,6 +60,8 @@ typedef struct _SPI_plan *SPIPlanPtr;
 #define SPI_OK_DELETE_RETURNING 12
 #define SPI_OK_UPDATE_RETURNING 13
 #define SPI_OK_REWRITTEN               14
+#define SPI_OK_REL_REGISTER            15
+#define SPI_OK_REL_UNREGISTER  16
 
 /* These used to be functions, now just no-ops for backwards compatibility */
 #define SPI_push()     ((void) 0)
@@ -146,6 +150,9 @@ extern void SPI_scroll_cursor_fetch(Portal, FetchDirection direction, long count
 extern void SPI_scroll_cursor_move(Portal, FetchDirection direction, long count);
 extern void SPI_cursor_close(Portal portal);
 
+extern int SPI_register_relation(EphemeralNamedRelation enr);
+extern int SPI_unregister_relation(const char *name);
+
 extern void AtEOXact_SPI(bool isCommit);
 extern void AtEOSubXact_SPI(bool isCommit, SubTransactionId mySubid);
 
index db8b59c38744324f776ca50681246b21c39bd07d..49aa7c94e7957d969d42305512999247f1cf84c8 100644 (file)
@@ -14,6 +14,7 @@
 #define SPI_PRIV_H
 
 #include "executor/spi.h"
+#include "utils/queryenvironment.h"
 
 
 #define _SPI_PLAN_MAGIC                569278163
@@ -31,6 +32,7 @@ typedef struct
        MemoryContext execCxt;          /* executor context */
        MemoryContext savedcxt;         /* context of SPI_connect's caller */
        SubTransactionId connectSubid;          /* ID of connecting subtransaction */
+       QueryEnvironment *queryEnv; /* query environment setup for SPI level */
 } _SPI_connection;
 
 /*
index 11a68500eeaa6891775d714d5b5280118dcf2340..fa992449f47eb452261f0645c0492edcbf0e6965 100644 (file)
@@ -22,6 +22,7 @@
 #include "nodes/params.h"
 #include "nodes/plannodes.h"
 #include "utils/hsearch.h"
+#include "utils/queryenvironment.h"
 #include "utils/reltrigger.h"
 #include "utils/sortsupport.h"
 #include "utils/tuplestore.h"
@@ -431,6 +432,8 @@ typedef struct EState
        ParamListInfo es_param_list_info;       /* values of external params */
        ParamExecData *es_param_exec_vals;      /* values of internal params */
 
+       QueryEnvironment *es_queryEnv;  /* query environment */
+
        /* Other working state: */
        MemoryContext es_query_cxt; /* per-query context in which EState lives */
 
@@ -1445,6 +1448,24 @@ typedef struct CteScanState
        bool            eof_cte;                /* reached end of CTE query? */
 } CteScanState;
 
+/* ----------------
+ *      NamedTuplestoreScanState information
+ *
+ *             NamedTuplestoreScan nodes are used to scan a Tuplestore created and
+ *             named prior to execution of the query.  An example is a transition
+ *             table for an AFTER trigger.
+ *
+ * Multiple NamedTuplestoreScan nodes can read out from the same Tuplestore.
+ * ----------------
+ */
+typedef struct NamedTuplestoreScanState
+{
+       ScanState       ss;                             /* its first field is NodeTag */
+       int                     readptr;                /* index of my tuplestore read pointer */
+       TupleDesc       tupdesc;                /* format of the tuples in the tuplestore */
+       Tuplestorestate *relation;      /* the rows */
+} NamedTuplestoreScanState;
+
 /* ----------------
  *      WorkTableScanState information
  *
index 963ce45ae338432e79f5237b7adbdf33c58e4eb6..177853b3bf994a2ac31c261f1b66df3381901acf 100644 (file)
@@ -63,6 +63,7 @@ typedef enum NodeTag
        T_ValuesScan,
        T_TableFuncScan,
        T_CteScan,
+       T_NamedTuplestoreScan,
        T_WorkTableScan,
        T_ForeignScan,
        T_CustomScan,
@@ -114,6 +115,7 @@ typedef enum NodeTag
        T_TableFuncScanState,
        T_ValuesScanState,
        T_CteScanState,
+       T_NamedTuplestoreScanState,
        T_WorkTableScanState,
        T_ForeignScanState,
        T_CustomScanState,
index 3a71dd5b37d1674585e6fd63faee5b0b7e066d5e..b2afd508180a57bcdd115690f9e7ed43492ccc55 100644 (file)
@@ -906,7 +906,8 @@ typedef enum RTEKind
        RTE_FUNCTION,                           /* function in FROM */
        RTE_TABLEFUNC,                          /* TableFunc(.., column list) */
        RTE_VALUES,                                     /* VALUES (<exprlist>), (<exprlist>), ... */
-       RTE_CTE                                         /* common table expr (WITH list element) */
+       RTE_CTE,                                        /* common table expr (WITH list element) */
+       RTE_NAMEDTUPLESTORE                     /* tuplestore, e.g. for AFTER triggers */
 } RTEKind;
 
 typedef struct RangeTblEntry
@@ -993,6 +994,9 @@ typedef struct RangeTblEntry
        List       *coltypmods;         /* integer list of column typmods */
        List       *colcollations;      /* OID list of column collation OIDs */
 
+       char       *enrname;            /* name of ephemeral named relation */
+       double          enrtuples;              /* estimated or actual from caller */
+
        /*
         * Fields valid in all RTEs:
         */
index 6e531b6238651f301a16f87d0cb538d5cbb3ab3d..a2dd26f8a9c4151c6eac81698de8bbeff1b01e53 100644 (file)
@@ -527,6 +527,16 @@ typedef struct CteScan
        int                     cteParam;               /* ID of Param representing CTE output */
 } CteScan;
 
+/* ----------------
+ *             NamedTuplestoreScan node
+ * ----------------
+ */
+typedef struct NamedTuplestoreScan
+{
+       Scan            scan;
+       char       *enrname;            /* Name given to Ephemeral Named Relation */
+} NamedTuplestoreScan;
+
 /* ----------------
  *             WorkTableScan node
  * ----------------
index d9a9b12a06cc33946e5a2b51f326913c601ec8e0..6909359bcff529658048c111479dfc43a6164089 100644 (file)
@@ -98,6 +98,8 @@ extern void cost_tablefuncscan(Path *path, PlannerInfo *root,
                                   RelOptInfo *baserel, ParamPathInfo *param_info);
 extern void cost_ctescan(Path *path, PlannerInfo *root,
                         RelOptInfo *baserel, ParamPathInfo *param_info);
+extern void cost_namedtuplestorescan(Path *path, PlannerInfo *root,
+                        RelOptInfo *baserel, ParamPathInfo *param_info);
 extern void cost_recursive_union(Path *runion, Path *nrterm, Path *rterm);
 extern void cost_sort(Path *path, PlannerInfo *root,
                  List *pathkeys, Cost input_cost, double tuples, int width,
@@ -187,6 +189,7 @@ extern void set_values_size_estimates(PlannerInfo *root, RelOptInfo *rel);
 extern void set_cte_size_estimates(PlannerInfo *root, RelOptInfo *rel,
                                           double cte_rows);
 extern void set_tablefunc_size_estimates(PlannerInfo *root, RelOptInfo *rel);
+extern void set_namedtuplestore_size_estimates(PlannerInfo *root, RelOptInfo *rel);
 extern void set_foreign_size_estimates(PlannerInfo *root, RelOptInfo *rel);
 extern PathTarget *set_pathtarget_cost_width(PlannerInfo *root, PathTarget *target);
 extern double compute_bitmap_pages(PlannerInfo *root, RelOptInfo *baserel,
index c72c7e02cbbc35ffc6b9d358caf365eb81c2dc48..82d4e8701c893c1568474360946389ce74c593ee 100644 (file)
@@ -100,6 +100,8 @@ extern Path *create_tablefuncscan_path(PlannerInfo *root, RelOptInfo *rel,
                                                  Relids required_outer);
 extern Path *create_ctescan_path(PlannerInfo *root, RelOptInfo *rel,
                                        Relids required_outer);
+extern Path *create_namedtuplestorescan_path(PlannerInfo *root, RelOptInfo *rel,
+                                       Relids required_outer);
 extern Path *create_worktablescan_path(PlannerInfo *root, RelOptInfo *rel,
                                                  Relids required_outer);
 extern ForeignPath *create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel,
index 17259409a72c2ff0644401e918d7941a2bd011d5..9b33ba5dfd175614d956ec7c300c951138bfc4d8 100644 (file)
@@ -23,7 +23,7 @@ extern PGDLLIMPORT post_parse_analyze_hook_type post_parse_analyze_hook;
 
 
 extern Query *parse_analyze(RawStmt *parseTree, const char *sourceText,
-                         Oid *paramTypes, int numParams);
+                         Oid *paramTypes, int numParams, QueryEnvironment *queryEnv);
 extern Query *parse_analyze_varparams(RawStmt *parseTree, const char *sourceText,
                                                Oid **paramTypes, int *numParams);
 
diff --git a/src/include/parser/parse_enr.h b/src/include/parser/parse_enr.h
new file mode 100644 (file)
index 0000000..48a7576
--- /dev/null
@@ -0,0 +1,22 @@
+/*-------------------------------------------------------------------------
+ *
+ * parse_enr.h
+ *             Internal definitions for parser
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/parser/parse_enr.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PARSE_ENR_H
+#define PARSE_ENR_H
+
+#include "parser/parse_node.h"
+
+extern bool name_matches_visible_ENR(ParseState *pstate, const char *refname);
+extern EphemeralNamedRelationMetadata get_visible_ENR(ParseState *pstate, const char *refname);
+
+#endif   /* PARSE_ENR_H */
index 3a25d9598d612a3486aaaa408f123828d174a225..1035bad322ce25ef8e5606d9111931f8291ba425 100644 (file)
@@ -15,6 +15,7 @@
 #define PARSE_NODE_H
 
 #include "nodes/parsenodes.h"
+#include "utils/queryenvironment.h"
 #include "utils/relcache.h"
 
 
@@ -188,6 +189,8 @@ struct ParseState
        bool            p_resolve_unknowns;             /* resolve unknown-type SELECT outputs
                                                                                 * as type text */
 
+       QueryEnvironment *p_queryEnv; /* curr env, incl refs to enclosing env */
+
        /* Flags telling about things found in the query: */
        bool            p_hasAggs;
        bool            p_hasWindowFuncs;
index 515c06cfef694400459b5f7b07e01089d32166ec..2f42cc8ef071b66b3dda8109d4b07cf13661d7b0 100644 (file)
@@ -42,6 +42,7 @@ extern RangeTblEntry *refnameRangeTblEntry(ParseState *pstate,
 extern CommonTableExpr *scanNameSpaceForCTE(ParseState *pstate,
                                        const char *refname,
                                        Index *ctelevelsup);
+extern bool scanNameSpaceForENR(ParseState *pstate, const char *refname);
 extern void checkNameSpaceConflicts(ParseState *pstate, List *namespace1,
                                                List *namespace2);
 extern int RTERangeTablePosn(ParseState *pstate,
@@ -107,6 +108,9 @@ extern RangeTblEntry *addRangeTableEntryForCTE(ParseState *pstate,
                                                 Index levelsup,
                                                 RangeVar *rv,
                                                 bool inFromCl);
+extern RangeTblEntry *addRangeTableEntryForENR(ParseState *pstate,
+                                                RangeVar *rv,
+                                                bool inFromCl);
 extern bool isLockedRefname(ParseState *pstate, const char *refname);
 extern void addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte,
                          bool addToJoinList,
index 1958be85b72019e985c2de5b93fca32a907a744b..f1a34a1c7248016f68d05c44d7db3d4ed86e7816 100644 (file)
@@ -24,6 +24,7 @@
 #include "nodes/plannodes.h"
 #include "storage/procsignal.h"
 #include "utils/guc.h"
+#include "utils/queryenvironment.h"
 
 
 /* Required daylight between max_stack_depth and the kernel limit, in bytes */
@@ -49,11 +50,13 @@ extern int  log_statement;
 extern List *pg_parse_query(const char *query_string);
 extern List *pg_analyze_and_rewrite(RawStmt *parsetree,
                                           const char *query_string,
-                                          Oid *paramTypes, int numParams);
+                                          Oid *paramTypes, int numParams,
+                                          QueryEnvironment *queryEnv);
 extern List *pg_analyze_and_rewrite_params(RawStmt *parsetree,
                                                          const char *query_string,
                                                          ParserSetupHook parserSetup,
-                                                         void *parserSetupArg);
+                                                         void *parserSetupArg,
+                                                         QueryEnvironment *queryEnv);
 extern PlannedStmt *pg_plan_query(Query *querytree, int cursorOptions,
                          ParamListInfo boundParams);
 extern List *pg_plan_queries(List *querytrees, int cursorOptions,
index 4f8d35390010b3c932288ccae480266bd61e4eb2..90f1215aec80baeadb2fe1eefc97c453b71fcb71 100644 (file)
@@ -26,15 +26,18 @@ typedef enum
 /* Hook for plugins to get control in ProcessUtility() */
 typedef void (*ProcessUtility_hook_type) (PlannedStmt *pstmt,
                                          const char *queryString, ProcessUtilityContext context,
-                                                                                                         ParamListInfo params,
+                                                                       ParamListInfo params,
+                                                                       QueryEnvironment *queryEnv,
                                                                        DestReceiver *dest, char *completionTag);
 extern PGDLLIMPORT ProcessUtility_hook_type ProcessUtility_hook;
 
 extern void ProcessUtility(PlannedStmt *pstmt, const char *queryString,
                           ProcessUtilityContext context, ParamListInfo params,
+                          QueryEnvironment *queryEnv,
                           DestReceiver *dest, char *completionTag);
 extern void standard_ProcessUtility(PlannedStmt *pstmt, const char *queryString,
                                                ProcessUtilityContext context, ParamListInfo params,
+                                               QueryEnvironment *queryEnv,
                                                DestReceiver *dest, char *completionTag);
 
 extern bool UtilityReturnsTuples(Node *parsetree);
index 84952d56e70d8bb182f0e88b668acbd48049f40b..48d4ac94b2ac7bb56b072abab4686e0ac6a100ef 100644 (file)
@@ -17,6 +17,7 @@
 
 #include "access/tupdesc.h"
 #include "nodes/params.h"
+#include "utils/queryenvironment.h"
 
 /* Forward declaration, to avoid including parsenodes.h here */
 struct RawStmt;
@@ -148,7 +149,8 @@ extern void ResetPlanCache(void);
 
 extern CachedPlanSource *CreateCachedPlan(struct RawStmt *raw_parse_tree,
                                 const char *query_string,
-                                const char *commandTag);
+                                const char *commandTag,
+                                QueryEnvironment *queryEnv);
 extern CachedPlanSource *CreateOneShotCachedPlan(struct RawStmt *raw_parse_tree,
                                                const char *query_string,
                                                const char *commandTag);
@@ -172,11 +174,13 @@ extern CachedPlanSource *CopyCachedPlan(CachedPlanSource *plansource);
 
 extern bool CachedPlanIsValid(CachedPlanSource *plansource);
 
-extern List *CachedPlanGetTargetList(CachedPlanSource *plansource);
+extern List *CachedPlanGetTargetList(CachedPlanSource *plansource,
+                                                       QueryEnvironment *queryEnv);
 
 extern CachedPlan *GetCachedPlan(CachedPlanSource *plansource,
                          ParamListInfo boundParams,
-                         bool useResOwner);
+                         bool useResOwner,
+                         QueryEnvironment *queryEnv);
 extern void ReleaseCachedPlan(CachedPlan *plan, bool useResOwner);
 
 #endif   /* PLANCACHE_H */
index e7c5a8bd091c051cc8054cc2db32cdd83587aaae..ef3898c98cb55c2c0e8aacbf845d9a77194bf82f 100644 (file)
@@ -137,6 +137,7 @@ typedef struct PortalData
        CachedPlan *cplan;                      /* CachedPlan, if stmts are from one */
 
        ParamListInfo portalParams; /* params to pass to query */
+       QueryEnvironment *queryEnv; /* environment for query */
 
        /* Features/options */
        PortalStrategy strategy;        /* see above */
diff --git a/src/include/utils/queryenvironment.h b/src/include/utils/queryenvironment.h
new file mode 100644 (file)
index 0000000..b4f65a1
--- /dev/null
@@ -0,0 +1,74 @@
+/*-------------------------------------------------------------------------
+ *
+ * queryenvironment.h
+ *       Access to functions to mutate the query environment and retrieve the
+ *       actual data related to entries (if any).
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/queryenvironment.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef QUERYENVIRONMENT_H
+#define QUERYENVIRONMENT_H
+
+#include "access/tupdesc.h"
+
+
+typedef enum EphemeralNameRelationType
+{
+       ENR_NAMED_TUPLESTORE            /* named tuplestore relation; e.g., deltas */
+} EphemeralNameRelationType;
+
+/*
+ * Some ephemeral named relations must match some relation (e.g., trigger
+ * transition tables), so to properly handle cached plans and DDL, we should
+ * carry the OID of that relation.  In other cases an ENR might be independent
+ * of any relation which is stored in the system catalogs, so we need to be
+ * able to directly store the TupleDesc.  We never need both.
+ */
+typedef struct EphemeralNamedRelationMetadataData
+{
+       char                       *name;               /* name used to identify the relation */
+
+       /* only one of the next two fields should be used */
+       Oid                                     reliddesc;      /* oid of relation to get tupdesc */
+       TupleDesc                       tupdesc;        /* description of result rows */
+
+       EphemeralNameRelationType enrtype;      /* to identify type of relation */
+       double                          enrtuples;      /* estimated number of tuples */
+} EphemeralNamedRelationMetadataData;
+
+typedef EphemeralNamedRelationMetadataData *EphemeralNamedRelationMetadata;
+
+/*
+ * Ephemeral Named Relation data; used for parsing named relations not in the
+ * catalog, like transition tables in AFTER triggers.
+ */
+typedef struct EphemeralNamedRelationData
+{
+       EphemeralNamedRelationMetadataData      md;
+       void       *reldata;            /* structure for execution-time access to data */
+} EphemeralNamedRelationData;
+
+typedef EphemeralNamedRelationData *EphemeralNamedRelation;
+
+/*
+ * This is an opaque structure outside of queryenvironment.c itself.  The
+ * intention is to be able to change the implementation or add new context
+ * features without needing to change existing code for use of existing
+ * features.
+ */
+typedef struct QueryEnvironment QueryEnvironment;
+
+
+extern QueryEnvironment *create_queryEnv(void);
+extern EphemeralNamedRelationMetadata get_visible_ENR_metadata(QueryEnvironment *queryEnv, const char *refname);
+extern void register_ENR(QueryEnvironment *queryEnv, EphemeralNamedRelation enr);
+extern void unregister_ENR(QueryEnvironment *queryEnv, const char *name);
+extern EphemeralNamedRelation get_ENR(QueryEnvironment *queryEnv, const char *name);
+extern TupleDesc ENRMetadataGetTupDesc(EphemeralNamedRelationMetadata enrmd);
+
+#endif   /* QUERYENVIRONMENT_H */
index a52a547037237503d64a041d8f4b8e4df17e3692..b31ede882b93a40408d86b4b40471d2733aab8da 100644 (file)
@@ -78,6 +78,8 @@ extern bool tuplestore_advance(Tuplestorestate *state, bool forward);
 extern bool tuplestore_skiptuples(Tuplestorestate *state,
                                          int64 ntuples, bool forward);
 
+extern int64 tuplestore_tuple_count(Tuplestorestate *state);
+
 extern bool tuplestore_ateof(Tuplestorestate *state);
 
 extern void tuplestore_rescan(Tuplestorestate *state);
index 3b7f689a98bd867426fd68469fcdaa767afaa211..fdcc4970a130496711b31eb22920cf86babdd8eb 100644 (file)
@@ -2272,3 +2272,6 @@ with ordinality as (select 1 as x) select * from ordinality;
  1
 (1 row)
 
+-- check sane response to attempt to modify CTE relation
+WITH d AS (SELECT 42) INSERT INTO d VALUES (1);
+ERROR:  relation "d" cannot be the target of a modifying statement
index 08ddc8bae01748fd3e8d21e671fb1f45cfe47023..8ae5184d0f17cc4c2e805aad0280ae90134fe41f 100644 (file)
@@ -1028,3 +1028,6 @@ DROP RULE y_rule ON y;
 create table foo (with baz);  -- fail, WITH is a reserved word
 create table foo (with ordinality);  -- fail, WITH is a reserved word
 with ordinality as (select 1 as x) select * from ordinality;
+
+-- check sane response to attempt to modify CTE relation
+WITH d AS (SELECT 42) INSERT INTO d VALUES (1);