|
42 | 42 | #include "utils/array.h" |
43 | 43 | #include "utils/builtins.h" |
44 | 44 | #include "utils/geo_decls.h" |
| 45 | +#include "utils/lsyscache.h" |
45 | 46 | #include "utils/memutils.h" |
46 | 47 | #include "utils/rel.h" |
47 | 48 | #include "utils/typcache.h" |
| 49 | +#include "utils/varlena.h" |
48 | 50 |
|
49 | 51 | #define EXPECT_TRUE(expr) \ |
50 | 52 | do { \ |
@@ -1028,3 +1030,200 @@ test_relpath(PG_FUNCTION_ARGS) |
1028 | 1030 |
|
1029 | 1031 | PG_RETURN_VOID(); |
1030 | 1032 | } |
| 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 | +} |
0 commit comments