Only in pgsql-iuret: config.log
Only in pgsql-iuret: config.status
diff -cr pgsql/doc/src/sgml/ref/insert.sgml pgsql-iuret/doc/src/sgml/ref/insert.sgml
*** pgsql/doc/src/sgml/ref/insert.sgml 2005-11-17 17:14:51.000000000 -0500
--- pgsql-iuret/doc/src/sgml/ref/insert.sgml 2006-07-31 21:55:19.000000000 -0400
***************
*** 21,27 ****
INSERT INTO table [ ( column [, ...] ) ]
! { DEFAULT VALUES | VALUES ( { expression | DEFAULT } [, ...] ) | query }
--- 21,27 ----
INSERT INTO table [ ( column [, ...] ) ]
! { DEFAULT VALUES | VALUES ( { expression | DEFAULT } [, ...] ) | query } [ RETURNING ( * | column [, ...] | expression ) ]
***************
*** 56,61 ****
--- 56,69 ----
+ The RETURNING> clause allows you to return values and
+ expressions from the INSERT> statement. This eliminates
+ the requirement of executing a subsequent SELECT> statement
+ to retrieve values such as the next generated sequence number in a
+ DEFAULT> field.
+
+
+
You must have INSERT privilege to a table in
order to insert into it. If you use the query clause to insert rows from a
***************
*** 135,142 ****
Outputs
! On successful completion, an INSERT> command returns a command
! tag of the form
INSERT oid count
--- 143,150 ----
Outputs
! On successful completion, an INSERT> command without a RETURNING>
! clause will return a command tag of the form
INSERT oid count
***************
*** 147,152 ****
--- 155,167 ----
OID assigned to the inserted row. Otherwise
oid is zero.
+
+
+ If an INSERT> command contained a RETURNING>
+ clause and the command was successful, the result will be similar to that
+ of a SELECT statement containing the columns and values defined in the
+ RETURNING> list.
+
***************
*** 213,218 ****
--- 228,243 ----
VALUES (2, '{{X," "," "},{" ",O," "},{" ",X," "}}');
+
+
+ Insert a single row into table distributors returning
+ the sequence number generated by the DEFAULT clause:
+
+
+ INSERT INTO distributors VALUES
+ (DEFAULT, 'XYZ Widgets') RETURNING did;
+
+
diff -cr pgsql/doc/src/sgml/ref/update.sgml pgsql-iuret/doc/src/sgml/ref/update.sgml
*** pgsql/doc/src/sgml/ref/update.sgml 2006-03-08 17:59:09.000000000 -0500
--- pgsql-iuret/doc/src/sgml/ref/update.sgml 2006-07-31 22:04:59.000000000 -0400
***************
*** 24,29 ****
--- 24,30 ----
SET column = { expression | DEFAULT } [, ...]
[ FROM fromlist ]
[ WHERE condition ]
+ [ RETURNING ( * | column [, ...] | expression ) ]
***************
*** 53,58 ****
--- 54,67 ----
+ The RETURNING> clause allows you to return values and
+ expressions affected by the UPDATE command.
+ Use of the RETURNING clause eliminates the
+ need of performing SELECT FOR UPDATE in cases
+ where you need the after-update value such as balance calculation.
+
+
+
You must have the UPDATE privilege on the table
to update it, as well as the SELECT
privilege to any table whose values are read in the
***************
*** 213,218 ****
--- 222,238 ----
+ Perform the same operation and return the updated entries:
+
+
+ UPDATE weather SET temp_lo = temp_lo+1, temp_hi = temp_lo+15, prcp = DEFAULT
+ WHERE city = 'San Francisco' AND date = '2003-07-03'
+ RETURNING temp_lo, temp_hi, prcp;
+
+
+
+
+
Increment the sales count of the salesperson who manages the
account for Acme Corporation, using the FROM
clause syntax:
Only in pgsql-iuret: GNUmakefile
Only in pgsql-iuret: install
Only in pgsql-iuret: patch.log
diff -cr pgsql/src/backend/access/common/printtup.c pgsql-iuret/src/backend/access/common/printtup.c
*** pgsql/src/backend/access/common/printtup.c 2006-07-14 10:52:16.000000000 -0400
--- pgsql-iuret/src/backend/access/common/printtup.c 2006-07-31 14:25:49.000000000 -0400
***************
*** 18,23 ****
--- 18,24 ----
#include "access/printtup.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
+ #include "executor/executor.h"
#include "tcop/pquery.h"
#include "utils/lsyscache.h"
***************
*** 110,115 ****
--- 111,118 ----
{
DR_printtup *myState = (DR_printtup *) self;
Portal portal = myState->portal;
+ List *returningList = ((Query *) linitial(portal->parseTrees))->returningList;
+ bool withReturning = (returningList != NIL);
if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
{
***************
*** 134,140 ****
SendRowDescriptionMessage(typeinfo,
FetchPortalTargetList(portal),
portal->formats);
!
/* ----------------
* We could set up the derived attr info at this time, but we postpone it
* until the first call of printtup, for 2 reasons:
--- 137,147 ----
SendRowDescriptionMessage(typeinfo,
FetchPortalTargetList(portal),
portal->formats);
! else if (withReturning)
! SendRowDescriptionMessage(ExecTypeFromTL(returningList, false),
! returningList,
! portal->formats);
!
/* ----------------
* We could set up the derived attr info at this time, but we postpone it
* until the first call of printtup, for 2 reasons:
***************
*** 297,303 ****
/*
* send the attributes of this tuple
*/
! for (i = 0; i < natts; ++i)
{
PrinttupAttrInfo *thisState = myState->myinfo + i;
Datum origattr = slot->tts_values[i],
--- 304,310 ----
/*
* send the attributes of this tuple
*/
! for (i = 0; i < natts; i++)
{
PrinttupAttrInfo *thisState = myState->myinfo + i;
Datum origattr = slot->tts_values[i],
Only in pgsql-iuret/src/backend/bootstrap: bootparse.c
Only in pgsql-iuret/src/backend/bootstrap: bootscanner.c
Only in pgsql-iuret/src/backend/bootstrap: bootstrap_tokens.h
diff -cr pgsql/src/backend/executor/execMain.c pgsql-iuret/src/backend/executor/execMain.c
*** pgsql/src/backend/executor/execMain.c 2006-07-30 21:16:37.000000000 -0400
--- pgsql-iuret/src/backend/executor/execMain.c 2006-07-31 14:24:24.000000000 -0400
***************
*** 77,88 ****
static void ExecSelect(TupleTableSlot *slot,
DestReceiver *dest,
EState *estate);
! static void ExecInsert(TupleTableSlot *slot, ItemPointer tupleid,
! EState *estate);
static void ExecDelete(TupleTableSlot *slot, ItemPointer tupleid,
EState *estate);
! static void ExecUpdate(TupleTableSlot *slot, ItemPointer tupleid,
! EState *estate);
static TupleTableSlot *EvalPlanQualNext(EState *estate);
static void EndEvalPlanQual(EState *estate);
static void ExecCheckRTEPerms(RangeTblEntry *rte);
--- 77,88 ----
static void ExecSelect(TupleTableSlot *slot,
DestReceiver *dest,
EState *estate);
! static void ExecInsert(TupleTableSlot *slot, DestReceiver *dest,
! ItemPointer tupleid, EState *estate);
static void ExecDelete(TupleTableSlot *slot, ItemPointer tupleid,
EState *estate);
! static void ExecUpdate(TupleTableSlot *slot, DestReceiver *dest,
! ItemPointer tupleid, EState *estate);
static TupleTableSlot *EvalPlanQualNext(EState *estate);
static void EndEvalPlanQual(EState *estate);
static void ExecCheckRTEPerms(RangeTblEntry *rte);
***************
*** 151,156 ****
--- 151,159 ----
estate->es_snapshot = queryDesc->snapshot;
estate->es_crosscheck_snapshot = queryDesc->crosscheck_snapshot;
estate->es_instrument = queryDesc->doInstrument;
+ estate->es_returning =
+ ExecTransformReturning(queryDesc->parsetree->returningList,
+ estate);
/*
* Initialize the plan state tree
***************
*** 187,192 ****
--- 190,196 ----
DestReceiver *dest;
TupleTableSlot *result;
MemoryContext oldcontext;
+ TupleDesc tupDesc = NULL;
/* sanity checks */
Assert(queryDesc != NULL);
***************
*** 212,218 ****
estate->es_processed = 0;
estate->es_lastoid = InvalidOid;
! (*dest->rStartup) (dest, operation, queryDesc->tupDesc);
/*
* run plan
--- 216,232 ----
estate->es_processed = 0;
estate->es_lastoid = InvalidOid;
! if (estate->es_returning)
! tupDesc = estate->es_returning->retTupleDesc;
! else
! tupDesc = queryDesc->tupDesc;
!
! if(!tupDesc || !OidIsValid(tupDesc->tdtypeid))
! ereport(ERROR,
! (errcode(ERRCODE_UNDEFINED_TABLE),
! errmsg("relation does not exist")));
!
! (*dest->rStartup) (dest, operation, tupDesc);
/*
* run plan
***************
*** 1303,1309 ****
break;
case CMD_INSERT:
! ExecInsert(slot, tupleid, estate);
result = NULL;
break;
--- 1317,1323 ----
break;
case CMD_INSERT:
! ExecInsert(slot, dest, tupleid, estate);
result = NULL;
break;
***************
*** 1313,1319 ****
break;
case CMD_UPDATE:
! ExecUpdate(slot, tupleid, estate);
result = NULL;
break;
--- 1327,1333 ----
break;
case CMD_UPDATE:
! ExecUpdate(slot, dest, tupleid, estate);
result = NULL;
break;
***************
*** 1412,1417 ****
--- 1426,1432 ----
*/
static void
ExecInsert(TupleTableSlot *slot,
+ DestReceiver *dest,
ItemPointer tupleid,
EState *estate)
{
***************
*** 1477,1482 ****
--- 1492,1507 ----
estate->es_snapshot->curcid,
true, true);
+ if (estate->es_returning != NULL)
+ {
+ /*
+ * send the tuple to the destination
+ */
+ TupleTableSlot *retSlot = ExecReturning(slot, estate);
+ (*dest->receiveSlot) (retSlot, dest);
+ ExecClearTuple(retSlot);
+ }
+
IncrAppended();
(estate->es_processed)++;
estate->es_lastoid = newId;
***************
*** 1609,1614 ****
--- 1634,1640 ----
*/
static void
ExecUpdate(TupleTableSlot *slot,
+ DestReceiver *dest,
ItemPointer tupleid,
EState *estate)
{
***************
*** 1733,1738 ****
--- 1759,1774 ----
return;
}
+ if (estate->es_returning != NULL)
+ {
+ /*
+ * send the tuple to the destination
+ */
+ TupleTableSlot *retSlot = ExecReturning(slot, estate);
+ (*dest->receiveSlot) (retSlot, dest);
+ ExecClearTuple(retSlot);
+ }
+
IncrReplaced();
(estate->es_processed)++;
diff -cr pgsql/src/backend/executor/execUtils.c pgsql-iuret/src/backend/executor/execUtils.c
*** pgsql/src/backend/executor/execUtils.c 2006-07-14 10:52:19.000000000 -0400
--- pgsql-iuret/src/backend/executor/execUtils.c 2006-07-31 14:23:22.000000000 -0400
***************
*** 1148,1150 ****
--- 1148,1209 ----
MemoryContextSwitchTo(oldcontext);
}
+
+ TupleTableSlot *
+ ExecReturning(TupleTableSlot *slot,
+ EState *estate)
+ {
+ TupleTableSlot *retSlot,
+ *scanTupleSave;
+ ExprContext *returningExprContext;
+ ProjectionInfo *retProject;
+
+ returningExprContext = (ExprContext *) linitial(estate->es_exprcontexts);
+
+ scanTupleSave = returningExprContext->ecxt_scantuple;
+ returningExprContext->ecxt_scantuple = slot;
+
+ retProject = ExecBuildProjectionInfo(estate->es_returning->retExprs,
+ returningExprContext,
+ estate->es_returning->retSlot);
+
+ retSlot = ExecProject(retProject, NULL);
+ returningExprContext->ecxt_scantuple = scanTupleSave;
+ return retSlot;
+ }
+
+ ReturningState *
+ ExecTransformReturning(List *returningList,
+ EState *estate)
+ {
+ ReturningState *retState;
+ List *retExprs = NIL;
+ ListCell *retElem;
+ int i = 1;
+
+ if (returningList == NIL)
+ return NULL;
+
+ retState = palloc(1 * sizeof(ReturningState));
+
+ foreach (retElem, returningList)
+ {
+ TargetEntry *tle;
+ GenericExprState *gstate;
+
+ tle = (TargetEntry *) lfirst(retElem);
+ tle->resno = i++;
+ gstate = makeNode(GenericExprState);
+ gstate->xprstate.expr = (Expr *) tle;
+ gstate->xprstate.evalfunc = NULL;
+ gstate->arg = ExecPrepareExpr(tle->expr, estate);
+
+ retExprs = lappend(retExprs, gstate);
+ }
+
+ retState->retTupleDesc = ExecTypeFromTL(returningList, false);
+ retState->retExprs = retExprs;
+ retState->retSlot = MakeSingleTupleTableSlot(retState->retTupleDesc);
+
+ return retState;
+ }
diff -cr pgsql/src/backend/nodes/copyfuncs.c pgsql-iuret/src/backend/nodes/copyfuncs.c
*** pgsql/src/backend/nodes/copyfuncs.c 2006-07-27 15:52:05.000000000 -0400
--- pgsql-iuret/src/backend/nodes/copyfuncs.c 2006-07-31 14:18:14.000000000 -0400
***************
*** 1687,1692 ****
--- 1687,1693 ----
COPY_NODE_FIELD(rtable);
COPY_NODE_FIELD(jointree);
COPY_NODE_FIELD(targetList);
+ COPY_NODE_FIELD(returningList);
COPY_NODE_FIELD(groupClause);
COPY_NODE_FIELD(havingQual);
COPY_NODE_FIELD(distinctClause);
***************
*** 1709,1714 ****
--- 1710,1716 ----
COPY_NODE_FIELD(cols);
COPY_NODE_FIELD(targetList);
COPY_NODE_FIELD(selectStmt);
+ COPY_NODE_FIELD(returningList);
return newnode;
}
***************
*** 1734,1739 ****
--- 1736,1742 ----
COPY_NODE_FIELD(targetList);
COPY_NODE_FIELD(whereClause);
COPY_NODE_FIELD(fromClause);
+ COPY_NODE_FIELD(returningList);
return newnode;
}
diff -cr pgsql/src/backend/nodes/equalfuncs.c pgsql-iuret/src/backend/nodes/equalfuncs.c
*** pgsql/src/backend/nodes/equalfuncs.c 2006-07-27 15:52:05.000000000 -0400
--- pgsql-iuret/src/backend/nodes/equalfuncs.c 2006-07-31 14:18:36.000000000 -0400
***************
*** 664,669 ****
--- 664,670 ----
COMPARE_NODE_FIELD(rtable);
COMPARE_NODE_FIELD(jointree);
COMPARE_NODE_FIELD(targetList);
+ COMPARE_NODE_FIELD(returningList);
COMPARE_NODE_FIELD(groupClause);
COMPARE_NODE_FIELD(havingQual);
COMPARE_NODE_FIELD(distinctClause);
***************
*** 684,689 ****
--- 685,691 ----
COMPARE_NODE_FIELD(cols);
COMPARE_NODE_FIELD(targetList);
COMPARE_NODE_FIELD(selectStmt);
+ COMPARE_NODE_FIELD(returningList);
return true;
}
***************
*** 705,710 ****
--- 707,713 ----
COMPARE_NODE_FIELD(targetList);
COMPARE_NODE_FIELD(whereClause);
COMPARE_NODE_FIELD(fromClause);
+ COMPARE_NODE_FIELD(returningList);
return true;
}
diff -cr pgsql/src/backend/nodes/outfuncs.c pgsql-iuret/src/backend/nodes/outfuncs.c
*** pgsql/src/backend/nodes/outfuncs.c 2006-07-27 15:52:05.000000000 -0400
--- pgsql-iuret/src/backend/nodes/outfuncs.c 2006-07-31 14:18:44.000000000 -0400
***************
*** 1516,1521 ****
--- 1516,1522 ----
WRITE_NODE_FIELD(rtable);
WRITE_NODE_FIELD(jointree);
WRITE_NODE_FIELD(targetList);
+ WRITE_NODE_FIELD(returningList);
WRITE_NODE_FIELD(groupClause);
WRITE_NODE_FIELD(havingQual);
WRITE_NODE_FIELD(distinctClause);
diff -cr pgsql/src/backend/nodes/readfuncs.c pgsql-iuret/src/backend/nodes/readfuncs.c
*** pgsql/src/backend/nodes/readfuncs.c 2006-07-27 15:52:05.000000000 -0400
--- pgsql-iuret/src/backend/nodes/readfuncs.c 2006-07-31 14:18:52.000000000 -0400
***************
*** 148,153 ****
--- 148,154 ----
READ_NODE_FIELD(rtable);
READ_NODE_FIELD(jointree);
READ_NODE_FIELD(targetList);
+ READ_NODE_FIELD(returningList);
READ_NODE_FIELD(groupClause);
READ_NODE_FIELD(havingQual);
READ_NODE_FIELD(distinctClause);
diff -cr pgsql/src/backend/parser/analyze.c pgsql-iuret/src/backend/parser/analyze.c
*** pgsql/src/backend/parser/analyze.c 2006-07-14 10:52:21.000000000 -0400
--- pgsql-iuret/src/backend/parser/analyze.c 2006-07-31 14:22:24.000000000 -0400
***************
*** 98,103 ****
--- 98,105 ----
static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
List **extras_before, List **extras_after);
+ static List *transformReturningList(ParseState *pstate, RangeVar *relation,
+ List *returningList);
static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt);
static Query *transformRuleStmt(ParseState *query, RuleStmt *stmt,
List **extras_before, List **extras_after);
***************
*** 665,670 ****
--- 667,678 ----
}
/*
+ * Transform any RETURNING values to form a targetlist.
+ */
+ qry->returningList = transformReturningList(pstate, stmt->relation,
+ stmt->returningList);
+
+ /*
* Now we are done with SELECT-like processing, and can get on with
* transforming the target list to match the INSERT target columns.
*/
***************
*** 724,729 ****
--- 732,764 ----
return qry;
}
+ static List *
+ transformReturningList(ParseState *pstate, RangeVar *relation, List *returningList)
+ {
+ List *ret = NIL;
+ RangeTblEntry *retrte;
+
+ if (returningList != NIL)
+ {
+ /*
+ * Add the RTE to the pstate if we don't have any already.
+ * This will usually happen for INSERT.
+ */
+ if (pstate->p_varnamespace == NIL)
+ {
+ retrte = addRangeTableEntry(pstate, relation,
+ makeAlias("*RETURNING*", NIL),
+ false, false);
+ addRTEtoQuery(pstate, retrte, false, true, true);
+ }
+
+ ret = transformTargetList(pstate, returningList);
+ if (ret != NIL)
+ markTargetListOrigins(pstate, ret);
+ }
+ return ret;
+ }
+
/*
* transformCreateStmt -
* transforms the "create table" statement
***************
*** 2394,2399 ****
--- 2429,2440 ----
qry->targetList = transformTargetList(pstate, stmt->targetList);
+ /*
+ * Transform any RETURNING values to form a targetlist.
+ */
+ qry->returningList = transformReturningList(pstate, stmt->relation,
+ stmt->returningList);
+
qual = transformWhereClause(pstate, stmt->whereClause, "WHERE");
qry->rtable = pstate->p_rtable;
Only in pgsql-iuret/src/backend/parser: gram.c
diff -cr pgsql/src/backend/parser/gram.y pgsql-iuret/src/backend/parser/gram.y
*** pgsql/src/backend/parser/gram.y 2006-07-30 21:16:37.000000000 -0400
--- pgsql-iuret/src/backend/parser/gram.y 2006-07-31 14:20:37.000000000 -0400
***************
*** 278,283 ****
--- 278,284 ----
%type opt_column event cursor_options
%type reindex_type drop_type comment_type
+ %type opt_returning_list
%type fetch_direction select_limit_value select_offset_value
***************
*** 412,418 ****
QUOTE
READ REAL REASSIGN RECHECK REFERENCES REINDEX RELATIVE_P RELEASE RENAME
! REPEATABLE REPLACE RESET RESTART RESTRICT RETURNS REVOKE RIGHT
ROLE ROLLBACK ROW ROWS RULE
SAVEPOINT SCHEMA SCROLL SECOND_P SECURITY SELECT SEQUENCE
--- 413,419 ----
QUOTE
READ REAL REASSIGN RECHECK REFERENCES REINDEX RELATIVE_P RELEASE RENAME
! REPEATABLE REPLACE RESET RESTART RESTRICT RETURNING RETURNS REVOKE RIGHT
ROLE ROLLBACK ROW ROWS RULE
SAVEPOINT SCHEMA SCROLL SECOND_P SECURITY SELECT SEQUENCE
***************
*** 5334,5342 ****
*****************************************************************************/
InsertStmt:
! INSERT INTO qualified_name insert_rest
{
$4->relation = $3;
$$ = (Node *) $4;
}
;
--- 5335,5344 ----
*****************************************************************************/
InsertStmt:
! INSERT INTO qualified_name insert_rest opt_returning_list
{
$4->relation = $3;
+ $4->returningList = $5;
$$ = (Node *) $4;
}
;
***************
*** 5397,5402 ****
--- 5399,5408 ----
}
;
+ opt_returning_list:
+ RETURNING target_list { $$ = $2; }
+ | /* EMPTY */ { $$ = NIL; }
+ ;
/*****************************************************************************
*
***************
*** 5462,5473 ****
--- 5468,5481 ----
SET update_target_list
from_clause
where_clause
+ opt_returning_list
{
UpdateStmt *n = makeNode(UpdateStmt);
n->relation = $2;
n->targetList = $4;
n->fromClause = $5;
n->whereClause = $6;
+ n->returningList = $7;
$$ = (Node *)n;
}
;
***************
*** 8200,8206 ****
}
;
-
/*****************************************************************************
*
* Names and constants
--- 8208,8213 ----
***************
*** 8816,8821 ****
--- 8823,8829 ----
| PLACING
| PRIMARY
| REFERENCES
+ | RETURNING
| SELECT
| SESSION_USER
| SOME
diff -cr pgsql/src/backend/parser/keywords.c pgsql-iuret/src/backend/parser/keywords.c
*** pgsql/src/backend/parser/keywords.c 2006-07-30 21:16:37.000000000 -0400
--- pgsql-iuret/src/backend/parser/keywords.c 2006-07-31 13:01:08.000000000 -0400
***************
*** 284,289 ****
--- 284,290 ----
{"reset", RESET},
{"restart", RESTART},
{"restrict", RESTRICT},
+ {"returning", RETURNING},
{"returns", RETURNS},
{"revoke", REVOKE},
{"right", RIGHT},
Only in pgsql-iuret/src/backend/parser: parse.h
Only in pgsql-iuret/src/backend/parser: scan.c
Only in pgsql-iuret/src/backend/port: dynloader.c
Only in pgsql-iuret/src/backend/port: pg_sema.c
Only in pgsql-iuret/src/backend/port: pg_shmem.c
Only in pgsql-iuret/src/backend/port: tas.s
Only in pgsql-iuret/src/backend/utils/misc: guc-file.c
Only in pgsql-iuret/src/bin/psql: psqlscan.c
Only in pgsql-iuret/src/bin/psql: sql_help.h
Only in pgsql-iuret/src/include: dynloader.h
diff -cr pgsql/src/include/executor/executor.h pgsql-iuret/src/include/executor/executor.h
*** pgsql/src/include/executor/executor.h 2006-06-16 14:42:23.000000000 -0400
--- pgsql-iuret/src/include/executor/executor.h 2006-07-31 13:48:35.000000000 -0400
***************
*** 226,231 ****
--- 226,235 ----
extern ExprContext *CreateExprContext(EState *estate);
extern void FreeExprContext(ExprContext *econtext);
extern void ReScanExprContext(ExprContext *econtext);
+ extern TupleTableSlot *ExecReturning(TupleTableSlot *slot,
+ EState *estate);
+ extern ReturningState *ExecTransformReturning(List *returning,
+ EState *estate);
#define ResetExprContext(econtext) \
MemoryContextReset((econtext)->ecxt_per_tuple_memory)
diff -cr pgsql/src/include/nodes/execnodes.h pgsql-iuret/src/include/nodes/execnodes.h
*** pgsql/src/include/nodes/execnodes.h 2006-07-27 15:52:07.000000000 -0400
--- pgsql-iuret/src/include/nodes/execnodes.h 2006-07-31 14:16:56.000000000 -0400
***************
*** 279,284 ****
--- 279,291 ----
JunkFilter *ri_junkFilter;
} ResultRelInfo;
+ typedef struct ReturningState
+ {
+ TupleDesc retTupleDesc;
+ List *retExprs;
+ TupleTableSlot *retSlot;
+ } ReturningState;
+
/* ----------------
* EState information
*
***************
*** 322,327 ****
--- 329,335 ----
bool es_instrument; /* true requests runtime instrumentation */
bool es_select_into; /* true if doing SELECT INTO */
bool es_into_oids; /* true to generate OIDs in SELECT INTO */
+ ReturningState *es_returning; /* list of expressions to return */
List *es_exprcontexts; /* List of ExprContexts within EState */
diff -cr pgsql/src/include/nodes/parsenodes.h pgsql-iuret/src/include/nodes/parsenodes.h
*** pgsql/src/include/nodes/parsenodes.h 2006-07-30 21:16:38.000000000 -0400
--- pgsql-iuret/src/include/nodes/parsenodes.h 2006-07-31 14:17:41.000000000 -0400
***************
*** 104,109 ****
--- 104,111 ----
List *targetList; /* target list (of TargetEntry) */
+ List *returningList; /* the list of columns to return */
+
List *groupClause; /* a list of GroupClause's */
Node *havingQual; /* qualifications applied to groups */
***************
*** 645,650 ****
--- 647,653 ----
*/
List *targetList; /* the target list (of ResTarget) */
Node *selectStmt; /* the source SELECT */
+ List *returningList; /* the list of columns to return */
} InsertStmt;
/* ----------------------
***************
*** 670,675 ****
--- 673,679 ----
List *targetList; /* the target list (of ResTarget) */
Node *whereClause; /* qualifications */
List *fromClause; /* optional from clause for more tables */
+ List *returningList; /* the list of columns to return */
} UpdateStmt;
/* ----------------------
Only in pgsql-iuret/src/include: pg_config.h
Only in pgsql-iuret/src/include: pg_config_os.h
Only in pgsql-iuret/src/include: stamp-h
Only in pgsql-iuret/src/interfaces/ecpg/preproc: pgc.c
Only in pgsql-iuret/src/interfaces/ecpg/preproc: preproc.c
Only in pgsql-iuret/src/interfaces/ecpg/preproc: preproc.h
Only in pgsql-iuret/src/interfaces/libpq: blibpqdll.def
Only in pgsql-iuret/src/interfaces/libpq: libpqddll.def
Only in pgsql-iuret/src/interfaces/libpq: libpqdll.def
Only in pgsql-iuret/src/interfaces/libpq: libpq.rc
Only in pgsql-iuret/src: Makefile.global
Only in pgsql-iuret/src: Makefile.port
diff -cr pgsql/src/pl/plpgsql/src/gram.y pgsql-iuret/src/pl/plpgsql/src/gram.y
*** pgsql/src/pl/plpgsql/src/gram.y 2006-06-16 19:29:26.000000000 -0400
--- pgsql-iuret/src/pl/plpgsql/src/gram.y 2006-07-31 22:35:32.000000000 -0400
***************
*** 29,34 ****
--- 29,35 ----
static PLpgSQL_expr *read_sql_stmt(const char *sqlstart);
static PLpgSQL_type *read_datatype(int tok);
static PLpgSQL_stmt *make_select_stmt(void);
+ static PLpgSQL_stmt *make_returning_stmt(char *command);
static PLpgSQL_stmt *make_fetch_stmt(void);
static void check_assignable(PLpgSQL_datum *datum);
static PLpgSQL_row *read_into_scalar_list(const char *initial_name,
***************
*** 122,127 ****
--- 123,129 ----
%type stmt_for stmt_select stmt_perform
%type stmt_dynexecute stmt_getdiag
%type stmt_open stmt_fetch stmt_close stmt_null
+ %type stmt_insert stmt_update
%type proc_exceptions
%type exception_sect
***************
*** 167,172 ****
--- 169,175 ----
%token K_IF
%token K_IN
%token K_INFO
+ %token K_INSERT
%token K_INTO
%token K_IS
%token K_LOG
***************
*** 184,195 ****
--- 187,200 ----
%token K_RESULT_OID
%token K_RETURN
%token K_RETURN_NEXT
+ %token K_RETURNING
%token K_REVERSE
%token K_SELECT
%token K_STRICT
%token K_THEN
%token K_TO
%token K_TYPE
+ %token K_UPDATE
%token K_WARNING
%token K_WHEN
%token K_WHILE
***************
*** 592,597 ****
--- 597,606 ----
{ $$ = $1; }
| stmt_select
{ $$ = $1; }
+ | stmt_insert
+ { $$ = $1; }
+ | stmt_update
+ { $$ = $1; }
| stmt_exit
{ $$ = $1; }
| stmt_return
***************
*** 1135,1140 ****
--- 1144,1163 ----
}
;
+ stmt_insert : K_INSERT lno
+ {
+ $$ = make_returning_stmt("INSERT");
+ $$->lineno = $2;
+ }
+ ;
+
+ stmt_update : K_UPDATE lno
+ {
+ $$ = make_returning_stmt("UPDATE");
+ $$->lineno = $2;
+ }
+ ;
+
stmt_exit : exit_type lno opt_label opt_exitcond
{
PLpgSQL_stmt_exit *new;
***************
*** 1358,1363 ****
--- 1381,1388 ----
new->cmd_type = PLPGSQL_STMT_EXECSQL;
new->lineno = $2;
new->sqlstmt = read_sql_stmt($1);
+ new->rec = NULL;
+ new->row = NULL;
$$ = (PLpgSQL_stmt *)new;
}
***************
*** 2126,2136 ****
--- 2151,2301 ----
execsql = palloc(sizeof(PLpgSQL_stmt_execsql));
execsql->cmd_type = PLPGSQL_STMT_EXECSQL;
execsql->sqlstmt = expr;
+ execsql->rec = rec;
+ execsql->row = row;
return (PLpgSQL_stmt *)execsql;
}
}
+ static PLpgSQL_stmt *
+ make_returning_stmt(char *command)
+ {
+ PLpgSQL_dstring ds;
+ int nparams = 0;
+ int params[1024];
+ char buf[32];
+ PLpgSQL_expr *expr;
+ PLpgSQL_row *row = NULL;
+ PLpgSQL_rec *rec = NULL;
+ int tok;
+ bool have_returning = false;
+ bool have_into = false;
+ PLpgSQL_stmt_execsql *execsql;
+
+ plpgsql_dstring_init(&ds);
+ plpgsql_dstring_append(&ds, command);
+ plpgsql_dstring_append(&ds, " ");
+
+ while (1)
+ {
+ tok = yylex();
+
+ if (tok == ';')
+ break;
+ if (tok == 0)
+ {
+ plpgsql_error_lineno = plpgsql_scanner_lineno();
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("unexpected end of function definition")));
+ }
+ if (tok == K_RETURNING)
+ {
+ if (have_returning)
+ {
+ plpgsql_error_lineno = plpgsql_scanner_lineno();
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("RETURNING specified more than once")));
+ }
+ have_returning = true;
+ }
+ if (tok == K_INTO && have_returning)
+ {
+ if (have_into)
+ {
+ plpgsql_error_lineno = plpgsql_scanner_lineno();
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("RETURNING INTO specified more than once")));
+ }
+ tok = yylex();
+ switch (tok)
+ {
+ case T_ROW:
+ row = yylval.row;
+ check_assignable((PLpgSQL_datum *) row);
+ have_into = true;
+ break;
+
+ case T_RECORD:
+ rec = yylval.rec;
+ check_assignable((PLpgSQL_datum *) rec);
+ have_into = true;
+ break;
+
+ case T_SCALAR:
+ row = read_into_scalar_list(yytext, yylval.scalar);
+ have_into = true;
+ break;
+
+ default:
+ /* Treat the INTO as non-special */
+ plpgsql_dstring_append(&ds, " INTO ");
+ plpgsql_push_back_token(tok);
+ break;
+ }
+ continue;
+ }
+
+ if (plpgsql_SpaceScanned)
+ plpgsql_dstring_append(&ds, " ");
+
+ /* Check for array overflow */
+ if (nparams >= 1024)
+ {
+ plpgsql_error_lineno = plpgsql_scanner_lineno();
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("too many parameters specified in SQL statement")));
+ }
+
+ switch (tok)
+ {
+ case T_SCALAR:
+ params[nparams] = yylval.scalar->dno;
+ snprintf(buf, sizeof(buf), " $%d ", ++nparams);
+ plpgsql_dstring_append(&ds, buf);
+ break;
+
+ case T_ROW:
+ params[nparams] = yylval.row->rowno;
+ snprintf(buf, sizeof(buf), " $%d ", ++nparams);
+ plpgsql_dstring_append(&ds, buf);
+ break;
+
+ case T_RECORD:
+ params[nparams] = yylval.rec->recno;
+ snprintf(buf, sizeof(buf), " $%d ", ++nparams);
+ plpgsql_dstring_append(&ds, buf);
+ break;
+
+ default:
+ plpgsql_dstring_append(&ds, yytext);
+ break;
+ }
+ }
+
+ expr = palloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
+ expr->dtype = PLPGSQL_DTYPE_EXPR;
+ expr->query = pstrdup(plpgsql_dstring_get(&ds));
+ expr->plan = NULL;
+ expr->nparams = nparams;
+ while(nparams-- > 0)
+ expr->params[nparams] = params[nparams];
+ plpgsql_dstring_free(&ds);
+
+ check_sql_expr(expr->query);
+
+ execsql = palloc(sizeof(PLpgSQL_stmt_execsql));
+ execsql->cmd_type = PLPGSQL_STMT_EXECSQL;
+ execsql->sqlstmt = expr;
+ execsql->rec = rec;
+ execsql->row = row;
+
+ return (PLpgSQL_stmt *)execsql;
+ }
static PLpgSQL_stmt *
make_fetch_stmt(void)
diff -cr pgsql/src/pl/plpgsql/src/pl_exec.c pgsql-iuret/src/pl/plpgsql/src/pl_exec.c
*** pgsql/src/pl/plpgsql/src/pl_exec.c 2006-07-13 12:49:20.000000000 -0400
--- pgsql-iuret/src/pl/plpgsql/src/pl_exec.c 2006-07-31 13:01:08.000000000 -0400
***************
*** 2296,2303 ****
/* ----------
! * exec_stmt_execsql Execute an SQL statement not
! * returning any data.
* ----------
*/
static int
--- 2296,2303 ----
/* ----------
! * exec_stmt_execsql Execute an SQL statement which
! * may return data.
* ----------
*/
static int
***************
*** 2309,2314 ****
--- 2309,2316 ----
char *nulls;
int rc;
PLpgSQL_expr *expr = stmt->sqlstmt;
+ PLpgSQL_rec *rec = NULL;
+ PLpgSQL_row *row = NULL;
/*
* On the first call for this expression generate the plan
***************
*** 2337,2352 ****
}
/*
* Execute the plan
*/
rc = SPI_execute_plan(expr->plan, values, nulls,
estate->readonly_func, 0);
switch (rc)
{
case SPI_OK_UTILITY:
case SPI_OK_SELINTO:
break;
-
case SPI_OK_INSERT:
case SPI_OK_DELETE:
case SPI_OK_UPDATE:
--- 2339,2373 ----
}
/*
+ * If the user has selected the RETURNING option, we're going to
+ * determine how to return it.
+ */
+ if (stmt->rec != NULL)
+ rec = (PLpgSQL_rec *) (estate->datums[stmt->rec->recno]);
+ else if (stmt->row != NULL)
+ row = (PLpgSQL_row *) (estate->datums[stmt->row->rowno]);
+
+ /*
* Execute the plan
*/
rc = SPI_execute_plan(expr->plan, values, nulls,
estate->readonly_func, 0);
+
+ /* Assign to INTO variable */
+ if (rec || row)
+ {
+ if (SPI_processed == 0)
+ exec_move_row(estate, rec, row, NULL, SPI_tuptable->tupdesc);
+ else
+ exec_move_row(estate, rec, row,
+ SPI_tuptable->vals[0], SPI_tuptable->tupdesc);
+ }
+
switch (rc)
{
case SPI_OK_UTILITY:
case SPI_OK_SELINTO:
break;
case SPI_OK_INSERT:
case SPI_OK_DELETE:
case SPI_OK_UPDATE:
***************
*** 2370,2387 ****
expr->query, SPI_result_code_string(rc));
}
! /*
! * Release any result tuples from SPI_execute_plan (probably shouldn't be
! * any)
! */
SPI_freetuptable(SPI_tuptable);
/* Save result info for GET DIAGNOSTICS */
estate->eval_processed = SPI_processed;
estate->eval_lastoid = SPI_lastoid;
! pfree(values);
! pfree(nulls);
return PLPGSQL_RC_OK;
}
--- 2391,2405 ----
expr->query, SPI_result_code_string(rc));
}
! /* Release any result tuples from SPI_execute_plan */
SPI_freetuptable(SPI_tuptable);
/* Save result info for GET DIAGNOSTICS */
estate->eval_processed = SPI_processed;
estate->eval_lastoid = SPI_lastoid;
! pfree(values);
! pfree(nulls);
return PLPGSQL_RC_OK;
}
Only in pgsql-iuret/src/pl/plpgsql/src: pl_gram.c
diff -cr pgsql/src/pl/plpgsql/src/plpgsql.h pgsql-iuret/src/pl/plpgsql/src/plpgsql.h
*** pgsql/src/pl/plpgsql/src/plpgsql.h 2006-07-11 13:26:59.000000000 -0400
--- pgsql-iuret/src/pl/plpgsql/src/plpgsql.h 2006-07-31 13:01:08.000000000 -0400
***************
*** 509,514 ****
--- 509,516 ----
{ /* Generic SQL statement to execute */
int cmd_type;
int lineno;
+ PLpgSQL_rec *rec; /* INTO record or row variable */
+ PLpgSQL_row *row;
PLpgSQL_expr *sqlstmt;
} PLpgSQL_stmt_execsql;
Only in pgsql-iuret/src/pl/plpgsql/src: pl_scan.c
Only in pgsql-iuret/src/pl/plpgsql/src: pl.tab.h
diff -cr pgsql/src/pl/plpgsql/src/scan.l pgsql-iuret/src/pl/plpgsql/src/scan.l
*** pgsql/src/pl/plpgsql/src/scan.l 2006-06-16 19:29:27.000000000 -0400
--- pgsql-iuret/src/pl/plpgsql/src/scan.l 2006-07-31 14:41:24.000000000 -0400
***************
*** 139,144 ****
--- 139,145 ----
if { return K_IF; }
in { return K_IN; }
info { return K_INFO; }
+ insert { return K_INSERT; }
into { return K_INTO; }
is { return K_IS; }
log { return K_LOG; }
***************
*** 154,159 ****
--- 155,161 ----
rename { return K_RENAME; }
result_oid { return K_RESULT_OID; }
return { return K_RETURN; }
+ returning { return K_RETURNING; }
reverse { return K_REVERSE; }
row_count { return K_ROW_COUNT; }
select { return K_SELECT; }
***************
*** 161,166 ****
--- 163,169 ----
then { return K_THEN; }
to { return K_TO; }
type { return K_TYPE; }
+ update { return K_UPDATE; }
warning { return K_WARNING; }
when { return K_WHEN; }
while { return K_WHILE; }
diff -cr pgsql/src/test/regress/expected/insert.out pgsql-iuret/src/test/regress/expected/insert.out
*** pgsql/src/test/regress/expected/insert.out 2003-09-25 02:58:06.000000000 -0400
--- pgsql-iuret/src/test/regress/expected/insert.out 2006-07-31 13:01:08.000000000 -0400
***************
*** 8,13 ****
--- 8,19 ----
insert into inserttest (col1, col2, col3) values (DEFAULT, 5, DEFAULT);
insert into inserttest values (DEFAULT, 5, 'test');
insert into inserttest values (DEFAULT, 7);
+ insert into inserttest (col2, col3) values (3, DEFAULT) returning col3, col1, col2, col2 * 5, least(col2, col2 * 5);
+ col3 | col1 | col2 | ?column? | least
+ ---------+------+------+----------+-------
+ testing | | 3 | 15 | 3
+ (1 row)
+
select * from inserttest;
col1 | col2 | col3
------+------+---------
***************
*** 15,21 ****
| 5 | testing
| 5 | test
| 7 | testing
! (4 rows)
--
-- insert with similar expression / target_list values (all fail)
--- 21,28 ----
| 5 | testing
| 5 | test
| 7 | testing
! | 3 | testing
! (5 rows)
--
-- insert with similar expression / target_list values (all fail)
***************
*** 35,40 ****
| 5 | testing
| 5 | test
| 7 | testing
! (4 rows)
drop table inserttest;
--- 42,48 ----
| 5 | testing
| 5 | test
| 7 | testing
! | 3 | testing
! (5 rows)
drop table inserttest;
diff -cr pgsql/src/test/regress/expected/update.out pgsql-iuret/src/test/regress/expected/update.out
*** pgsql/src/test/regress/expected/update.out 2006-03-14 17:48:25.000000000 -0500
--- pgsql-iuret/src/test/regress/expected/update.out 2006-07-31 22:10:23.000000000 -0400
***************
*** 49,52 ****
--- 49,67 ----
^
HINT: Perhaps you meant to reference the table alias "t".
ROLLBACK;
+ -- Test UPDATE RETURNING
+ UPDATE update_test SET a = 5, b = 10 RETURNING b, a, a * 2 + b, greatest(a, b);
+ b | a | ?column? | greatest
+ ----+---+----------+----------
+ 10 | 5 | 20 | 10
+ 10 | 5 | 20 | 10
+ (2 rows)
+
+ SELECT * FROM update_test;
+ a | b
+ ---+----
+ 5 | 10
+ 5 | 10
+ (2 rows)
+
DROP TABLE update_test;
diff -cr pgsql/src/test/regress/sql/insert.sql pgsql-iuret/src/test/regress/sql/insert.sql
*** pgsql/src/test/regress/sql/insert.sql 2002-04-23 22:22:54.000000000 -0400
--- pgsql-iuret/src/test/regress/sql/insert.sql 2006-07-31 13:01:08.000000000 -0400
***************
*** 7,12 ****
--- 7,13 ----
insert into inserttest (col1, col2, col3) values (DEFAULT, 5, DEFAULT);
insert into inserttest values (DEFAULT, 5, 'test');
insert into inserttest values (DEFAULT, 7);
+ insert into inserttest (col2, col3) values (3, DEFAULT) returning col3, col1, col2, col2 * 5, least(col2, col2 * 5);
select * from inserttest;
diff -cr pgsql/src/test/regress/sql/update.sql pgsql-iuret/src/test/regress/sql/update.sql
*** pgsql/src/test/regress/sql/update.sql 2006-01-22 00:20:35.000000000 -0500
--- pgsql-iuret/src/test/regress/sql/update.sql 2006-07-31 13:01:08.000000000 -0400
***************
*** 32,35 ****
--- 32,40 ----
UPDATE update_test AS t SET b = update_test.b + 10 WHERE t.a = 10;
ROLLBACK;
+ -- Test UPDATE RETURNING
+ UPDATE update_test SET a = 5, b = 10 RETURNING b, a, a * 2 + b, greatest(a, b);
+
+ SELECT * FROM update_test;
+
DROP TABLE update_test;