Here's my very first proof-of-concept patch to PL/pgSQL to use the RETURNING syntax... INSERT/UPDATE seem to work fine but I think I've found an error with DELETE RETURNING though, so it doesn't work properly just yet.  Give this a test if you get a chance.

CREATE SEQUENCE test_id_seq START 1 INCREMENT 1;

CREATE TABLE test_tbl (
    test_id     BIGINT NOT NULL
                    DEFAULT nextval('test_id_seq'),
    test_name   VARCHAR(64) NOT NULL,
    PRIMARY KEY (test_id));

CREATE OR REPLACE FUNCTION test_func (test_nm VARCHAR)
    RETURNS VOID AS $$
DECLARE
    current_rec RECORD;
BEGIN
    -- Test INSERT RETURNING
    INSERT INTO test_tbl (test_name) VALUES (test_nm)
        RETURNING * INTO current_rec;

    RAISE NOTICE 'test_id is %', current_rec.test_id;
    RAISE NOTICE 'test_name is %', current_rec.test_name;

    -- Test UPDATE RETURNING
    UPDATE test_tbl SET test_name = 'Uncle Bob'
        WHERE test_id = current_rec.test_id
        RETURNING * INTO current_rec;

    RAISE NOTICE 'test_id is %', current_rec.test_id;
    RAISE NOTICE 'test_name is %', current_rec.test_name;

    -- Test DELETE RETURNING
    DELETE FROM test_tbl WHERE test_id = current_rec.test_id
        RETURNING * INTO current_rec;

    -- This DOES NOT WORK
    RAISE NOTICE 'test_id is %', current_rec.test_id;
    RAISE NOTICE 'test_name is %', current_rec.test_name;

    RETURN;
END;
$$ LANGUAGE plpgsql;


On 3/2/06, Jonah H. Harris <[EMAIL PROTECTED]> wrote:
All,

This is only the current patch updated to apply cleanly on cvs... it's not ready for -patches yet as I still haven't spent much time looking through it and testing it.  This is just for anyone to play with and find issues.

My focus for the next couple days is on getting INS/UPD/DEL RETURNING ready for 8.2... all comments, suggestions, issues would be appreciated.


--
Jonah H. Harris, Database Internals Architect
EnterpriseDB Corporation
732.331.1324




--
Jonah H. Harris, Database Internals Architect
EnterpriseDB Corporation
732.331.1324
diff -cr pgsql/src/pl/plpgsql/src/gram.y pgsql-iudret/src/pl/plpgsql/src/gram.y
*** pgsql/src/pl/plpgsql/src/gram.y	2006-02-12 01:37:05.000000000 -0500
--- pgsql-iudret/src/pl/plpgsql/src/gram.y	2006-03-02 17:08:47.000000000 -0500
***************
*** 50,55 ****
--- 50,56 ----
  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,
***************
*** 141,146 ****
--- 142,148 ----
  %type <stmt>	stmt_for stmt_select stmt_perform
  %type <stmt>	stmt_dynexecute stmt_getdiag
  %type <stmt>	stmt_open stmt_fetch stmt_close stmt_null
+ %type <stmt>	stmt_insert stmt_update stmt_delete
  
  %type <list>	proc_exceptions
  %type <exception_block> exception_sect
***************
*** 170,175 ****
--- 172,178 ----
  %token	K_DEBUG
  %token	K_DECLARE
  %token	K_DEFAULT
+ %token	K_DELETE
  %token	K_DIAGNOSTICS
  %token	K_DOTDOT
  %token	K_ELSE
***************
*** 185,190 ****
--- 188,194 ----
  %token	K_IF
  %token	K_IN
  %token	K_INFO
+ %token	K_INSERT
  %token	K_INTO
  %token	K_IS
  %token	K_LOG
***************
*** 201,212 ****
--- 205,218 ----
  %token	K_RENAME
  %token	K_RESULT_OID
  %token	K_RETURN
+ %token	K_RETURNING
  %token	K_RETURN_NEXT
  %token	K_REVERSE
  %token	K_SELECT
  %token	K_THEN
  %token	K_TO
  %token	K_TYPE
