Hello,

this small patch complete MOVE support in plpgsql and equalize plpgsql
syntax with sql syntax.

There are possible new directions:

FORWARD expr, FORWARD ALL, BACKWARD expr, BACKWARD all.

These directions are not allowed for FETCH statement, because returns more rows.

This patch is related to ToDo issue: Review handling of MOVE and FETCH

Regards
Pavel Stehule

p.s. Scrollable cursors are supported yet in plpgsql. Do you know,
somebody, why this point is in ToDo (plpgsql) still?
*** ./doc/src/sgml/plpgsql.sgml.orig	2009-08-27 17:14:26.926410144 +0200
--- ./doc/src/sgml/plpgsql.sgml	2009-08-27 17:32:47.928407934 +0200
***************
*** 2656,2670 ****
  
      <para>
       The options for the <replaceable>direction</replaceable> clause are
!      the same as for <command>FETCH</>, namely
       <literal>NEXT</>,
       <literal>PRIOR</>,
       <literal>FIRST</>,
       <literal>LAST</>,
       <literal>ABSOLUTE</> <replaceable>count</replaceable>,
       <literal>RELATIVE</> <replaceable>count</replaceable>,
!      <literal>FORWARD</>, or
!      <literal>BACKWARD</>.
       Omitting <replaceable>direction</replaceable> is the same
       as specifying <literal>NEXT</>.
       <replaceable>direction</replaceable> values that require moving
--- 2656,2670 ----
  
      <para>
       The options for the <replaceable>direction</replaceable> clause are
!      similar as for <command>FETCH</>, namely
       <literal>NEXT</>,
       <literal>PRIOR</>,
       <literal>FIRST</>,
       <literal>LAST</>,
       <literal>ABSOLUTE</> <replaceable>count</replaceable>,
       <literal>RELATIVE</> <replaceable>count</replaceable>,
!      <literal>FORWARD</> <optional> <replaceable>count</replaceable> | <literal>ALL</> </optional>, or
!      <literal>BACKWARD</> <optional> <replaceable>count</replaceable> | <literal>ALL</> </optional>.
       Omitting <replaceable>direction</replaceable> is the same
       as specifying <literal>NEXT</>.
       <replaceable>direction</replaceable> values that require moving
*** ./src/pl/plpgsql/src/gram.y.orig	2009-08-26 22:43:23.138239357 +0200
--- ./src/pl/plpgsql/src/gram.y	2009-08-27 08:18:13.418238086 +0200
***************
*** 72,77 ****
--- 72,79 ----
  										  int until, const char *expected);
  static List				*read_raise_options(void);
  
+ static PLpgSQL_stmt_fetch *complete_direction(PLpgSQL_stmt_fetch *fetch, bool *check_FROM);
+ 
  %}
  
  %expect 0
***************
*** 178,183 ****
--- 180,186 ----
  		 * Keyword tokens
  		 */
  %token	K_ALIAS
+ %token  K_ALL
  %token	K_ASSIGN
  %token	K_BEGIN
  %token	K_BY
***************
*** 1621,1626 ****
--- 1624,1635 ----
  
  						if (yylex() != ';')
  							yyerror("syntax error");
+ 							
+ 						if (!fetch->returns_row)
+ 							ereport(ERROR,
+ 									(errcode(ERRCODE_SYNTAX_ERROR),
+ 									 errmsg("statement FETCH returns more rows."),
+ 									 errhint("Multirows fetch are not allowed in PL/pgSQL.")));
  
  						fetch->lineno = $2;
  						fetch->rec		= rec;
***************
*** 2252,2257 ****
--- 2261,2271 ----
  }
  
  
+ /*
+  * Read FETCH or MOVE statement direction. For statement for are only 
+  * one row directions allowed. MOVE statement can use FORWARD [(n|ALL)],
+  * BACKWARD [(n|ALL)] directions too.
+  */
  static PLpgSQL_stmt_fetch *
  read_fetch_direction(void)
  {
***************
*** 2269,2274 ****
--- 2283,2289 ----
  	fetch->direction = FETCH_FORWARD;
  	fetch->how_many  = 1;
  	fetch->expr      = NULL;
+ 	fetch->returns_row = true;
  
  	/*
  	 * Most of the direction keywords are not plpgsql keywords, so we
***************
*** 2313,2323 ****
  	}
  	else if (pg_strcasecmp(yytext, "forward") == 0)
  	{
! 		/* use defaults */
  	}
  	else if (pg_strcasecmp(yytext, "backward") == 0)
  	{
  		fetch->direction = FETCH_BACKWARD;
  	}
  	else if (tok != T_SCALAR)
  	{
--- 2328,2339 ----
  	}
  	else if (pg_strcasecmp(yytext, "forward") == 0)
  	{
! 		fetch = complete_direction(fetch, &check_FROM);
  	}
  	else if (pg_strcasecmp(yytext, "backward") == 0)
  	{
  		fetch->direction = FETCH_BACKWARD;
+ 		fetch = complete_direction(fetch, &check_FROM);
  	}
  	else if (tok != T_SCALAR)
  	{
***************
*** 2346,2351 ****
--- 2362,2408 ----
  }
  
  
