Skip to content

Commit d9239da

Browse files
author
Commitfest Bot
committed
[CF 5923] v0 - Let plan_cache_mode to be a little less strict
This branch was automatically generated by a robot using patches from an email thread registered at: https://commitfest.postgresql.org/patch/5923 The branch will be overwritten each time a new patch version is posted to the thread, and also periodically to check for bitrot caused by changes on the master branch. Patch(es): https://www.postgresql.org/message-id/[email protected] Author(s): Andrei Lepikhov
2 parents e1d9171 + e99fb57 commit d9239da

File tree

4 files changed

+245
-0
lines changed

4 files changed

+245
-0
lines changed

src/test/regress/expected/misc_functions.out

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -914,6 +914,18 @@ SELECT test_relpath();
914914

915915
(1 row)
916916

917+
CREATE FUNCTION prepare_spi_plan(text, text, VARIADIC text[])
918+
RETURNS bigint
919+
AS :'regresslib', 'prepare_spi_plan'
920+
LANGUAGE C;
921+
CREATE FUNCTION execute_spi_plan(bigint, text, "any")
922+
RETURNS SETOF record
923+
AS :'regresslib', 'execute_spi_plan'
924+
LANGUAGE C STRICT;
925+
CREATE FUNCTION free_spi_plan(bigint)
926+
RETURNS void
927+
AS :'regresslib', 'free_spi_plan'
928+
LANGUAGE C STRICT;
917929
-- pg_replication_origin.roname limit
918930
SELECT pg_replication_origin_create('regress_' || repeat('a', 505));
919931
ERROR: replication origin name is too long

src/test/regress/regress.c

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,11 @@
4242
#include "utils/array.h"
4343
#include "utils/builtins.h"
4444
#include "utils/geo_decls.h"
45+
#include "utils/lsyscache.h"
4546
#include "utils/memutils.h"
4647
#include "utils/rel.h"
4748
#include "utils/typcache.h"
49+
#include "utils/varlena.h"
4850

