-<!-- $PostgreSQL: pgsql/doc/src/sgml/spi.sgml,v 1.61 2008/03/25 22:42:42 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/spi.sgml,v 1.62 2008/04/01 03:09:30 tgl Exp $ -->
<chapter id="spi">
<title>Server Programming Interface</title>
<title>Description</title>
<para>
- <function>SPI_pop</function> pops the previous environment from the
+ <function>SPI_pop</function> pops the previous environment from the
SPI call stack. See <function>SPI_push</function>.
</para>
</refsect1>
then you can use the
global pointer <literal>SPITupleTable *SPI_tuptable</literal> to
access the result rows. Some utility commands (such as
- <command>EXPLAIN</>) also return row sets, and <literal>SPI_tuptable</>
+ <command>EXPLAIN</>) also return row sets, and <literal>SPI_tuptable</>
will contain the result in these cases too.
</para>
<!-- *********************************************** -->
+<refentry id="spi-spi-execute-with-args">
+ <refmeta>
+ <refentrytitle>SPI_execute_with_args</refentrytitle>
+ </refmeta>
+
+ <refnamediv>
+ <refname>SPI_execute_with_args</refname>
+ <refpurpose>execute a command with out-of-line parameters</refpurpose>
+ </refnamediv>
+
+ <indexterm><primary>SPI_execute_with_args</primary></indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+int SPI_execute_with_args(const char *<parameter>command</parameter>,
+ int <parameter>nargs</parameter>, Oid *<parameter>argtypes</parameter>,
+ Datum *<parameter>values</parameter>, const char *<parameter>nulls</parameter>,
+ bool <parameter>read_only</parameter>, long <parameter>count</parameter>)
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <function>SPI_execute_with_args</function> executes a command that might
+ include references to externally supplied parameters. The command text
+ refers to a parameter as <literal>$<replaceable>n</></literal>, and
+ the call specifies data types and values for each such symbol.
+ <parameter>read_only</parameter> and <parameter>count</parameter> have
+ the same interpretation as in <function>SPI_execute</function>.
+ </para>
+
+ <para>
+ The main advantage of this routine compared to
+ <function>SPI_execute</function> is that data values can be inserted
+ into the command without tedious quoting/escaping, and thus with much
+ less risk of SQL-injection attacks.
+ </para>
+
+ <para>
+ Similar results can be achieved with <function>SPI_prepare</> followed by
+ <function>SPI_execute_plan</function>; however, when using this function
+ the query plan is customized to the specific parameter values provided.
+ For one-time query execution, this function should be preferred.
+ If the same command is to be executed with many different parameters,
+ either method might be faster, depending on the cost of re-planning
+ versus the benefit of custom plans.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Arguments</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>const char * <parameter>command</parameter></literal></term>
+ <listitem>
+ <para>
+ command string
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>int <parameter>nargs</parameter></literal></term>
+ <listitem>
+ <para>
+ number of input parameters (<literal>$1</>, <literal>$2</>, etc.)
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>Oid * <parameter>argtypes</parameter></literal></term>
+ <listitem>
+ <para>
+ an array containing the <acronym>OID</acronym>s of
+ the data types of the parameters
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>Datum * <parameter>values</parameter></literal></term>
+ <listitem>
+ <para>
+ an array of actual parameter values
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>const char * <parameter>nulls</parameter></literal></term>
+ <listitem>
+ <para>
+ an array describing which parameters are null
+ </para>
+
+ <para>
+ If <parameter>nulls</parameter> is <symbol>NULL</symbol> then
+ <function>SPI_execute_with_args</function> assumes that no parameters are
+ null.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>bool <parameter>read_only</parameter></literal></term>
+ <listitem>
+ <para>
+ <literal>true</> for read-only execution
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>long <parameter>count</parameter></literal></term>
+ <listitem>
+ <para>
+ maximum number of rows to process or return
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>
+ The return value is the same as for <function>SPI_execute</function>.
+ </para>
+
+ <para>
+ <varname>SPI_processed</varname> and
+ <varname>SPI_tuptable</varname> are set as in
+ <function>SPI_execute</function> if successful.
+ </para>
+ </refsect1>
+</refentry>
+
+<!-- *********************************************** -->
+
<refentry id="spi-spi-prepare">
<refmeta>
<refentrytitle>SPI_prepare</refentrytitle>
</para>
</listitem>
</varlistentry>
-
+
<varlistentry>
<term><literal>int <parameter>cursorOptions</parameter></literal></term>
<listitem>
<!-- *********************************************** -->
+<refentry id="spi-spi-cursor-open-with-args">
+ <refmeta>
+ <refentrytitle>SPI_cursor_open_with_args</refentrytitle>
+ </refmeta>
+
+ <refnamediv>
+ <refname>SPI_cursor_open_with_args</refname>
+ <refpurpose>set up a cursor using a query and parameters</refpurpose>
+ </refnamediv>
+
+ <indexterm><primary>SPI_cursor_open_with_args</primary></indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+Portal SPI_cursor_open_with_args(const char *<parameter>name</parameter>,
+ const char *<parameter>command</parameter>,
+ int <parameter>nargs</parameter>, Oid *<parameter>argtypes</parameter>,
+ Datum *<parameter>values</parameter>, const char *<parameter>nulls</parameter>,
+ bool <parameter>read_only</parameter>, int <parameter>cursorOptions</parameter>)
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <function>SPI_cursor_open_with_args</function> sets up a cursor
+ (internally, a portal) that will execute the specified query.
+ Most of the parameters have the same meanings as the corresponding
+ parameters to <function>SPI_prepare_cursor</function>
+ and <function>SPI_cursor_open</function>.
+ </para>
+
+ <para>
+ For one-time query execution, this function should be preferred
+ over <function>SPI_prepare_cursor</function> followed by
+ <function>SPI_cursor_open</function>.
+ If the same command is to be executed with many different parameters,
+ either method might be faster, depending on the cost of re-planning
+ versus the benefit of custom plans.
+ </para>
+
+ <para>
+ The passed-in data will be copied into the cursor's portal, so it
+ can be freed while the cursor still exists.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Arguments</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><literal>const char * <parameter>name</parameter></literal></term>
+ <listitem>
+ <para>
+ name for portal, or <symbol>NULL</symbol> to let the system
+ select a name
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>const char * <parameter>command</parameter></literal></term>
+ <listitem>
+ <para>
+ command string
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>int <parameter>nargs</parameter></literal></term>
+ <listitem>
+ <para>
+ number of input parameters (<literal>$1</>, <literal>$2</>, etc.)
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>Oid * <parameter>argtypes</parameter></literal></term>
+ <listitem>
+ <para>
+ an array containing the <acronym>OID</acronym>s of
+ the data types of the parameters
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>Datum * <parameter>values</parameter></literal></term>
+ <listitem>
+ <para>
+ an array of actual parameter values
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>const char * <parameter>nulls</parameter></literal></term>
+ <listitem>
+ <para>
+ an array describing which parameters are null
+ </para>
+
+ <para>
+ If <parameter>nulls</parameter> is <symbol>NULL</symbol> then
+ <function>SPI_cursor_open_with_args</function> assumes that no
+ parameters are null.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>bool <parameter>read_only</parameter></literal></term>
+ <listitem>
+ <para>
+ <literal>true</> for read-only execution
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>int <parameter>cursorOptions</parameter></literal></term>
+ <listitem>
+ <para>
+ integer bitmask of cursor options; zero produces default behavior
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>
+ Pointer to portal containing the cursor. Note there is no error
+ return convention; any error will be reported via <function>elog</>.
+ </para>
+ </refsect1>
+</refentry>
+
+<!-- *********************************************** -->
+
<refentry id="spi-spi-cursor-find">
<refmeta>
<refentrytitle>SPI_cursor_find</refentrytitle>
<para>
See the SQL <xref linkend="sql-fetch" endterm="sql-fetch-title"> command
- for details of the interpretation of the
+ for details of the interpretation of the
<parameter>direction</parameter> and
<parameter>count</parameter> parameters.
</para>
<para>
See the SQL <xref linkend="sql-fetch" endterm="sql-fetch-title"> command
- for details of the interpretation of the
+ for details of the interpretation of the
<parameter>direction</parameter> and
<parameter>count</parameter> parameters.
</para>
command = text_to_cstring(sql);
SPI_connect();
-
+
ret = SPI_exec(command, cnt);
-
+
proc = SPI_processed;
/*
* If some rows were fetched, print them via elog(INFO).
SPITupleTable *tuptable = SPI_tuptable;
char buf[8192];
int i, j;
-
+
for (j = 0; j < proc; j++)
{
HeapTuple tuple = tuptable->vals[j];
-
+
for (i = 1, buf[0] = 0; i <= tupdesc->natts; i++)
snprintf(buf + strlen (buf), sizeof(buf) - strlen(buf), " %s%s",
SPI_getvalue(tuple, tupdesc, i),
2
2 -- 2 rows * 1 (x in first row)
6 -- 3 rows (2 + 1 just inserted) * 2 (x in second row)
-(4 rows) ^^^^^^
+(4 rows) ^^^^^^
rows visible to execq() in different invocations
</programlisting>
</para>
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.191 2008/03/26 18:48:59 alvherre Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.192 2008/04/01 03:09:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
static int _SPI_connected = -1;
static int _SPI_curid = -1;
-static void _SPI_prepare_plan(const char *src, SPIPlanPtr plan);
+static void _SPI_prepare_plan(const char *src, SPIPlanPtr plan,
+ ParamListInfo boundParams);
-static int _SPI_execute_plan(SPIPlanPtr plan,
- Datum *Values, const char *Nulls,
+static int _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
Snapshot snapshot, Snapshot crosscheck_snapshot,
bool read_only, bool fire_triggers, long tcount);
+static ParamListInfo _SPI_convert_params(int nargs, Oid *argtypes,
+ Datum *Values, const char *Nulls,
+ int pflags);
+
static int _SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, long tcount);
static void _SPI_error_callback(void *arg);
plan.magic = _SPI_PLAN_MAGIC;
plan.cursor_options = 0;
- _SPI_prepare_plan(src, &plan);
+ _SPI_prepare_plan(src, &plan, NULL);
- res = _SPI_execute_plan(&plan, NULL, NULL,
+ res = _SPI_execute_plan(&plan, NULL,
InvalidSnapshot, InvalidSnapshot,
read_only, true, tcount);
return res;
res = _SPI_execute_plan(plan,
- Values, Nulls,
+ _SPI_convert_params(plan->nargs, plan->argtypes,
+ Values, Nulls,
+ 0),
InvalidSnapshot, InvalidSnapshot,
read_only, true, tcount);
return res;
res = _SPI_execute_plan(plan,
- Values, Nulls,
+ _SPI_convert_params(plan->nargs, plan->argtypes,
+ Values, Nulls,
+ 0),
snapshot, crosscheck_snapshot,
read_only, fire_triggers, tcount);
return res;
}
+/*
+ * SPI_execute_with_args -- plan and execute a query with supplied arguments
+ *
+ * This is functionally comparable to SPI_prepare followed by
+ * SPI_execute_plan, except that since we know the plan will be used only
+ * once, we can tell the planner to rely on the parameter values as constants.
+ * This eliminates potential performance disadvantages compared to
+ * inserting the parameter values directly into the query text.
+ */
+int
+SPI_execute_with_args(const char *src,
+ int nargs, Oid *argtypes,
+ Datum *Values, const char *Nulls,
+ bool read_only, long tcount)
+{
+ int res;
+ _SPI_plan plan;
+ ParamListInfo paramLI;
+
+ if (src == NULL || nargs < 0 || tcount < 0)
+ return SPI_ERROR_ARGUMENT;
+
+ if (nargs > 0 && (argtypes == NULL || Values == NULL))
+ return SPI_ERROR_PARAM;
+
+ res = _SPI_begin_call(true);
+ if (res < 0)
+ return res;
+
+ memset(&plan, 0, sizeof(_SPI_plan));
+ plan.magic = _SPI_PLAN_MAGIC;
+ plan.cursor_options = 0;
+ plan.nargs = nargs;
+ plan.argtypes = argtypes;
+
+ paramLI = _SPI_convert_params(nargs, argtypes,
+ Values, Nulls,
+ PARAM_FLAG_CONST);
+
+ _SPI_prepare_plan(src, &plan, paramLI);
+
+ /* We don't need to copy the plan since it will be thrown away anyway */
+
+ res = _SPI_execute_plan(&plan, paramLI,
+ InvalidSnapshot, InvalidSnapshot,
+ read_only, true, tcount);
+
+ _SPI_end_call(true);
+ return res;
+}
+
SPIPlanPtr
SPI_prepare(const char *src, int nargs, Oid *argtypes)
{
plan.nargs = nargs;
plan.argtypes = argtypes;
- _SPI_prepare_plan(src, &plan);
+ _SPI_prepare_plan(src, &plan, NULL);
/* copy plan to procedure context */
result = _SPI_copy_plan(&plan, _SPI_current->procCxt);
}
+/*
+ * SPI_cursor_open_with_args()
+ *
+ * Parse and plan a query and open it as a portal. Like SPI_execute_with_args,
+ * we can tell the planner to rely on the parameter values as constants,
+ * because the plan will only be used once.
+ */
+Portal
+SPI_cursor_open_with_args(const char *name,
+ const char *src,
+ int nargs, Oid *argtypes,
+ Datum *Values, const char *Nulls,
+ bool read_only, int cursorOptions)
+{
+ Portal result;
+ _SPI_plan plan;
+ ParamListInfo paramLI;
+
+ if (src == NULL || nargs < 0)
+ elog(ERROR, "SPI_cursor_open_with_args called with invalid arguments");
+
+ if (nargs > 0 && (argtypes == NULL || Values == NULL))
+ elog(ERROR, "SPI_cursor_open_with_args called with missing parameters");
+
+ SPI_result = _SPI_begin_call(true);
+ if (SPI_result < 0)
+ elog(ERROR, "SPI_cursor_open_with_args called while not connected");
+
+ memset(&plan, 0, sizeof(_SPI_plan));
+ plan.magic = _SPI_PLAN_MAGIC;
+ plan.cursor_options = cursorOptions;
+ plan.nargs = nargs;
+ plan.argtypes = argtypes;
+
+ paramLI = _SPI_convert_params(nargs, argtypes,
+ Values, Nulls,
+ PARAM_FLAG_CONST);
+
+ _SPI_prepare_plan(src, &plan, paramLI);
+
+ /* We needn't copy the plan; SPI_cursor_open will do so */
+
+ /* Adjust stack so that SPI_cursor_open doesn't complain */
+ _SPI_curid--;
+
+ /* SPI_cursor_open expects to be called in procedure memory context */
+ _SPI_procmem();
+
+ result = SPI_cursor_open(name, &plan, Values, Nulls, read_only);
+
+ /* And clean up */
+ _SPI_curid++;
+ _SPI_end_call(true);
+
+ return result;
+}
+
+
/*
* SPI_cursor_find()
*
* Parse and plan a querystring.
*
* At entry, plan->argtypes, plan->nargs, and plan->cursor_options must be
- * valid.
+ * valid. If boundParams isn't NULL then it represents parameter values
+ * that are made available to the planner (as either estimates or hard values
+ * depending on their PARAM_FLAG_CONST marking). The boundParams had better
+ * match the param types embedded in the plan!
*
* Results are stored into *plan (specifically, plan->plancache_list).
* Note however that the result trees are all in CurrentMemoryContext
* and need to be copied somewhere to survive.
*/
static void
-_SPI_prepare_plan(const char *src, SPIPlanPtr plan)
+_SPI_prepare_plan(const char *src, SPIPlanPtr plan, ParamListInfo boundParams)
{
List *raw_parsetree_list;
List *plancache_list;
/* Need a copyObject here to keep parser from modifying raw tree */
stmt_list = pg_analyze_and_rewrite(copyObject(parsetree),
src, argtypes, nargs);
- stmt_list = pg_plan_queries(stmt_list, cursor_options, NULL, false);
+ stmt_list = pg_plan_queries(stmt_list, cursor_options,
+ boundParams, false);
plansource = (CachedPlanSource *) palloc0(sizeof(CachedPlanSource));
cplan = (CachedPlan *) palloc0(sizeof(CachedPlan));
* tcount: execution tuple-count limit, or 0 for none
*/
static int
-_SPI_execute_plan(SPIPlanPtr plan, Datum *Values, const char *Nulls,
+_SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
Snapshot snapshot, Snapshot crosscheck_snapshot,
bool read_only, bool fire_triggers, long tcount)
{
saveActiveSnapshot = ActiveSnapshot;
PG_TRY();
{
- ListCell *lc1;
ErrorContextCallback spierrcontext;
- int nargs = plan->nargs;
- ParamListInfo paramLI;
CachedPlan *cplan = NULL;
-
- /* Convert parameters to form wanted by executor */
- if (nargs > 0)
- {
- int k;
-
- /* sizeof(ParamListInfoData) includes the first array element */
- paramLI = (ParamListInfo) palloc(sizeof(ParamListInfoData) +
- (nargs - 1) *sizeof(ParamExternData));
- paramLI->numParams = nargs;
-
- for (k = 0; k < nargs; k++)
- {
- ParamExternData *prm = ¶mLI->params[k];
-
- prm->value = Values[k];
- prm->isnull = (Nulls && Nulls[k] == 'n');
- prm->pflags = 0;
- prm->ptype = plan->argtypes[k];
- }
- }
- else
- paramLI = NULL;
+ ListCell *lc1;
/*
* Setup error traceback support for ereport()
return my_res;
}
+/*
+ * Convert query parameters to form wanted by planner and executor
+ */
+static ParamListInfo
+_SPI_convert_params(int nargs, Oid *argtypes,
+ Datum *Values, const char *Nulls,
+ int pflags)
+{
+ ParamListInfo paramLI;
+
+ if (nargs > 0)
+ {
+ int i;
+
+ /* sizeof(ParamListInfoData) includes the first array element */
+ paramLI = (ParamListInfo) palloc(sizeof(ParamListInfoData) +
+ (nargs - 1) *sizeof(ParamExternData));
+ paramLI->numParams = nargs;
+
+ for (i = 0; i < nargs; i++)
+ {
+ ParamExternData *prm = ¶mLI->params[i];
+
+ prm->value = Values[i];
+ prm->isnull = (Nulls && Nulls[i] == 'n');
+ prm->pflags = pflags;
+ prm->ptype = argtypes[i];
+ }
+ }
+ else
+ paramLI = NULL;
+ return paramLI;
+}
+
static int
_SPI_pquery(QueryDesc *queryDesc, bool fire_triggers, long tcount)
{
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/executor/spi.h,v 1.65 2008/01/01 19:45:57 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/executor/spi.h,v 1.66 2008/04/01 03:09:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
Snapshot snapshot,
Snapshot crosscheck_snapshot,
bool read_only, bool fire_triggers, long tcount);
+extern int SPI_execute_with_args(const char *src,
+ int nargs, Oid *argtypes,
+ Datum *Values, const char *Nulls,
+ bool read_only, long tcount);
extern SPIPlanPtr SPI_prepare(const char *src, int nargs, Oid *argtypes);
extern SPIPlanPtr SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes,
int cursorOptions);
extern Portal SPI_cursor_open(const char *name, SPIPlanPtr plan,
Datum *Values, const char *Nulls, bool read_only);
+extern Portal SPI_cursor_open_with_args(const char *name,
+ const char *src,
+ int nargs, Oid *argtypes,
+ Datum *Values, const char *Nulls,
+ bool read_only, int cursorOptions);
extern Portal SPI_cursor_find(const char *name);
extern void SPI_cursor_fetch(Portal portal, bool forward, long count);
extern void SPI_cursor_move(Portal portal, bool forward, long count);