static void _SPI_prepare_oneshot_plan(const char *src, SPIPlanPtr plan);
-static int _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
+static int _SPI_execute_plan(SPIPlanPtr plan, const SPIExecuteOptions *options,
Snapshot snapshot, Snapshot crosscheck_snapshot,
- bool read_only, bool allow_nonatomic,
- bool fire_triggers, uint64 tcount,
- DestReceiver *caller_dest,
- ResourceOwner plan_owner);
+ bool fire_triggers);
static ParamListInfo _SPI_convert_params(int nargs, Oid *argtypes,
Datum *Values, const char *Nulls);
SPI_execute(const char *src, bool read_only, long tcount)
{
_SPI_plan plan;
+ SPIExecuteOptions options;
int res;
if (src == NULL || tcount < 0)
_SPI_prepare_oneshot_plan(src, &plan);
- res = _SPI_execute_plan(&plan, NULL,
+ memset(&options, 0, sizeof(options));
+ options.read_only = read_only;
+ options.tcount = tcount;
+
+ res = _SPI_execute_plan(&plan, &options,
InvalidSnapshot, InvalidSnapshot,
- read_only, false,
- true, tcount,
- NULL, NULL);
+ true);
_SPI_end_call(true);
return res;
_SPI_prepare_oneshot_plan(src, &plan);
- res = _SPI_execute_plan(&plan, options->params,
+ res = _SPI_execute_plan(&plan, options,
InvalidSnapshot, InvalidSnapshot,
- options->read_only, options->allow_nonatomic,
- true, options->tcount,
- options->dest, options->owner);
+ true);
_SPI_end_call(true);
return res;
SPI_execute_plan(SPIPlanPtr plan, Datum *Values, const char *Nulls,
bool read_only, long tcount)
{
+ SPIExecuteOptions options;
int res;
if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || tcount < 0)
if (res < 0)
return res;
- res = _SPI_execute_plan(plan,
- _SPI_convert_params(plan->nargs, plan->argtypes,
- Values, Nulls),
+ memset(&options, 0, sizeof(options));
+ options.params = _SPI_convert_params(plan->nargs, plan->argtypes,
+ Values, Nulls);
+ options.read_only = read_only;
+ options.tcount = tcount;
+
+ res = _SPI_execute_plan(plan, &options,
InvalidSnapshot, InvalidSnapshot,
- read_only, false,
- true, tcount,
- NULL, NULL);
+ true);
_SPI_end_call(true);
return res;
if (res < 0)
return res;
- res = _SPI_execute_plan(plan, options->params,
+ res = _SPI_execute_plan(plan, options,
InvalidSnapshot, InvalidSnapshot,
- options->read_only, options->allow_nonatomic,
- true, options->tcount,
- options->dest, options->owner);
+ true);
_SPI_end_call(true);
return res;
SPI_execute_plan_with_paramlist(SPIPlanPtr plan, ParamListInfo params,
bool read_only, long tcount)
{
+ SPIExecuteOptions options;
int res;
if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || tcount < 0)
if (res < 0)
return res;
- res = _SPI_execute_plan(plan, params,
+ memset(&options, 0, sizeof(options));
+ options.params = params;
+ options.read_only = read_only;
+ options.tcount = tcount;
+
+ res = _SPI_execute_plan(plan, &options,
InvalidSnapshot, InvalidSnapshot,
- read_only, false,
- true, tcount,
- NULL, NULL);
+ true);
_SPI_end_call(true);
return res;
Snapshot snapshot, Snapshot crosscheck_snapshot,
bool read_only, bool fire_triggers, long tcount)
{
+ SPIExecuteOptions options;
int res;
if (plan == NULL || plan->magic != _SPI_PLAN_MAGIC || tcount < 0)
if (res < 0)
return res;
- res = _SPI_execute_plan(plan,
- _SPI_convert_params(plan->nargs, plan->argtypes,
- Values, Nulls),
+ memset(&options, 0, sizeof(options));
+ options.params = _SPI_convert_params(plan->nargs, plan->argtypes,
+ Values, Nulls);
+ options.read_only = read_only;
+ options.tcount = tcount;
+
+ res = _SPI_execute_plan(plan, &options,
snapshot, crosscheck_snapshot,
- read_only, false,
- fire_triggers, tcount,
- NULL, NULL);
+ fire_triggers);
_SPI_end_call(true);
return res;
int res;
_SPI_plan plan;
ParamListInfo paramLI;
+ SPIExecuteOptions options;
if (src == NULL || nargs < 0 || tcount < 0)
return SPI_ERROR_ARGUMENT;
_SPI_prepare_oneshot_plan(src, &plan);
- res = _SPI_execute_plan(&plan, paramLI,
+ memset(&options, 0, sizeof(options));
+ options.params = paramLI;
+ options.read_only = read_only;
+ options.tcount = tcount;
+
+ res = _SPI_execute_plan(&plan, &options,
InvalidSnapshot, InvalidSnapshot,
- read_only, false,
- true, tcount,
- NULL, NULL);
+ true);
_SPI_end_call(true);
return res;
}
/*
- * Execute the given plan with the given parameter values
+ * _SPI_execute_plan: execute the given plan with the given options
*
+ * options contains options accessible from outside SPI:
+ * params: parameter values to pass to query
+ * read_only: true for read-only execution (no CommandCounterIncrement)
+ * allow_nonatomic: true to allow nonatomic CALL/DO execution
+ * must_return_tuples: throw error if query doesn't return tuples
+ * tcount: execution tuple-count limit, or 0 for none
+ * dest: DestReceiver to receive output, or NULL for normal SPI output
+ * owner: ResourceOwner that will be used to hold refcount on plan;
+ * if NULL, CurrentResourceOwner is used (ignored for non-saved plan)
+ *
+ * Additional, only-internally-accessible options:
* snapshot: query snapshot to use, or InvalidSnapshot for the normal
* behavior of taking a new snapshot for each query.
* crosscheck_snapshot: for RI use, all others pass InvalidSnapshot
- * read_only: true for read-only execution (no CommandCounterIncrement)
- * allow_nonatomic: true to allow nonatomic CALL/DO execution
* fire_triggers: true to fire AFTER triggers at end of query (normal case);
* false means any AFTER triggers are postponed to end of outer query
- * tcount: execution tuple-count limit, or 0 for none
- * caller_dest: DestReceiver to receive output, or NULL for normal SPI output
- * plan_owner: ResourceOwner that will be used to hold refcount on plan;
- * if NULL, CurrentResourceOwner is used (ignored for non-saved plan)
*/
static int
-_SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
+_SPI_execute_plan(SPIPlanPtr plan, const SPIExecuteOptions *options,
Snapshot snapshot, Snapshot crosscheck_snapshot,
- bool read_only, bool allow_nonatomic,
- bool fire_triggers, uint64 tcount,
- DestReceiver *caller_dest, ResourceOwner plan_owner)
+ bool fire_triggers)
{
int my_res = 0;
uint64 my_processed = 0;
SPITupleTable *my_tuptable = NULL;
int res = 0;
bool pushed_active_snap = false;
+ ResourceOwner plan_owner = options->owner;
SPICallbackArg spicallbackarg;
ErrorContextCallback spierrcontext;
CachedPlan *cplan = NULL;
*/
if (snapshot != InvalidSnapshot)
{
- Assert(!allow_nonatomic);
- if (read_only)
+ Assert(!options->allow_nonatomic);
+ if (options->read_only)
{
PushActiveSnapshot(snapshot);
pushed_active_snap = true;
else if (plan_owner == NULL)
plan_owner = CurrentResourceOwner;
+ /*
+ * We interpret must_return_tuples as "there must be at least one query,
+ * and all of them must return tuples". This is a bit laxer than
+ * SPI_is_cursor_plan's check, but there seems no reason to enforce that
+ * there be only one query.
+ */
+ if (options->must_return_tuples && plan->plancache_list == NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("empty query does not return tuples")));
+
foreach(lc1, plan->plancache_list)
{
CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc1);
false); /* not fixed result */
}
+ /*
+ * If asked to, complain when query does not return tuples.
+ * (Replanning can't change this, so we can check it before that.
+ * However, we can't check it till after parse analysis, so in the
+ * case of a one-shot plan this is the earliest we could check.)
+ */
+ if (options->must_return_tuples && !plansource->resultDesc)
+ {
+ /* try to give a good error message */
+ const char *cmdtag;
+
+ /* A SELECT without resultDesc must be SELECT INTO */
+ if (plansource->commandTag == CMDTAG_SELECT)
+ cmdtag = "SELECT INTO";
+ else
+ cmdtag = GetCommandTagName(plansource->commandTag);
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ /* translator: %s is name of a SQL command, eg INSERT */
+ errmsg("%s query does not return tuples", cmdtag)));
+ }
+
/*
* Replan if needed, and increment plan refcount. If it's a saved
* plan, the refcount must be backed by the plan_owner.
*/
- cplan = GetCachedPlan(plansource, paramLI,
+ cplan = GetCachedPlan(plansource, options->params,
plan_owner, _SPI_current->queryEnv);
stmt_list = cplan->stmt_list;
* Skip it when doing non-atomic execution, though (we rely
* entirely on the Portal snapshot in that case).
*/
- if (!read_only && !allow_nonatomic)
+ if (!options->read_only && !options->allow_nonatomic)
{
if (pushed_active_snap)
PopActiveSnapshot();
}
}
- if (read_only && !CommandIsReadOnly(stmt))
+ if (options->read_only && !CommandIsReadOnly(stmt))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
/* translator: %s is a SQL statement name */
* command and update the snapshot. (But skip it if the snapshot
* isn't under our control.)
*/
- if (!read_only && pushed_active_snap)
+ if (!options->read_only && pushed_active_snap)
{
CommandCounterIncrement();
UpdateActiveSnapshotCommandId();
*/
if (!canSetTag)
dest = CreateDestReceiver(DestNone);
- else if (caller_dest)
- dest = caller_dest;
+ else if (options->dest)
+ dest = options->dest;
else
dest = CreateDestReceiver(DestSPI);
plansource->query_string,
snap, crosscheck_snapshot,
dest,
- paramLI, _SPI_current->queryEnv,
+ options->params,
+ _SPI_current->queryEnv,
0);
res = _SPI_pquery(qdesc, fire_triggers,
- canSetTag ? tcount : 0);
+ canSetTag ? options->tcount : 0);
FreeQueryDesc(qdesc);
}
else
* nonatomic operations, tell ProcessUtility this is an atomic
* execution context.
*/
- if (_SPI_current->atomic || !allow_nonatomic)
+ if (_SPI_current->atomic || !options->allow_nonatomic)
context = PROCESS_UTILITY_QUERY;
else
context = PROCESS_UTILITY_QUERY_NONATOMIC;
plansource->query_string,
true, /* protect plancache's node tree */
context,
- paramLI,
+ options->params,
_SPI_current->queryEnv,
dest,
&qc);
* command. This ensures that its effects are visible, in case it was
* DDL that would affect the next CachedPlanSource.
*/
- if (!read_only)
+ if (!options->read_only)
CommandCounterIncrement();
}