4951
#define EXPECT_TRUE(expr) \
5052
do { \
@@ -1028,3 +1030,200 @@ test_relpath(PG_FUNCTION_ARGS)
10281030

10291031
PG_RETURN_VOID();
10301032
}
1033+
1034+
static int
1035+
setup_cursor_options(const char *options_string)
1036+
{
1037+
List *optionLst = NIL;
1038+
ListCell *lc;
1039+
int cursorOptions = 0;
1040+
1041+
if (!SplitIdentifierString(pstrdup(options_string), ',', &optionLst))
1042+
/* syntax error in option list */
1043+
ereport(ERROR,
1044+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1045+
errmsg("parameter \"%s\" must be a list of parameters",
1046+
"cursor_options")));
1047+
1048+
foreach(lc, optionLst)
1049+
{
1050+
const char *option = (const char *) lfirst(lc);
1051+
1052+
if (pg_strcasecmp(option, "fast_plan") == 0)
1053+
cursorOptions |= CURSOR_OPT_FAST_PLAN;
1054+
else if (pg_strcasecmp(option, "generic_plan") == 0)
1055+
cursorOptions |= CURSOR_OPT_GENERIC_PLAN;
1056+
else if (pg_strcasecmp(option, "custom_plan") == 0)
1057+
cursorOptions |= CURSOR_OPT_CUSTOM_PLAN;
1058+
else if (pg_strcasecmp(option, "parallel_ok") == 0)
1059+
cursorOptions |= CURSOR_OPT_PARALLEL_OK;
1060+
else
1061+
ereport(ERROR,
1062+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1063+
errmsg("unknown cursor option %s", option)));
1064+
}
1065+
1066+
list_free(optionLst);
1067+
return cursorOptions;
1068+
}
1069+
1070+
/*
1071+
* Prepare a plan cache entry for incoming parameterised query with specific
1072+
* set of cursor options.
1073+
*
1074+
* cursor_options string may contain any combination of the following values:
1075+
* 'fast_plan', 'generic_plan', 'custom_plan', or 'parallel_ok'.
1076+
*
1077+
* Returns internal pointer to saved SPI plan.
1078+
*/
1079+
PG_FUNCTION_INFO_V1(prepare_spi_plan);
1080+
Datum
1081+
prepare_spi_plan(PG_FUNCTION_ARGS)
1082+
{
1083+
const char *query_text;
1084+
int cursorOptions = 0;
1085+
ArrayType *arr;
1086+
Datum *elements = NULL;
1087+
bool *nulls = NULL;
1088+
Oid element_type = InvalidOid;
1089+
int16 elmlen;
1090+
bool elmbyval;
1091+
char elmalign;
1092+
int nitems;
1093+
int i;
1094+
Oid *argtypes;
1095+
SPIPlanPtr result;
1096+
1097+
Assert(PG_NARGS() == 3 && get_fn_expr_variadic(fcinfo->flinfo));
1098+
1099+
/* Check supplied arguments */
1100+
if (PG_ARGISNULL(0))
1101+
ereport(ERROR,
1102+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1103+
errmsg("query text cannot be null")));
1104+
query_text = text_to_cstring(PG_GETARG_TEXT_PP(0));
1105+
if (PG_ARGISNULL(2))
1106+
ereport(ERROR,
1107+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1108+
errmsg("list of parameters canot be null")));
1109+
1110+
if (!PG_ARGISNULL(1))
1111+
{
1112+
char *options = text_to_cstring(PG_GETARG_TEXT_PP(1));
1113+
1114+
cursorOptions = setup_cursor_options(options);
1115+
}
1116+
1117+
/* Prepare the Oid array of the query parameters */
1118+
1119+
arr = PG_GETARG_ARRAYTYPE_P(2);
1120+
element_type = ARR_ELEMTYPE(arr);
1121+
get_typlenbyvalalign(element_type, &elmlen, &elmbyval, &elmalign);
1122+
1123+
/* Extract all array elements */
1124+
deconstruct_array(arr, element_type, elmlen, elmbyval, elmalign,
1125+
&elements, &nulls, &nitems);
1126+
1127+
argtypes = palloc(nitems * sizeof(Oid));
1128+
for (i = 0; i < nitems; i++)
1129+
{
1130+
const char *typname;
1131+
1132+
if (nulls[i])
1133+
ereport(ERROR,
1134+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1135+
errmsg("type name cannot be NULL")));
1136+
1137+
typname = TextDatumGetCString(elements[i]);
1138+
argtypes[i] = DirectFunctionCall1Coll(regtypein,
1139+
InvalidOid,
1140+
CStringGetDatum(typname));
1141+
}
1142+
1143+
/* Create plancache entry and save the plan */
1144+
SPI_connect();
1145+
result = SPI_prepare_cursor(query_text, nitems, argtypes, cursorOptions);
1146+
result = SPI_saveplan(result);
1147+
SPI_finish();
1148+
PG_RETURN_POINTER(result);
1149+
}
1150+
1151+
PG_FUNCTION_INFO_V1(execute_spi_plan);
1152+
Datum
1153+
execute_spi_plan(PG_FUNCTION_ARGS)
1154+
{
1155+
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
1156+
SPIPlanPtr plan = (SPIPlanPtr) PG_GETARG_POINTER(0);
1157+
int nargs = PG_NARGS();
1158+
Datum *values;
1159+
char *Nulls;
1160+
int spirc;
1161+
int i;
1162+
1163+
if (!SPI_plan_is_valid(plan))
1164+
PG_RETURN_NULL();
1165+
1166+
Assert(!get_fn_expr_variadic(fcinfo->flinfo));
1167+
1168+
/*
1169+
* Caller wants to change cursor options. Update it for each statement.
1170+
*/
1171+
if (!PG_ARGISNULL(1))
1172+
{
1173+
char *options = text_to_cstring(PG_GETARG_TEXT_PP(1));
1174+
List *plansources = SPI_plan_get_plan_sources(plan);
1175+
int cursorOptions = 0;
1176+
ListCell *lc;
1177+
1178+
cursorOptions = setup_cursor_options(options);
1179+
1180+
foreach(lc, plansources)
1181+
{
1182+
CachedPlanSource *src = lfirst(lc);
1183+
1184+
src->cursor_options = cursorOptions;
1185+
}
1186+
}
1187+
1188+
values = palloc((nargs - 2) * sizeof(Datum));
1189+
Nulls = palloc((nargs - 2) * sizeof(char *));
1190+
for (i = 2; i < nargs; i++)
1191+
{
1192+
if (!PG_ARGISNULL(i))
1193+
{
1194+
Nulls[i - 2] = ' ';
1195+
values[i - 2] = PG_GETARG_DATUM(i);
1196+
}
1197+
else
1198+
Nulls[i - 2] = 'n';
1199+
}
1200+
1201+
SPI_connect();
1202+
spirc = SPI_execute_plan(plan, values, Nulls, false, 0);
1203+
if (spirc <= 0)
1204+
elog(ERROR, "failed to execute the SPI plan %d", spirc);
1205+
1206+
rsinfo->expectedDesc = SPI_tuptable->tupdesc;
1207+
InitMaterializedSRF(fcinfo, MAT_SRF_USE_EXPECTED_DESC | MAT_SRF_BLESS);
1208+
1209+
for (i = 0; i < SPI_processed; i++)
1210+
{
1211+
tuplestore_puttuple(rsinfo->setResult, SPI_tuptable->vals[i]);
1212+
}
1213+
1214+
SPI_finish();
1215+
1216+
return (Datum) 0;
1217+
}
1218+
1219+
PG_FUNCTION_INFO_V1(free_spi_plan);
1220+
Datum
1221+
free_spi_plan(PG_FUNCTION_ARGS)
1222+
{
1223+
SPIPlanPtr plan = (SPIPlanPtr) PG_GETARG_POINTER(0);
1224+
1225+
SPI_connect();
1226+
SPI_freeplan(plan);
1227+
SPI_finish();
1228+
PG_RETURN_VOID();
1229+
}

