-<!-- $PostgreSQL: pgsql/doc/src/sgml/plperl.sgml,v 2.55 2006/05/30 11:40:21 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/plperl.sgml,v 2.56 2006/08/12 20:05:54 tgl Exp $ -->
<chapter id="plperl">
<title>PL/Perl - Perl Procedural Language</title>
SELECT * FROM perl_set();
</programlisting>
-
</para>
- <para>
- <application>PL/Perl</> does not currently have full support for
- domain types: it treats a domain the same as the underlying scalar
- type. This means that constraints associated with the domain will
- not be enforced. This is not an issue for function arguments, but
- it is a hazard if you declare a <application>PL/Perl</> function
- as returning a domain type.
- </para>
-
<para>
If you wish to use the <literal>strict</> pragma with your code,
the easiest way to do so is to <command>SET</>
<para>
The advantage of prepared queries is that is it possible to use one prepared plan for more
- than one query execution. After the plan is not needed anymore, it must be freed with
+ than one query execution. After the plan is not needed anymore, it may be freed with
<literal>spi_freeplan</literal>:
</para>
<para>
<programlisting>
CREATE OR REPLACE FUNCTION init() RETURNS INTEGER AS $$
- $_SHARED{my_plan} = spi_prepare( 'SELECT (now() + $1)::date AS now', 'INTERVAL');
+ $_SHARED{my_plan} = spi_prepare( 'SELECT (now() + $1)::date AS now', 'INTERVAL');
$$ LANGUAGE plperl;
CREATE OR REPLACE FUNCTION add_time( INTERVAL ) RETURNS TEXT AS $$
- return spi_exec_prepared(
- $_SHARED{my_plan},
- $_[0],
- )->{rows}->[0]->{now};
+ return spi_exec_prepared(
+ $_SHARED{my_plan},
+ $_[0],
+ )->{rows}->[0]->{now};
$$ LANGUAGE plperl;
CREATE OR REPLACE FUNCTION done() RETURNS INTEGER AS $$
- spi_freeplan( $_SHARED{my_plan});
- undef $_SHARED{my_plan};
+ spi_freeplan( $_SHARED{my_plan});
+ undef $_SHARED{my_plan};
$$ LANGUAGE plperl;
SELECT init();
</para>
<para>
- <literal>spi_cursor_close</literal> can be used to abort sequence of
- <literal>spi_fetchrow</literal> calls. Normally, the call to
- <literal>spi_fetchrow</literal> that returns <literal>undef</literal> is
- the signal that there are no more rows to read. Also
- that call automatically frees the cursor associated with the query. If it is desired not
- to read all retuned rows, <literal>spi_cursor_close</literal> must be
- called to avoid memory leaks.
+ Normally, <function>spi_fetchrow</> should be repeated until it
+ returns <literal>undef</literal>, indicating that there are no more
+ rows to read. The cursor is automatically freed when
+ <function>spi_fetchrow</> returns <literal>undef</literal>.
+ If you do not wish to read all the rows, instead call
+ <function>spi_cursor_close</> to free the cursor.
+ Failure to do so will result in memory leaks.
</para>
-
-
</listitem>
</varlistentry>
return 1;
$$ LANGUAGE plperl;
</programlisting>
- The creation of this function will fail as its use of a forbidden
- operation will be be caught by the validator.
+ The creation of this function will fail as its use of a forbidden
+ operation will be be caught by the validator.
</para>
<para>
<listitem>
<para>
Name of the table on which the trigger fired. This has been deprecated,
- and could be removed in a future release.
- Please use $_TD->{table_name} instead.
+ and could be removed in a future release.
+ Please use $_TD->{table_name} instead.
</para>
</listitem>
</varlistentry>
-<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.97 2006/06/16 23:29:26 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.98 2006/08/12 20:05:54 tgl Exp $ -->
<chapter id="plpgsql">
<title><application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language</title>
The <replaceable>target</replaceable> is a record variable, row variable,
or comma-separated list of scalar variables.
The <replaceable>target</replaceable> is successively assigned each row
- resulting from the <replaceable>query</replaceable> (which must be a
- <command>SELECT</command> command) and the loop body is executed for each
- row. Here is an example:
+ resulting from the <replaceable>query</replaceable> and the loop body is
+ executed for each row. Here is an example:
<programlisting>
CREATE FUNCTION cs_refresh_mviews() RETURNS integer AS $$
DECLARE
</para>
<para>
+ The <replaceable>query</replaceable> used in this type of <literal>FOR</>
+ statement can be any query that returns rows to the caller:
+ <command>SELECT</> (without <literal>INTO</>) is the most common case,
+ but you can also use <command>INSERT</>, <command>UPDATE</>, or
+ <command>DELETE</> with a <literal>RETURNING</> clause. Some utility
+ commands such as <command>EXPLAIN</> will work too.
+ </para>
+
+ <para>
The <literal>FOR-IN-EXECUTE</> statement is another way to iterate over
rows:
<synopsis>
<replaceable>statements</replaceable>
END LOOP <optional> <replaceable>label</replaceable> </optional>;
</synopsis>
- This is like the previous form, except that the source
- <command>SELECT</command> statement is specified as a string
- expression, which is evaluated and replanned on each entry to
- the <literal>FOR</> loop. This allows the programmer to choose the speed of
- a preplanned query or the flexibility of a dynamic query, just
- as with a plain <command>EXECUTE</command> statement.
+ This is like the previous form, except that the source query
+ is specified as a string expression, which is evaluated and replanned
+ on each entry to the <literal>FOR</> loop. This allows the programmer to
+ choose the speed of a preplanned query or the flexibility of a dynamic
+ query, just as with a plain <command>EXECUTE</command> statement.
</para>
<note>
-<!-- $PostgreSQL: pgsql/doc/src/sgml/spi.sgml,v 1.45 2006/03/10 19:10:49 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/spi.sgml,v 1.46 2006/08/12 20:05:54 tgl Exp $ -->
<chapter id="spi">
<title>Server Programming Interface</title>
<term><symbol>SPI_ERROR_TRANSACTION</symbol></term>
<listitem>
<para>
- if any command involving transaction manipulation was attempted
- (<command>BEGIN</>,
- <command>COMMIT</>,
- <command>ROLLBACK</>,
- <command>SAVEPOINT</>,
- <command>PREPARE TRANSACTION</>,
- <command>COMMIT PREPARED</>,
- <command>ROLLBACK PREPARED</>,
- or any variant thereof)
+ if any command involving transaction manipulation was attempted
+ (<command>BEGIN</>,
+ <command>COMMIT</>,
+ <command>ROLLBACK</>,
+ <command>SAVEPOINT</>,
+ <command>PREPARE TRANSACTION</>,
+ <command>COMMIT PREPARED</>,
+ <command>ROLLBACK PREPARED</>,
+ or any variant thereof)
</para>
</listitem>
</varlistentry>
<para>
<function>SPI_is_cursor_plan</function> returns <symbol>true</symbol>
if a plan prepared by <function>SPI_prepare</function> can be passed
- as an argument to <function>SPI_cursor_open</function> and <symbol>
- false</symbol> if that is not the case. The criteria are that the
+ as an argument to <function>SPI_cursor_open</function>, or
+ <symbol>false</symbol> if that is not the case. The criteria are that the
<parameter>plan</parameter> represents one single command and that this
- command is a <command>SELECT</command> without an <command>INTO</command>
+ command returns tuples to the caller; for example, <command>SELECT</>
+ is allowed unless it contains an <literal>INTO</> clause, and
+ <command>UPDATE</> is allowed only if it contains a <literal>RETURNING</>
clause.
</para>
</refsect1>
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.49 2006/08/08 01:23:15 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.50 2006/08/12 20:05:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
if (query->into)
ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
+ (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
errmsg("DECLARE CURSOR may not specify INTO")));
if (query->rowMarks != NIL)
ereport(ERROR,
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.278 2006/08/12 02:52:04 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.279 2006/08/12 20:05:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* if there is a result relation, initialize result relation stuff
*/
- if (parseTree->resultRelation != 0 && operation != CMD_SELECT)
+ if (parseTree->resultRelation)
{
List *resultRelations = parseTree->resultRelations;
int numResultRelations;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.104 2006/07/14 14:52:19 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.105 2006/08/12 20:05:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* run it to completion. (If we run to completion then
* ExecutorRun is guaranteed to return NULL.)
*/
- if (LAST_POSTQUEL_COMMAND(es) && es->qd->operation == CMD_SELECT)
+ if (LAST_POSTQUEL_COMMAND(es) &&
+ es->qd->operation == CMD_SELECT &&
+ es->qd->parsetree->into == NULL)
count = 1L;
else
count = 0L;
JunkFilter **junkFilter)
{
Query *parse;
- int cmd;
+ bool isSelect;
List *tlist;
ListCell *tlistitem;
int tlistlen;
/* find the final query */
parse = (Query *) lfirst(list_tail(queryTreeList));
- cmd = parse->commandType;
- tlist = parse->targetList;
+ /*
+ * Note: eventually replace this with QueryReturnsTuples? We'd need
+ * a more general method of determining the output type, though.
+ */
+ isSelect = (parse->commandType == CMD_SELECT && parse->into == NULL);
/*
* The last query must be a SELECT if and only if return type isn't VOID.
*/
if (rettype == VOIDOID)
{
- if (cmd == CMD_SELECT)
+ if (isSelect)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("return type mismatch in function declared to return %s",
}
/* by here, the function is declared to return some type */
- if (cmd != CMD_SELECT)
+ if (!isSelect)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("return type mismatch in function declared to return %s",
/*
* Count the non-junk entries in the result targetlist.
*/
+ tlist = parse->targetList;
tlistlen = ExecCleanTargetListLength(tlist);
fn_typtype = get_typtype(rettype);
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.154 2006/08/12 02:52:04 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.155 2006/08/12 20:05:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
planTree = (Plan *) linitial(ptlist);
/* Must be a query that returns tuples */
- switch (queryTree->commandType)
- {
- case CMD_SELECT:
- if (queryTree->into != NULL)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
- errmsg("cannot open SELECT INTO query as cursor")));
- break;
- case CMD_UTILITY:
- if (!UtilityReturnsTuples(queryTree->utilityStmt))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
- errmsg("cannot open non-SELECT query as cursor")));
- break;
- default:
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
- errmsg("cannot open non-SELECT query as cursor")));
- break;
- }
+ if (!QueryReturnsTuples(queryTree))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
+ /* translator: %s is name of a SQL command, eg INSERT */
+ errmsg("cannot open %s query as cursor",
+ CreateQueryTag(queryTree))));
/* Reset SPI result (note we deliberately don't touch lastoid) */
SPI_processed = 0;
portal = CreatePortal(name, false, false);
}
- /* Switch to portals memory and copy the parsetree and plan to there */
+ /* Switch to portal's memory and copy the parsetree and plan to there */
oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
queryTree = copyObject(queryTree);
planTree = copyObject(planTree);
* Set up the portal.
*/
PortalDefineQuery(portal,
- NULL,
+ NULL, /* no statement name */
spiplan->query,
- "SELECT", /* don't have the raw parse tree... */
+ CreateQueryTag(queryTree),
list_make1(queryTree),
list_make1(planTree),
PortalGetHeapMemory(portal));
*/
PortalStart(portal, paramLI, snapshot);
- Assert(portal->strategy == PORTAL_ONE_SELECT ||
- portal->strategy == PORTAL_ONE_RETURNING ||
- portal->strategy == PORTAL_UTIL_SELECT);
+ /*
+ * If this test fails then we're out of sync with pquery.c about
+ * which queries can return tuples...
+ */
+ if (portal->strategy == PORTAL_MULTI_QUERY)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
+ /* translator: %s is name of a SQL command, eg INSERT */
+ errmsg("cannot open %s query as cursor",
+ CreateQueryTag(queryTree))));
/* Return the created portal */
return portal;
/*
* Returns true if the plan contains exactly one command
- * and that command originates from normal SELECT (i.e.
- * *not* a SELECT ... INTO). In essence, the result indicates
- * if the command can be used with SPI_cursor_open
+ * and that command returns tuples to the caller (eg, SELECT or
+ * INSERT ... RETURNING, but not SELECT ... INTO). In essence,
+ * the result indicates if the command can be used with SPI_cursor_open
*
* Parameters
- * plan A plan previously prepared using SPI_prepare
+ * plan: A plan previously prepared using SPI_prepare
*/
bool
SPI_is_cursor_plan(void *plan)
{
Query *queryTree = (Query *) linitial((List *) linitial(qtlist));
- if (queryTree->commandType == CMD_SELECT && queryTree->into == NULL)
+ if (QueryReturnsTuples(queryTree))
return true;
}
return false;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.41 2006/08/12 02:52:05 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.42 2006/08/12 20:05:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
if (!IsA(subquery, Query) ||
subquery->commandType != CMD_SELECT ||
- subquery->resultRelation != 0 ||
subquery->into != NULL)
elog(ERROR, "subquery is bogus");
/* Let's just make sure it's a valid subselect ... */
if (!IsA(subquery, Query) ||
subquery->commandType != CMD_SELECT ||
- subquery->resultRelation != 0 ||
subquery->into != NULL)
elog(ERROR, "subquery is bogus");
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.218 2006/08/12 02:52:05 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.219 2006/08/12 20:05:55 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
*/
if (!IsA(querytree, Query) ||
querytree->commandType != CMD_SELECT ||
- querytree->resultRelation != 0 ||
querytree->into ||
querytree->hasAggs ||
querytree->hasSubLinks ||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.345 2006/08/12 02:52:05 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.346 2006/08/12 20:05:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/* Shouldn't get any extras, since grammar only allows SelectStmt */
if (extras_before || extras_after)
elog(ERROR, "unexpected extra stuff in cursor statement");
+ if (!IsA(stmt->query, Query) ||
+ ((Query *) stmt->query)->commandType != CMD_SELECT)
+ elog(ERROR, "unexpected non-SELECT command in cursor statement");
+
+ /* But we must explicitly disallow DECLARE CURSOR ... SELECT INTO */
+ if (((Query *) stmt->query)->into)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
+ errmsg("DECLARE CURSOR may not specify INTO")));
return result;
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.155 2006/07/26 19:31:51 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.156 2006/08/12 20:05:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
if (query->commandType != CMD_SELECT)
elog(ERROR, "expected SELECT query from subquery in FROM");
- if (query->resultRelation != 0 || query->into != NULL)
+ if (query->into != NULL)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("subquery in FROM may not have SELECT INTO")));
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.196 2006/08/02 01:59:46 joe Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.197 2006/08/12 20:05:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
elog(ERROR, "bad query in sub-select");
qtree = (Query *) linitial(qtrees);
if (qtree->commandType != CMD_SELECT ||
- qtree->resultRelation != 0)
+ qtree->into != NULL)
elog(ERROR, "bad query in sub-select");
sublink->subselect = (Node *) qtree;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/rewrite/rewriteDefine.c,v 1.111 2006/07/18 17:42:00 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/rewrite/rewriteDefine.c,v 1.112 2006/08/12 20:05:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* ... the one action must be a SELECT, ...
*/
query = (Query *) linitial(action);
- if (!is_instead || query->commandType != CMD_SELECT)
+ if (!is_instead ||
+ query->commandType != CMD_SELECT || query->into != NULL)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("rules on SELECT must have action INSTEAD SELECT")));
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.264 2006/08/12 02:52:05 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.265 2006/08/12 20:05:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
+ * QueryReturnsTuples
+ * Return "true" if this Query will send output to the destination.
+ */
+bool
+QueryReturnsTuples(Query *parsetree)
+{
+ switch (parsetree->commandType)
+ {
+ case CMD_SELECT:
+ /* returns tuples ... unless it's SELECT INTO */
+ if (parsetree->into == NULL)
+ return true;
+ break;
+ case CMD_INSERT:
+ case CMD_UPDATE:
+ case CMD_DELETE:
+ /* the forms with RETURNING return tuples */
+ if (parsetree->returningList)
+ return true;
+ break;
+ case CMD_UTILITY:
+ return UtilityReturnsTuples(parsetree->utilityStmt);
+ case CMD_UNKNOWN:
+ case CMD_NOTHING:
+ /* probably shouldn't get here */
+ break;
+ }
+ return false; /* default */
+}
+
+
+/*
* CreateCommandTag
* utility to get a string representation of the
* command operation, given a raw (un-analyzed) parsetree.
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.322 2006/08/12 02:52:06 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.323 2006/08/12 20:05:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
Node *utilityStmt; /* non-null if this is a non-optimizable
* statement */
- int resultRelation; /* target relation (index into rtable) */
+ int resultRelation; /* rtable index of target relation for
+ * INSERT/UPDATE/DELETE; 0 for SELECT */
RangeVar *into; /* target relation for SELECT INTO */
List *intoOptions; /* options from WITH clause */
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/tcop/utility.h,v 1.27 2006/03/05 15:59:00 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/tcop/utility.h,v 1.28 2006/08/12 20:05:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern const char *CreateQueryTag(Query *parsetree);
+extern bool QueryReturnsTuples(Query *parsetree);
+
extern bool QueryIsReadOnly(Query *parsetree);
extern void CheckRelationOwnership(RangeVar *rel, bool noCatalogs);