@@ -48,8 +48,9 @@ static int _SPI_curid = -1;
48
48
static Portal SPI_cursor_open_internal (const char * name , SPIPlanPtr plan ,
49
49
ParamListInfo paramLI , bool read_only );
50
50
51
- static void _SPI_prepare_plan (const char * src , SPIPlanPtr plan ,
52
- ParamListInfo boundParams );
51
+ static void _SPI_prepare_plan (const char * src , SPIPlanPtr plan );
52
+
53
+ static void _SPI_prepare_oneshot_plan (const char * src , SPIPlanPtr plan );
53
54
54
55
static int _SPI_execute_plan (SPIPlanPtr plan , ParamListInfo paramLI ,
55
56
Snapshot snapshot , Snapshot crosscheck_snapshot ,
@@ -354,7 +355,7 @@ SPI_execute(const char *src, bool read_only, long tcount)
354
355
plan .magic = _SPI_PLAN_MAGIC ;
355
356
plan .cursor_options = 0 ;
356
357
357
- _SPI_prepare_plan (src , & plan , NULL );
358
+ _SPI_prepare_oneshot_plan (src , & plan );
358
359
359
360
res = _SPI_execute_plan (& plan , NULL ,
360
361
InvalidSnapshot , InvalidSnapshot ,
@@ -505,7 +506,7 @@ SPI_execute_with_args(const char *src,
505
506
paramLI = _SPI_convert_params (nargs , argtypes ,
506
507
Values , Nulls );
507
508
508
- _SPI_prepare_plan (src , & plan , paramLI );
509
+ _SPI_prepare_oneshot_plan (src , & plan );
509
510
510
511
res = _SPI_execute_plan (& plan , paramLI ,
511
512
InvalidSnapshot , InvalidSnapshot ,
@@ -546,7 +547,7 @@ SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes,
546
547
plan .parserSetup = NULL ;
547
548
plan .parserSetupArg = NULL ;
548
549
549
- _SPI_prepare_plan (src , & plan , NULL );
550
+ _SPI_prepare_plan (src , & plan );
550
551
551
552
/* copy plan to procedure context */
552
553
result = _SPI_make_plan_non_temp (& plan );
@@ -583,7 +584,7 @@ SPI_prepare_params(const char *src,
583
584
plan .parserSetup = parserSetup ;
584
585
plan .parserSetupArg = parserSetupArg ;
585
586
586
- _SPI_prepare_plan (src , & plan , NULL );
587
+ _SPI_prepare_plan (src , & plan );
587
588
588
589
/* copy plan to procedure context */
589
590
result = _SPI_make_plan_non_temp (& plan );
@@ -598,7 +599,8 @@ SPI_keepplan(SPIPlanPtr plan)
598
599
{
599
600
ListCell * lc ;
600
601
601
- if (plan == NULL || plan -> magic != _SPI_PLAN_MAGIC || plan -> saved )
602
+ if (plan == NULL || plan -> magic != _SPI_PLAN_MAGIC ||
603
+ plan -> saved || plan -> oneshot )
602
604
return SPI_ERROR_ARGUMENT ;
603
605
604
606
/*
@@ -1082,7 +1084,7 @@ SPI_cursor_open_with_args(const char *name,
1082
1084
paramLI = _SPI_convert_params (nargs , argtypes ,
1083
1085
Values , Nulls );
1084
1086
1085
- _SPI_prepare_plan (src , & plan , paramLI );
1087
+ _SPI_prepare_plan (src , & plan );
1086
1088
1087
1089
/* We needn't copy the plan; SPI_cursor_open_internal will do so */
1088
1090
@@ -1644,10 +1646,6 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
1644
1646
*
1645
1647
* At entry, plan->argtypes and plan->nargs (or alternatively plan->parserSetup
1646
1648
* and plan->parserSetupArg) must be valid, as must plan->cursor_options.
1647
- * If boundParams isn't NULL then it represents parameter values that are made
1648
- * available to the planner (as either estimates or hard values depending on
1649
- * their PARAM_FLAG_CONST marking). The boundParams had better match the
1650
- * param type information embedded in the plan!
1651
1649
*
1652
1650
* Results are stored into *plan (specifically, plan->plancache_list).
1653
1651
* Note that the result data is all in CurrentMemoryContext or child contexts
@@ -1656,13 +1654,12 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
1656
1654
* parsing is also left in CurrentMemoryContext.
1657
1655
*/
1658
1656
static void
1659
- _SPI_prepare_plan (const char * src , SPIPlanPtr plan , ParamListInfo boundParams )
1657
+ _SPI_prepare_plan (const char * src , SPIPlanPtr plan )
1660
1658
{
1661
1659
List * raw_parsetree_list ;
1662
1660
List * plancache_list ;
1663
1661
ListCell * list_item ;
1664
1662
ErrorContextCallback spierrcontext ;
1665
- int cursor_options = plan -> cursor_options ;
1666
1663
1667
1664
/*
1668
1665
* Setup error traceback support for ereport()
@@ -1725,13 +1722,80 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan, ParamListInfo boundParams)
1725
1722
plan -> nargs ,
1726
1723
plan -> parserSetup ,
1727
1724
plan -> parserSetupArg ,
1728
- cursor_options ,
1725
+ plan -> cursor_options ,
1729
1726
false); /* not fixed result */
1730
1727
1731
1728
plancache_list = lappend (plancache_list , plansource );
1732
1729
}
1733
1730
1734
1731
plan -> plancache_list = plancache_list ;
1732
+ plan -> oneshot = false;
1733
+
1734
+ /*
1735
+ * Pop the error context stack
1736
+ */
1737
+ error_context_stack = spierrcontext .previous ;
1738
+ }
1739
+
1740
+ /*
1741
+ * Parse, but don't analyze, a querystring.
1742
+ *
1743
+ * This is a stripped-down version of _SPI_prepare_plan that only does the
1744
+ * initial raw parsing. It creates "one shot" CachedPlanSources
1745
+ * that still require parse analysis before execution is possible.
1746
+ *
1747
+ * The advantage of using the "one shot" form of CachedPlanSource is that
1748
+ * we eliminate data copying and invalidation overhead. Postponing parse
1749
+ * analysis also prevents issues if some of the raw parsetrees are DDL
1750
+ * commands that affect validity of later parsetrees. Both of these
1751
+ * attributes are good things for SPI_execute() and similar cases.
1752
+ *
1753
+ * Results are stored into *plan (specifically, plan->plancache_list).
1754
+ * Note that the result data is all in CurrentMemoryContext or child contexts
1755
+ * thereof; in practice this means it is in the SPI executor context, and
1756
+ * what we are creating is a "temporary" SPIPlan. Cruft generated during
1757
+ * parsing is also left in CurrentMemoryContext.
1758
+ */
1759
+ static void
1760
+ _SPI_prepare_oneshot_plan (const char * src , SPIPlanPtr plan )
1761
+ {
1762
+ List * raw_parsetree_list ;
1763
+ List * plancache_list ;
1764
+ ListCell * list_item ;
1765
+ ErrorContextCallback spierrcontext ;
1766
+
1767
+ /*
1768
+ * Setup error traceback support for ereport()
1769
+ */
1770
+ spierrcontext .callback = _SPI_error_callback ;
1771
+ spierrcontext .arg = (void * ) src ;
1772
+ spierrcontext .previous = error_context_stack ;
1773
+ error_context_stack = & spierrcontext ;
1774
+
1775
+ /*
1776
+ * Parse the request string into a list of raw parse trees.
1777
+ */
1778
+ raw_parsetree_list = pg_parse_query (src );
1779
+
1780
+ /*
1781
+ * Construct plancache entries, but don't do parse analysis yet.
1782
+ */
1783
+ plancache_list = NIL ;
1784
+
1785
+ foreach (list_item , raw_parsetree_list )
1786
+ {
1787
+ Node * parsetree = (Node * ) lfirst (list_item );
1788
+ CachedPlanSource * plansource ;
1789
+
1790
+ plansource = CreateOneShotCachedPlan (parsetree ,
1791
+ src ,
1792
+ CreateCommandTag (parsetree ));
1793
+
1794
+ plancache_list = lappend (plancache_list , plansource );
1795
+ }
1796
+
1797
+ plan -> plancache_list = plancache_list ;
1798
+ plan -> oneshot = true;
1735
1799
1736
1800
/*
1737
1801
* Pop the error context stack
@@ -1769,7 +1833,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
1769
1833
* Setup error traceback support for ereport()
1770
1834
*/
1771
1835
spierrcontext .callback = _SPI_error_callback ;
1772
- spierrcontext .arg = NULL ;
1836
+ spierrcontext .arg = NULL ; /* we'll fill this below */
1773
1837
spierrcontext .previous = error_context_stack ;
1774
1838
error_context_stack = & spierrcontext ;
1775
1839
@@ -1815,6 +1879,47 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
1815
1879
1816
1880
spierrcontext .arg = (void * ) plansource -> query_string ;
1817
1881
1882
+ /*
1883
+ * If this is a one-shot plan, we still need to do parse analysis.
1884
+ */
1885
+ if (plan -> oneshot )
1886
+ {
1887
+ Node * parsetree = plansource -> raw_parse_tree ;
1888
+ const char * src = plansource -> query_string ;
1889
+ List * stmt_list ;
1890
+
1891
+ /*
1892
+ * Parameter datatypes are driven by parserSetup hook if provided,
1893
+ * otherwise we use the fixed parameter list.
1894
+ */
1895
+ if (plan -> parserSetup != NULL )
1896
+ {
1897
+ Assert (plan -> nargs == 0 );
1898
+ stmt_list = pg_analyze_and_rewrite_params (parsetree ,
1899
+ src ,
1900
+ plan -> parserSetup ,
1901
+ plan -> parserSetupArg );
1902
+ }
1903
+ else
1904
+ {
1905
+ stmt_list = pg_analyze_and_rewrite (parsetree ,
1906
+ src ,
1907
+ plan -> argtypes ,
1908
+ plan -> nargs );
1909
+ }
1910
+
1911
+ /* Finish filling in the CachedPlanSource */
1912
+ CompleteCachedPlan (plansource ,
1913
+ stmt_list ,
1914
+ NULL ,
1915
+ plan -> argtypes ,
1916
+ plan -> nargs ,
1917
+ plan -> parserSetup ,
1918
+ plan -> parserSetupArg ,
1919
+ plan -> cursor_options ,
1920
+ false); /* not fixed result */
1921
+ }
1922
+
1818
1923
/*
1819
1924
* Replan if needed, and increment plan refcount. If it's a saved
1820
1925
* plan, the refcount must be backed by the CurrentResourceOwner.
@@ -2306,6 +2411,8 @@ _SPI_make_plan_non_temp(SPIPlanPtr plan)
2306
2411
/* Assert the input is a temporary SPIPlan */
2307
2412
Assert (plan -> magic == _SPI_PLAN_MAGIC );
2308
2413
Assert (plan -> plancxt == NULL );
2414
+ /* One-shot plans can't be saved */
2415
+ Assert (!plan -> oneshot );
2309
2416
2310
2417
/*
2311
2418
* Create a memory context for the plan, underneath the procedure context.
@@ -2323,6 +2430,7 @@ _SPI_make_plan_non_temp(SPIPlanPtr plan)
2323
2430
newplan = (SPIPlanPtr ) palloc (sizeof (_SPI_plan ));
2324
2431
newplan -> magic = _SPI_PLAN_MAGIC ;
2325
2432
newplan -> saved = false;
2433
+ newplan -> oneshot = false;
2326
2434
newplan -> plancache_list = NIL ;
2327
2435
newplan -> plancxt = plancxt ;
2328
2436
newplan -> cursor_options = plan -> cursor_options ;
@@ -2372,6 +2480,9 @@ _SPI_save_plan(SPIPlanPtr plan)
2372
2480
MemoryContext oldcxt ;
2373
2481
ListCell * lc ;
2374
2482
2483
+ /* One-shot plans can't be saved */
2484
+ Assert (!plan -> oneshot );
2485
+
2375
2486
/*
2376
2487
* Create a memory context for the plan. We don't expect the plan to be
2377
2488
* very large, so use smaller-than-default alloc parameters. It's a
@@ -2388,6 +2499,7 @@ _SPI_save_plan(SPIPlanPtr plan)
2388
2499
newplan = (SPIPlanPtr ) palloc (sizeof (_SPI_plan ));
2389
2500
newplan -> magic = _SPI_PLAN_MAGIC ;
2390
2501
newplan -> saved = false;
2502
+ newplan -> oneshot = false;
2391
2503
newplan -> plancache_list = NIL ;
2392
2504
newplan -> plancxt = plancxt ;
2393
2505
newplan -> cursor_options = plan -> cursor_options ;
0 commit comments