src/test/regress/sql/misc_functions.sql

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,5 +412,20 @@ CREATE FUNCTION test_relpath()
412412
LANGUAGE C;
413413
SELECT test_relpath();
414414

415+
CREATE FUNCTION prepare_spi_plan(text, text, VARIADIC text[])
416+
RETURNS bigint
417+
AS :'regresslib', 'prepare_spi_plan'
418+
LANGUAGE C;
419+
420+
CREATE FUNCTION execute_spi_plan(bigint, text, "any")
421+
RETURNS SETOF record
422+
AS :'regresslib', 'execute_spi_plan'
423+
LANGUAGE C STRICT;
424+
425+
CREATE FUNCTION free_spi_plan(bigint)
426+
RETURNS void
427+
AS :'regresslib', 'free_spi_plan'
428+
LANGUAGE C STRICT;
429+
415430
-- pg_replication_origin.roname limit
416431
SELECT pg_replication_origin_create('regress_' || repeat('a', 505));

src/test/regress/sql/plancache.sql

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,3 +223,22 @@ select name, generic_plans, custom_plans from pg_prepared_statements
223223
where name = 'test_mode_pp';
224224

225225
drop table test_mode;
226+
227+
-- Check the interference between plan_cache_mode and cursor_options
228+
-- EXPLAIN (COSTS OFF, GENERIC_PLAN)
229+
230+
SELECT prepare_spi_plan(NULL, NULL, NULL); -- ERROR
231+
SELECT prepare_spi_plan(
232+
'EXPLAIN (COSTS OFF) SELECT * FROM pcachetest WHERE q1 = $1',
233+
NULL, 'integer') AS p1 \gset
234+
SELECT prepare_spi_plan(
235+
'SELECT * FROM pcachetest WHERE q1 = $1 OR q1 = $2',
236+
NULL, 'integer', NULL); --ERROR
237+
SELECT prepare_spi_plan(
238+
'SELECT * FROM pcachetest WHERE q1 = $1 OR q1 = 3',
239+
NULL, 'integer', 'numeric') AS p2 \gset
240+
241+
SELECT execute_spi_plan(:p1, 'generic_plan', 42);
242+
243+
SELECT free_spi_plan(:p1);
244+
SELECT free_spi_plan(:p2);

0 commit comments

Comments
 (0)