@@ -49,8 +49,9 @@ static int _SPI_curid = -1;
4949static Portal SPI_cursor_open_internal (const char * name , SPIPlanPtr plan ,
5050 ParamListInfo paramLI , bool read_only );
5151
52- static void _SPI_prepare_plan (const char * src , SPIPlanPtr plan ,
53- ParamListInfo boundParams );
52+ static void _SPI_prepare_plan (const char * src , SPIPlanPtr plan );
53+
54+ static void _SPI_prepare_oneshot_plan (const char * src , SPIPlanPtr plan );
5455
5556static int _SPI_execute_plan (SPIPlanPtr plan , ParamListInfo paramLI ,
5657 Snapshot snapshot , Snapshot crosscheck_snapshot ,
@@ -355,7 +356,7 @@ SPI_execute(const char *src, bool read_only, long tcount)
355356 plan .magic = _SPI_PLAN_MAGIC ;
356357 plan .cursor_options = 0 ;
357358
358- _SPI_prepare_plan (src , & plan , NULL );
359+ _SPI_prepare_oneshot_plan (src , & plan );
359360
360361 res = _SPI_execute_plan (& plan , NULL ,
361362 InvalidSnapshot , InvalidSnapshot ,
@@ -506,7 +507,7 @@ SPI_execute_with_args(const char *src,
506507 paramLI = _SPI_convert_params (nargs , argtypes ,
507508 Values , Nulls );
508509
509- _SPI_prepare_plan (src , & plan , paramLI );
510+ _SPI_prepare_oneshot_plan (src , & plan );
510511
511512 res = _SPI_execute_plan (& plan , paramLI ,
512513 InvalidSnapshot , InvalidSnapshot ,
@@ -547,7 +548,7 @@ SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes,
547548 plan .parserSetup = NULL ;
548549 plan .parserSetupArg = NULL ;
549550
550- _SPI_prepare_plan (src , & plan , NULL );
551+ _SPI_prepare_plan (src , & plan );
551552
552553 /* copy plan to procedure context */
553554 result = _SPI_make_plan_non_temp (& plan );
@@ -584,7 +585,7 @@ SPI_prepare_params(const char *src,
584585 plan .parserSetup = parserSetup ;
585586 plan .parserSetupArg = parserSetupArg ;
586587
587- _SPI_prepare_plan (src , & plan , NULL );
588+ _SPI_prepare_plan (src , & plan );
588589
589590 /* copy plan to procedure context */
590591 result = _SPI_make_plan_non_temp (& plan );
@@ -599,7 +600,8 @@ SPI_keepplan(SPIPlanPtr plan)
599600{
600601 ListCell * lc ;
601602
602- if (plan == NULL || plan -> magic != _SPI_PLAN_MAGIC || plan -> saved )
603+ if (plan == NULL || plan -> magic != _SPI_PLAN_MAGIC ||
604+ plan -> saved || plan -> oneshot )
603605 return SPI_ERROR_ARGUMENT ;
604606
605607 /*
@@ -1083,7 +1085,7 @@ SPI_cursor_open_with_args(const char *name,
10831085 paramLI = _SPI_convert_params (nargs , argtypes ,
10841086 Values , Nulls );
10851087
1086- _SPI_prepare_plan (src , & plan , paramLI );
1088+ _SPI_prepare_plan (src , & plan );
10871089
10881090 /* We needn't copy the plan; SPI_cursor_open_internal will do so */
10891091
@@ -1645,10 +1647,6 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
16451647 *
16461648 * At entry, plan->argtypes and plan->nargs (or alternatively plan->parserSetup
16471649 * and plan->parserSetupArg) must be valid, as must plan->cursor_options.
1648- * If boundParams isn't NULL then it represents parameter values that are made
1649- * available to the planner (as either estimates or hard values depending on
1650- * their PARAM_FLAG_CONST marking). The boundParams had better match the
1651- * param type information embedded in the plan!
16521650 *
16531651 * Results are stored into *plan (specifically, plan->plancache_list).
16541652 * Note that the result data is all in CurrentMemoryContext or child contexts
@@ -1657,13 +1655,12 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
16571655 * parsing is also left in CurrentMemoryContext.
16581656 */
16591657static void
1660- _SPI_prepare_plan (const char * src , SPIPlanPtr plan , ParamListInfo boundParams )
1658+ _SPI_prepare_plan (const char * src , SPIPlanPtr plan )
16611659{
16621660 List * raw_parsetree_list ;
16631661 List * plancache_list ;
16641662 ListCell * list_item ;
16651663 ErrorContextCallback spierrcontext ;
1666- int cursor_options = plan -> cursor_options ;
16671664
16681665 /*
16691666 * Setup error traceback support for ereport()
@@ -1726,13 +1723,80 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan, ParamListInfo boundParams)
17261723 plan -> nargs ,
17271724 plan -> parserSetup ,
17281725 plan -> parserSetupArg ,
1729- cursor_options ,
1726+ plan -> cursor_options ,
17301727 false); /* not fixed result */
17311728
17321729 plancache_list = lappend (plancache_list , plansource );
17331730 }
17341731
17351732 plan -> plancache_list = plancache_list ;
1733+ plan -> oneshot = false;
1734+
1735+ /*
1736+ * Pop the error context stack
1737+ */
1738+ error_context_stack = spierrcontext .previous ;
1739+ }
1740+
1741+ /*
1742+ * Parse, but don't analyze, a querystring.
1743+ *
1744+ * This is a stripped-down version of _SPI_prepare_plan that only does the
1745+ * initial raw parsing. It creates "one shot" CachedPlanSources
1746+ * that still require parse analysis before execution is possible.
1747+ *
1748+ * The advantage of using the "one shot" form of CachedPlanSource is that
1749+ * we eliminate data copying and invalidation overhead. Postponing parse
1750+ * analysis also prevents issues if some of the raw parsetrees are DDL
1751+ * commands that affect validity of later parsetrees. Both of these
1752+ * attributes are good things for SPI_execute() and similar cases.
1753+ *
1754+ * Results are stored into *plan (specifically, plan->plancache_list).
1755+ * Note that the result data is all in CurrentMemoryContext or child contexts
1756+ * thereof; in practice this means it is in the SPI executor context, and
1757+ * what we are creating is a "temporary" SPIPlan. Cruft generated during
1758+ * parsing is also left in CurrentMemoryContext.
1759+ */
1760+ static void
1761+ _SPI_prepare_oneshot_plan (const char * src , SPIPlanPtr plan )
1762+ {
1763+ List * raw_parsetree_list ;
1764+ List * plancache_list ;
1765+ ListCell * list_item ;
1766+ ErrorContextCallback spierrcontext ;
1767+
1768+ /*
1769+ * Setup error traceback support for ereport()
1770+ */
1771+ spierrcontext .callback = _SPI_error_callback ;
1772+ spierrcontext .arg = (void * ) src ;
1773+ spierrcontext .previous = error_context_stack ;
1774+ error_context_stack = & spierrcontext ;
1775+
1776+ /*
1777+ * Parse the request string into a list of raw parse trees.
1778+ */
1779+ raw_parsetree_list = pg_parse_query (src );
1780+
1781+ /*
1782+ * Construct plancache entries, but don't do parse analysis yet.
1783+ */
1784+ plancache_list = NIL ;
1785+
1786+ foreach (list_item , raw_parsetree_list )
1787+ {
1788+ Node * parsetree = (Node * ) lfirst (list_item );
1789+ CachedPlanSource * plansource ;
1790+
1791+ plansource = CreateOneShotCachedPlan (parsetree ,
1792+ src ,
1793+ CreateCommandTag (parsetree ));
1794+
1795+ plancache_list = lappend (plancache_list , plansource );
1796+ }
1797+
1798+ plan -> plancache_list = plancache_list ;
1799+ plan -> oneshot = true;
17361800
17371801 /*
17381802 * Pop the error context stack
@@ -1770,7 +1834,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
17701834 * Setup error traceback support for ereport()
17711835 */
17721836 spierrcontext .callback = _SPI_error_callback ;
1773- spierrcontext .arg = NULL ;
1837+ spierrcontext .arg = NULL ; /* we'll fill this below */
17741838 spierrcontext .previous = error_context_stack ;
17751839 error_context_stack = & spierrcontext ;
17761840
@@ -1816,6 +1880,47 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
18161880
18171881 spierrcontext .arg = (void * ) plansource -> query_string ;
18181882
1883+ /*
1884+ * If this is a one-shot plan, we still need to do parse analysis.
1885+ */
1886+ if (plan -> oneshot )
1887+ {
1888+ Node * parsetree = plansource -> raw_parse_tree ;
1889+ const char * src = plansource -> query_string ;
1890+ List * stmt_list ;
1891+
1892+ /*
1893+ * Parameter datatypes are driven by parserSetup hook if provided,
1894+ * otherwise we use the fixed parameter list.
1895+ */
1896+ if (plan -> parserSetup != NULL )
1897+ {
1898+ Assert (plan -> nargs == 0 );
1899+ stmt_list = pg_analyze_and_rewrite_params (parsetree ,
1900+ src ,
1901+ plan -> parserSetup ,
1902+ plan -> parserSetupArg );
1903+ }
1904+ else
1905+ {
1906+ stmt_list = pg_analyze_and_rewrite (parsetree ,
1907+ src ,
1908+ plan -> argtypes ,
1909+ plan -> nargs );
1910+ }
1911+
1912+ /* Finish filling in the CachedPlanSource */
1913+ CompleteCachedPlan (plansource ,
1914+ stmt_list ,
1915+ NULL ,
1916+ plan -> argtypes ,
1917+ plan -> nargs ,
1918+ plan -> parserSetup ,
1919+ plan -> parserSetupArg ,
1920+ plan -> cursor_options ,
1921+ false); /* not fixed result */
1922+ }
1923+
18191924 /*
18201925 * Replan if needed, and increment plan refcount. If it's a saved
18211926 * plan, the refcount must be backed by the CurrentResourceOwner.
@@ -2313,6 +2418,8 @@ _SPI_make_plan_non_temp(SPIPlanPtr plan)
23132418 /* Assert the input is a temporary SPIPlan */
23142419 Assert (plan -> magic == _SPI_PLAN_MAGIC );
23152420 Assert (plan -> plancxt == NULL );
2421+ /* One-shot plans can't be saved */
2422+ Assert (!plan -> oneshot );
23162423
23172424 /*
23182425 * Create a memory context for the plan, underneath the procedure context.
@@ -2330,6 +2437,7 @@ _SPI_make_plan_non_temp(SPIPlanPtr plan)
23302437 newplan = (SPIPlanPtr ) palloc (sizeof (_SPI_plan ));
23312438 newplan -> magic = _SPI_PLAN_MAGIC ;
23322439 newplan -> saved = false;
2440+ newplan -> oneshot = false;
23332441 newplan -> plancache_list = NIL ;
23342442 newplan -> plancxt = plancxt ;
23352443 newplan -> cursor_options = plan -> cursor_options ;
@@ -2379,6 +2487,9 @@ _SPI_save_plan(SPIPlanPtr plan)
23792487 MemoryContext oldcxt ;
23802488 ListCell * lc ;
23812489
2490+ /* One-shot plans can't be saved */
2491+ Assert (!plan -> oneshot );
2492+
23822493 /*
23832494 * Create a memory context for the plan. We don't expect the plan to be
23842495 * very large, so use smaller-than-default alloc parameters. It's a
@@ -2395,6 +2506,7 @@ _SPI_save_plan(SPIPlanPtr plan)
23952506 newplan = (SPIPlanPtr ) palloc (sizeof (_SPI_plan ));
23962507 newplan -> magic = _SPI_PLAN_MAGIC ;
23972508 newplan -> saved = false;
2509+ newplan -> oneshot = false;
23982510 newplan -> plancache_list = NIL ;
23992511 newplan -> plancxt = plancxt ;
24002512 newplan -> cursor_options = plan -> cursor_options ;
0 commit comments