@@ -49,8 +49,9 @@ static int _SPI_curid = -1;
49
49
static Portal SPI_cursor_open_internal (const char * name , SPIPlanPtr plan ,
50
50
ParamListInfo paramLI , bool read_only );
51
51
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 );
54
55
55
56
static int _SPI_execute_plan (SPIPlanPtr plan , ParamListInfo paramLI ,
56
57
Snapshot snapshot , Snapshot crosscheck_snapshot ,
@@ -355,7 +356,7 @@ SPI_execute(const char *src, bool read_only, long tcount)
355
356
plan .magic = _SPI_PLAN_MAGIC ;
356
357
plan .cursor_options = 0 ;
357
358
358
- _SPI_prepare_plan (src , & plan , NULL );
359
+ _SPI_prepare_oneshot_plan (src , & plan );
359
360
360
361
res = _SPI_execute_plan (& plan , NULL ,
361
362
InvalidSnapshot , InvalidSnapshot ,
@@ -506,7 +507,7 @@ SPI_execute_with_args(const char *src,
506
507
paramLI = _SPI_convert_params (nargs , argtypes ,
507
508
Values , Nulls );
508
509
509
- _SPI_prepare_plan (src , & plan , paramLI );
510
+ _SPI_prepare_oneshot_plan (src , & plan );
510
511
511
512
res = _SPI_execute_plan (& plan , paramLI ,
512
513
InvalidSnapshot , InvalidSnapshot ,
@@ -547,7 +548,7 @@ SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes,
547
548
plan .parserSetup = NULL ;
548
549
plan .parserSetupArg = NULL ;
549
550
550
- _SPI_prepare_plan (src , & plan , NULL );
551
+ _SPI_prepare_plan (src , & plan );
551
552
552
553
/* copy plan to procedure context */
553
554
result = _SPI_make_plan_non_temp (& plan );
@@ -584,7 +585,7 @@ SPI_prepare_params(const char *src,
584
585
plan .parserSetup = parserSetup ;
585
586
plan .parserSetupArg = parserSetupArg ;
586
587
587
- _SPI_prepare_plan (src , & plan , NULL );
588
+ _SPI_prepare_plan (src , & plan );
588
589
589
590
/* copy plan to procedure context */
590
591
result = _SPI_make_plan_non_temp (& plan );
@@ -599,7 +600,8 @@ SPI_keepplan(SPIPlanPtr plan)
599
600
{
600
601
ListCell * lc ;
601
602
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 )
603
605
return SPI_ERROR_ARGUMENT ;
604
606
605
607
/*
@@ -1083,7 +1085,7 @@ SPI_cursor_open_with_args(const char *name,
1083
1085
paramLI = _SPI_convert_params (nargs , argtypes ,
1084
1086
Values , Nulls );
1085
1087
1086
- _SPI_prepare_plan (src , & plan , paramLI );
1088
+ _SPI_prepare_plan (src , & plan );
1087
1089
1088
1090
/* We needn't copy the plan; SPI_cursor_open_internal will do so */
1089
1091
@@ -1645,10 +1647,6 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
1645
1647
*
1646
1648
* At entry, plan->argtypes and plan->nargs (or alternatively plan->parserSetup
1647
1649
* 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!
1652
1650
*
1653
1651
* Results are stored into *plan (specifically, plan->plancache_list).
1654
1652
* Note that the result data is all in CurrentMemoryContext or child contexts
@@ -1657,13 +1655,12 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
1657
1655
* parsing is also left in CurrentMemoryContext.
1658
1656
*/
1659
1657
static void
1660
- _SPI_prepare_plan (const char * src , SPIPlanPtr plan , ParamListInfo boundParams )
1658
+ _SPI_prepare_plan (const char * src , SPIPlanPtr plan )
1661
1659
{
1662
1660
List * raw_parsetree_list ;
1663
1661
List * plancache_list ;
1664
1662
ListCell * list_item ;
1665
1663
ErrorContextCallback spierrcontext ;
1666
- int cursor_options = plan -> cursor_options ;
1667
1664
1668
1665
/*
1669
1666
* Setup error traceback support for ereport()
@@ -1726,13 +1723,80 @@ _SPI_prepare_plan(const char *src, SPIPlanPtr plan, ParamListInfo boundParams)
1726
1723
plan -> nargs ,
1727
1724
plan -> parserSetup ,
1728
1725
plan -> parserSetupArg ,
1729
- cursor_options ,
1726
+ plan -> cursor_options ,
1730
1727
false); /* not fixed result */
1731
1728
1732
1729
plancache_list = lappend (plancache_list , plansource );
1733
1730
}
1734
1731
1735
1732
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;
1736
1800
1737
1801
/*
1738
1802
* Pop the error context stack
@@ -1770,7 +1834,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
1770
1834
* Setup error traceback support for ereport()
1771
1835
*/
1772
1836
spierrcontext .callback = _SPI_error_callback ;
1773
- spierrcontext .arg = NULL ;
1837
+ spierrcontext .arg = NULL ; /* we'll fill this below */
1774
1838
spierrcontext .previous = error_context_stack ;
1775
1839
error_context_stack = & spierrcontext ;
1776
1840
@@ -1816,6 +1880,47 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
1816
1880
1817
1881
spierrcontext .arg = (void * ) plansource -> query_string ;
1818
1882
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
+
1819
1924
/*
1820
1925
* Replan if needed, and increment plan refcount. If it's a saved
1821
1926
* plan, the refcount must be backed by the CurrentResourceOwner.
@@ -2313,6 +2418,8 @@ _SPI_make_plan_non_temp(SPIPlanPtr plan)
2313
2418
/* Assert the input is a temporary SPIPlan */
2314
2419
Assert (plan -> magic == _SPI_PLAN_MAGIC );
2315
2420
Assert (plan -> plancxt == NULL );
2421
+ /* One-shot plans can't be saved */
2422
+ Assert (!plan -> oneshot );
2316
2423
2317
2424
/*
2318
2425
* Create a memory context for the plan, underneath the procedure context.
@@ -2330,6 +2437,7 @@ _SPI_make_plan_non_temp(SPIPlanPtr plan)
2330
2437
newplan = (SPIPlanPtr ) palloc (sizeof (_SPI_plan ));
2331
2438
newplan -> magic = _SPI_PLAN_MAGIC ;
2332
2439
newplan -> saved = false;
2440
+ newplan -> oneshot = false;
2333
2441
newplan -> plancache_list = NIL ;
2334
2442
newplan -> plancxt = plancxt ;
2335
2443
newplan -> cursor_options = plan -> cursor_options ;
@@ -2379,6 +2487,9 @@ _SPI_save_plan(SPIPlanPtr plan)
2379
2487
MemoryContext oldcxt ;
2380
2488
ListCell * lc ;
2381
2489
2490
+ /* One-shot plans can't be saved */
2491
+ Assert (!plan -> oneshot );
2492
+
2382
2493
/*
2383
2494
* Create a memory context for the plan. We don't expect the plan to be
2384
2495
* very large, so use smaller-than-default alloc parameters. It's a
@@ -2395,6 +2506,7 @@ _SPI_save_plan(SPIPlanPtr plan)
2395
2506
newplan = (SPIPlanPtr ) palloc (sizeof (_SPI_plan ));
2396
2507
newplan -> magic = _SPI_PLAN_MAGIC ;
2397
2508
newplan -> saved = false;
2509
+ newplan -> oneshot = false;
2398
2510
newplan -> plancache_list = NIL ;
2399
2511
newplan -> plancxt = plancxt ;
2400
2512
newplan -> cursor_options = plan -> cursor_options ;
0 commit comments