+ %token	K_UPDATE
  %token	K_WARNING
  %token	K_WHEN
  %token	K_WHILE
***************
*** 605,610 ****
--- 611,622 ----
  						{ $$ = $1; }
  				| stmt_select
  						{ $$ = $1; }
+ 				| stmt_insert
+ 						{ $$ = $1; }
+ 				| stmt_update
+ 						{ $$ = $1; }
+ 				| stmt_delete
+ 						{ $$ = $1; }
  				| stmt_exit
  						{ $$ = $1; }
  				| stmt_return
***************
*** 1119,1124 ****
--- 1131,1157 ----
  					}
  				;
  
+ stmt_insert		: K_INSERT lno
+ 					{
+ 						$$ = make_returning_stmt("INSERT");
+ 						$$->lineno = $2;
+ 					}
+ 				;
+ 
+ stmt_update		: K_UPDATE lno
+ 					{
+ 						$$ = make_returning_stmt("UPDATE");
+ 						$$->lineno = $2;
+ 					}
+ 				;
+ 
+ stmt_delete		: K_DELETE lno
+ 					{
+ 						$$ = make_returning_stmt("DELETE");
+ 						$$->lineno = $2;
+ 					}
+ 				;
+ 
  stmt_exit		: exit_type lno opt_label opt_exitcond
  					{
  						PLpgSQL_stmt_exit *new;
***************
*** 2077,2087 ****
--- 2110,2260 ----
  		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-iudret/src/pl/plpgsql/src/pl_exec.c
*** pgsql/src/pl/plpgsql/src/pl_exec.c	2006-03-02 00:34:12.000000000 -0500
--- pgsql-iudret/src/pl/plpgsql/src/pl_exec.c	2006-03-02 16:36:36.000000000 -0500
***************
*** 2266,2273 ****
  
  
  /* ----------
!  * exec_stmt_execsql			Execute an SQL statement not
!  *					returning any data.
   * ----------
   */
  static int
--- 2266,2273 ----
  
  
  /* ----------
!  * exec_stmt_execsql			Execute an SQL statement which
!  *					may return data.
   * ----------
   */
  static int
***************
*** 2279,2284 ****
--- 2279,2286 ----
  	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
***************
*** 2307,2322 ****
  	}
  
  	/*
  	 * 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:
--- 2309,2343 ----
  	}
  
  	/*
+ 	 * 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:
***************
*** 2340,2357 ****
  				 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;
  }
--- 2361,2375 ----
  				 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;
  }
diff -cr pgsql/src/pl/plpgsql/src/plpgsql.h pgsql-iudret/src/pl/plpgsql/src/plpgsql.h
*** pgsql/src/pl/plpgsql/src/plpgsql.h	2006-03-02 00:34:12.000000000 -0500
--- pgsql-iudret/src/pl/plpgsql/src/plpgsql.h	2006-03-02 15:11:56.000000000 -0500
***************
*** 528,533 ****
--- 528,535 ----
  {								/* 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;
  
diff -cr pgsql/src/pl/plpgsql/src/scan.l pgsql-iudret/src/pl/plpgsql/src/scan.l
*** pgsql/src/pl/plpgsql/src/scan.l	2006-02-27 11:09:50.000000000 -0500
--- pgsql-iudret/src/pl/plpgsql/src/scan.l	2006-03-02 16:24:38.000000000 -0500
***************
*** 144,149 ****
--- 144,150 ----
  debug			{ return K_DEBUG;			}
  declare			{ return K_DECLARE;			}
  default			{ return K_DEFAULT;			}
+ delete			{ return K_DELETE;			}
  diagnostics		{ return K_DIAGNOSTICS;		}
  else			{ return K_ELSE;			}
  elseif          { return K_ELSIF;           }
***************
*** 159,164 ****
--- 160,166 ----
  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;				}
***************
*** 174,185 ****
--- 176,189 ----
  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;			}
  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;			}

---------------------------(end of broadcast)---------------------------
TIP 4: Have you searched our list archives?

               http://archives.postgresql.org

Reply via email to