+ /*
+  * Allows directions:
+  *   FORWARD expr,  FORWARD ALL,  FORWARD
+  *   BACKWARD expr, BACKWARD ALL, BACKWARD
+  *
+  * so plpgsql should fully support PostgreSQL's MOVE statement.
+  */
+ static PLpgSQL_stmt_fetch *
+ complete_direction(PLpgSQL_stmt_fetch *fetch,  bool *check_FROM)
+ {
+ 	int	tok;
+ 	PLpgSQL_expr   *expr;
+ 
+ 	tok = yylex();
+ 	if (tok == K_FROM || tok == K_IN)
+ 	{
+ 		*check_FROM = false;
+ 		
+ 		return fetch;
+ 	}
+ 	
+ 	if (tok == K_ALL)
+ 	{
+ 		fetch->how_many = fetch->direction == FETCH_FORWARD ? -1 : 0;
+ 		fetch->direction = FETCH_ABSOLUTE;
+ 		fetch->returns_row = false;
+ 		*check_FROM = true;
+ 		
+ 		return fetch;
+ 	}
+ 
+ 	plpgsql_push_back_token(tok);
+ 	expr = read_sql_expression2(K_FROM, K_IN,
+ 							   "FROM or IN",
+ 							   NULL);
+ 	fetch->returns_row = false;
+ 	*check_FROM = false;
+ 	
+ 	return fetch;
+ }
+ 
  static PLpgSQL_stmt *
  make_return_stmt(int lineno)
  {
*** ./src/pl/plpgsql/src/plpgsql.h.orig	2009-08-27 07:46:45.051237969 +0200
--- ./src/pl/plpgsql/src/plpgsql.h	2009-08-27 07:58:57.816237398 +0200
***************
*** 520,525 ****
--- 520,526 ----
  	int			how_many;		/* count, if constant (expr is NULL) */
  	PLpgSQL_expr *expr;			/* count, if expression */
  	bool		is_move;		/* is this a fetch or move? */
+ 	bool		returns_row;		/* returns one or more rows? */
  } PLpgSQL_stmt_fetch;
  
  
*** ./src/pl/plpgsql/src/scan.l.orig	2009-08-27 07:55:10.058239657 +0200
--- ./src/pl/plpgsql/src/scan.l	2009-08-27 07:55:19.387238292 +0200
***************
*** 147,152 ****
--- 147,153 ----
  =				{ return K_ASSIGN;			}
  \.\.			{ return K_DOTDOT;			}
  alias			{ return K_ALIAS;			}
+ all			{ return K_ALL;				}
  begin			{ return K_BEGIN;			}
  by				{ return K_BY;   			}
  case			{ return K_CASE;			}
*** ./src/test/regress/expected/plpgsql.out.orig	2009-08-27 17:07:50.785412959 +0200
--- ./src/test/regress/expected/plpgsql.out	2009-08-27 17:06:17.000000000 +0200
***************
*** 3027,3032 ****
--- 3027,3055 ----
  
  create or replace function sc_test() returns setof integer as $$
  declare
+   c refcursor;
+   x integer;
+ begin
+   open c scroll for execute 'select f1 from int4_tbl';
+   fetch last from c into x;
+   while found loop
+     return next x;
+     move backward 2 from c;
+     fetch relative -1 from c into x;
+   end loop;
+   close c;
+ end;
+ $$ language plpgsql;
+ select * from sc_test();
+    sc_test   
+ -------------
+  -2147483647
+      -123456
+            0
+ (3 rows)
+ 
+ create or replace function sc_test() returns setof integer as $$
+ declare
    c cursor for select * from generate_series(1, 10);
    x integer;
  begin
***************
*** 3052,3057 ****
--- 3075,3109 ----
         9
  (3 rows)
  
+ create or replace function sc_test() returns setof integer as $$
+ declare
+   c cursor for select * from generate_series(1, 10);
+   x integer;
+ begin
+   open c;
+   loop
+       move forward 2 in c;
+       if not found then
+           exit;
+       end if;
+       fetch next from c into x;
+       if found then
+           return next x;
+       end if;
+   end loop;
+   close c;
+ end;
+ $$ language plpgsql;
+ select * from sc_test();
+  sc_test 
+ ---------
+        2
+        4
+        6
+        8
+       10
+ (5 rows)
+ 
  drop function sc_test();
  -- test qualified variable names
  create function pl_qual_names (param1 int) returns void as $$
*** ./src/test/regress/sql/plpgsql.sql.orig	2009-08-27 16:56:20.809413381 +0200
--- ./src/test/regress/sql/plpgsql.sql	2009-08-27 17:05:19.340410442 +0200
***************
*** 2513,2518 ****
--- 2513,2538 ----
  
  create or replace function sc_test() returns setof integer as $$
  declare
+   c refcursor;
+   x integer;
+ begin
+   open c scroll for execute 'select f1 from int4_tbl';
+   fetch last from c into x;
+   while found loop
+     return next x;
+     move backward 2 from c;
+     fetch relative -1 from c into x;
+   end loop;
+   close c;
+ end;
+ $$ language plpgsql;
+ 
+ select * from sc_test();
+ 
+ 
+ 
+ create or replace function sc_test() returns setof integer as $$
+ declare
    c cursor for select * from generate_series(1, 10);
    x integer;
  begin
***************
*** 2533,2538 ****
--- 2553,2581 ----
  
  select * from sc_test();
  
+ create or replace function sc_test() returns setof integer as $$
+ declare
+   c cursor for select * from generate_series(1, 10);
+   x integer;
+ begin
+   open c;
+   loop
+       move forward 2 in c;
+       if not found then
+           exit;
+       end if;
+       fetch next from c into x;
+       if found then
+           return next x;
+       end if;
+   end loop;
+   close c;
+ end;
+ $$ language plpgsql;
+ 
+ select * from sc_test();
+ 
+ 
  drop function sc_test();
  
  -- test qualified variable names
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to