patch: Review handling of MOVE and FETCH (ToDo)

Started by Pavel Stehuleover 16 years ago9 messages
#1Pavel Stehule
pavel.stehule@gmail.com
1 attachment(s)

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?

Attachments:

plpgsql-move.difftext/x-patch; charset=US-ASCII; name=plpgsql-move.diffDownload
*** ./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
#2Kevin Grittner
Kevin.Grittner@wicourts.gov
In reply to: Pavel Stehule (#1)
Re: patch: Review handling of MOVE and FETCH (ToDo)

Pavel Stehule <pavel.stehule@gmail.com> wrote:

this small patch complete MOVE support in plpgsql and equalize

plpgsql

syntax with sql syntax.

Quick correction on the doc changes:

s/similar as for/similar to/

-Kevin

#3Selena Deckelmann
selenamarie@gmail.com
In reply to: Pavel Stehule (#1)
2 attachment(s)
Re: patch: Review handling of MOVE and FETCH (ToDo)

Hi!

John Naylor and I reviewed this patch. John created two test cases to
demonstrated issues described later in this email. I've attached
those for reference.

On Thu, Aug 27, 2009 at 8:04 PM, Pavel Stehule <pavel.stehule@gmail.com> wrote:

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

== Submission review ==

* Is the patch in the standard form?

Yes, we have a contextual diff!

* Does it apply cleanly to the current CVS HEAD?

Yes!

* Does it include reasonable tests, docs, etc?

Suggestion: change variable 'returns_row' to 'returns_multiple_rows'
and invert the values of 'returns_row' in the patch.

Example:

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.")));

instead do:

if (fetch->returns_multiple_rows)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("statement FETCH returns more than one row."),
errhint("Multirow FETCH is not allowed in PL/pgSQL.")));

Does that make sense? In reading the code, we thought this change
made this variable much easier to understand, and the affected code
much easier to read.

===

Need a hard tab here to match the surrounding code: :)
+ %token K_ALL$

===

Can you clarify this comment?

+ /*
+  * 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.
+  */

We think what you mean is:
By default, cursor will only move one row. To MOVE more than one row
at a time see complete_direction()

We tested on Mac OS X and Linux (Ubuntu)
===

Also, the tests did not test what the standard SQL syntax would
require. John created a test case that demonstrated that the current
patch did not work according to the SQL spec.

We used that to find a little bug in complete_direction() (see below!).

== Usability review ==

Read what the patch is supposed to do, and consider:

* Does the patch actually implement that?

No -- we found a bug:

Line 162 of the patch:
+   expr = read_sql_expression2(K_FROM, K_IN,
 Should be:
+   fetch->expr = read_sql_expression2(K_FROM, K_IN,

And you don' t need to declare expr earlier in the function.

Once that's changed, the regression test needs to be updated for the
expected result set.

* Does it follow SQL spec, or the community-agreed behavior?

It conforms with the already implemented SQL syntax once the
'fetch->expr' thing is fixed.

* Have all the bases been covered?

We think so, as long the documentation is fixed and the above changes
are applied.

Another thing John noted is that additional documentation needs to be
updated for the SQL standard syntax, so that it no longer says that
PL/PgSQL doesn't implement the same functionality.

Thanks!
-selena & John

--
http://chesnok.com/daily - me
http://endpoint.com - work

Attachments:

MOVE_patch_test_case2application/octet-stream; name=MOVE_patch_test_case2Download
create or replace function sc_test() returns integer  --(not setof integer)
as $$
declare
  c cursor for select * from generate_series(1, 10);
  x integer;
begin
  open c;
      move forward in c;                  --now at '1'
      fetch relative 0 from c into x;     --fetch current position
      if found then
          return x;
      end if;
  close c;
end;
$$ language plpgsql;
CREATE FUNCTION
postgres=# select * from sc_test();
 sc_test 
---------
       1            --OK
(1 row)

****************************************************
create or replace function sc_test() returns integer
as $$
declare
  c cursor for select * from generate_series(1, 10);
  x integer;
begin
  open c;
      move forward 5 in c;                --should be at '5'
      fetch relative 0 from c into x;     --fetch current position
      if found then
          return x;
      end if;
  close c;
end;
$$ language plpgsql;
CREATE FUNCTION
postgres=# select * from sc_test();
 sc_test 
---------
       1       -- not 5!!!
(1 row)
*****************************************************SQL:
postgres=# begin;
BEGIN
postgres=# DECLARE c SCROLL CURSOR FOR select * FROM generate_series(1,10);
DECLARE CURSOR
postgres=# move forward 5 in c;
MOVE 5
postgres=# fetch relative 0 from c;
 generate_series 
-----------------
               5
(1 row)

MOVE_patch_test_caseapplication/octet-stream; name=MOVE_patch_test_caseDownload
****************From Pavel's regression 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();
 sc_test 
---------
       2
       4
       6
       8
      10
(5 rows)

***********This matches what I get by copying and pasting into a psql session:

postgres=# CREATE LANGUAGE plpgsql;
CREATE LANGUAGE
postgres=# create or replace function sc_test() returns setof integer as $$
postgres$# declare
postgres$#   c cursor for select * from generate_series(1, 10);
postgres$#   x integer;
postgres$# begin
postgres$#   open c;
postgres$#   loop
postgres$#       move forward 2 in c;
postgres$#       if not found then
postgres$#           exit;
postgres$#       end if;
postgres$#       fetch next from c into x;
postgres$#       if found then
postgres$#           return next x;
postgres$#       end if;
postgres$#   end loop;
postgres$#   close c;
postgres$# end;
postgres$# $$ language plpgsql;
CREATE FUNCTION
postgres=# select * from sc_test();
 sc_test 
---------
       2
       4
       6
       8
      10
(5 rows)

**********But that doesn't match what I get using SQL:

postgres=# begin;
BEGIN
postgres=# DECLARE c SCROLL CURSOR FOR select * FROM generate_series(1,10);
DECLARE CURSOR
postgres=# move forward 2 in c;
MOVE 2
postgres=# fetch next from c;
 generate_series 
-----------------
               3
(1 row)

postgres=# move forward 2 in c;
MOVE 2
postgres=# fetch next from c;
 generate_series 
-----------------
               6
(1 row)

postgres=# move forward 2 in c;
MOVE 2
postgres=# fetch next from c;
 generate_series 
-----------------
               9
(1 row)

postgres=# rollback;
ROLLBACK

*******************Same with the INT4_TBL test:

*******************First the table (from /src/test/regress/sql/int4.sql):

postgres=# CREATE TABLE INT4_TBL(f1 int4);
CREATE TABLE
postgres=# INSERT INTO INT4_TBL(f1) VALUES ('   0  ');
INSERT 0 1
postgres=# INSERT INTO INT4_TBL(f1) VALUES ('123456     ');
INSERT 0 1
postgres=# INSERT INTO INT4_TBL(f1) VALUES ('    -123456');
INSERT 0 1
postgres=# INSERT INTO INT4_TBL(f1) VALUES ('2147483647');
INSERT 0 1
postgres=# INSERT INTO INT4_TBL(f1) VALUES ('-2147483647');
INSERT 0 1
postgres=# select * from INT4_TBL;
     f1      
-------------
           0
      123456
     -123456
  2147483647
 -2147483647
(5 rows)

****************from Pavel's other regression test:

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)

***************** Doesn't match SQL either:

postgres=# begin;
BEGIN
postgres=# DECLARE c SCROLL CURSOR FOR select * FROM INT4_TBL;
DECLARE CURSOR
postgres=# fetch last from c;
     f1      
-------------
 -2147483647
(1 row)

postgres=# move backward 2 from c;
MOVE 2
postgres=# fetch relative -1 from c;
   f1   
--------
 123456
(1 row)

postgres=# move backward 2 from c
;
MOVE 1
postgres=# fetch relative -1 from c;
 f1 
----
(0 rows)

postgres=# rollback;
ROLLBACK


#4Pavel Stehule
pavel.stehule@gmail.com
In reply to: Selena Deckelmann (#3)
Re: patch: Review handling of MOVE and FETCH (ToDo)

ok, thank you, I'll look on these issues early

regards
Pavel

2009/9/18 Selena Deckelmann <selenamarie@gmail.com>:

Show quoted text

Hi!

John Naylor and I reviewed this patch. John created two test cases to
demonstrated issues described later in this email.  I've attached
those for reference.

On Thu, Aug 27, 2009 at 8:04 PM, Pavel Stehule <pavel.stehule@gmail.com> wrote:

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

== Submission review ==

* Is the patch in the standard form?

Yes, we have a contextual diff!

* Does it apply cleanly to the current CVS HEAD?

Yes!

* Does it include reasonable tests, docs, etc?

Suggestion: change variable 'returns_row' to 'returns_multiple_rows'
and invert the values of 'returns_row' in the patch.

Example:

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.")));

instead do:

if (fetch->returns_multiple_rows)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("statement FETCH returns more than one row."),
errhint("Multirow FETCH is not allowed in PL/pgSQL.")));

Does that make sense?  In reading the code, we thought this change
made this variable much easier to understand, and the affected code
much easier to read.

===

Need a hard tab here to match the surrounding code: :)
+ %token  K_ALL$

===

Can you clarify this comment?

+ /*
+  * 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.
+  */

We think what you mean is:
By default, cursor will only move one row. To MOVE more than one row
at a time see complete_direction()

We tested on Mac OS X and Linux (Ubuntu)
===

Also, the tests did not test what the standard SQL syntax would
require. John created a test case that demonstrated that the current
patch did not work according to the SQL spec.

We used that to find a little bug in complete_direction() (see below!).

== Usability review ==

Read what the patch is supposed to do, and consider:

* Does the patch actually implement that?

No -- we found a bug:

Line 162 of the patch:
+   expr = read_sql_expression2(K_FROM, K_IN,
 Should be:
+   fetch->expr = read_sql_expression2(K_FROM, K_IN,

And you don' t need to declare expr earlier in the function.

Once that's changed, the regression test needs to be updated for the
expected result set.

* Does it follow SQL spec, or the community-agreed behavior?

It conforms with the already implemented SQL syntax once the
'fetch->expr' thing is fixed.

* Have all the bases been covered?

We think so, as long the documentation is fixed and the above changes
are applied.

Another thing John noted is that additional documentation needs to be
updated for the SQL standard syntax, so that it no longer says that
PL/PgSQL doesn't implement the same functionality.

Thanks!
-selena & John

--
http://chesnok.com/daily - me
http://endpoint.com - work

#5Pavel Stehule
pavel.stehule@gmail.com
In reply to: Selena Deckelmann (#3)
1 attachment(s)
Re: patch: Review handling of MOVE and FETCH (ToDo)

Hello

2009/9/18 Selena Deckelmann <selenamarie@gmail.com>:

Hi!

John Naylor and I reviewed this patch. John created two test cases to
demonstrated issues described later in this email.  I've attached
those for reference.

On Thu, Aug 27, 2009 at 8:04 PM, Pavel Stehule <pavel.stehule@gmail.com> wrote:

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

== Submission review ==

* Is the patch in the standard form?

Yes, we have a contextual diff!

* Does it apply cleanly to the current CVS HEAD?

Yes!

* Does it include reasonable tests, docs, etc?

Suggestion: change variable 'returns_row' to 'returns_multiple_rows'
and invert the values of 'returns_row' in the patch.

good idea - changed

Example:

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.")));

instead do:

if (fetch->returns_multiple_rows)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("statement FETCH returns more than one row."),
errhint("Multirow FETCH is not allowed in PL/pgSQL.")));

Does that make sense?  In reading the code, we thought this change
made this variable much easier to understand, and the affected code
much easier to read.

===

Need a hard tab here to match the surrounding code: :)
+ %token  K_ALL$

fixed

===

Can you clarify this comment?

+ /*
+  * 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.
+  */

We think what you mean is:
By default, cursor will only move one row. To MOVE more than one row
at a time see complete_direction()

fixed - I add new comment there - I am sure, so this comments needs
some correction from native speakers - sorry, my English is bad still.

We tested on Mac OS X and Linux (Ubuntu)
===

Also, the tests did not test what the standard SQL syntax would
require. John created a test case that demonstrated that the current
patch did not work according to the SQL spec.

We used that to find a little bug in complete_direction() (see below!).

== Usability review ==

Read what the patch is supposed to do, and consider:

* Does the patch actually implement that?

No -- we found a bug:

Line 162 of the patch:
+   expr = read_sql_expression2(K_FROM, K_IN,
 Should be:
+   fetch->expr = read_sql_expression2(K_FROM, K_IN,

grr

fixed

And you don' t need to declare expr earlier in the function.

Once that's changed, the regression test needs to be updated for the
expected result set.

* Does it follow SQL spec, or the community-agreed behavior?

It conforms with the already implemented SQL syntax once the
'fetch->expr' thing is fixed.

* Have all the bases been covered?

We think so, as long the documentation is fixed and the above changes
are applied.

Another thing John noted is that additional documentation needs to be
updated for the SQL standard syntax, so that it no longer says that
PL/PgSQL doesn't implement the same functionality.

Thanks!
-selena & John

--
http://chesnok.com/daily - me
http://endpoint.com - work

Thank You
Pavel

Attachments:

plpgsql-move-fix090918.difftext/x-patch; charset=US-ASCII; name=plpgsql-move-fix090918.diffDownload
*** ./doc/src/sgml/plpgsql.sgml.orig	2009-08-27 17:14:26.926410144 +0200
--- ./doc/src/sgml/plpgsql.sgml	2009-09-18 22:44:45.267608075 +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 to <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
***************
*** 2678,2683 ****
--- 2678,2684 ----
  MOVE curs1;
  MOVE LAST FROM curs3;
  MOVE RELATIVE -2 FROM curs4;
+ MOVE FORWARD 2 FROM curs4;
  </programlisting>
         </para>
       </sect3>
*** ./src/pl/plpgsql/src/gram.y.orig	2009-08-26 22:43:23.138239357 +0200
--- ./src/pl/plpgsql/src/gram.y	2009-09-18 22:10:37.239609424 +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_multiple_rows)
+ 							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. By default, 
+  * cursor will only move one row. To MOVE more than one row
+  * at a time see complete_direction(). 
+  */
  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_multiple_rows = false;
  
  	/*
  	 * 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,2415 ----
  }
  
  
+ /*
+  * 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;
+ 
+ 	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_multiple_rows = true;
+ 		*check_FROM = true;
+ 		
+ 		return fetch;
+ 	}
+ 
+ 	plpgsql_push_back_token(tok);
+ 	fetch->expr = read_sql_expression2(K_FROM, K_IN,
+ 							   "FROM or IN",
+ 							   NULL);
+ 							   
+ 	/*
+ 	 * We don't allow multiple rows in PL/pgSQL's FETCH statement. This
+ 	 * limit is enforced by syntax. Only for sure one row directions
+ 	 * are allowed. Because result of expr should be greather than
+ 	 * one, we expect possible multiple rows and disable this direction
+ 	 * when current statement is FETCH statement.
+ 	 */   
+ 	fetch->returns_multiple_rows = true;
+ 	*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-09-18 21:17:28.312609825 +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_multiple_rows;		/* returns more than one 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-09-18 22:36:40.000000000 +0200
***************
*** 3027,3032 ****
--- 3027,3054 ----
  
  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
+ (2 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 ****
--- 3074,3149 ----
         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 
+ ---------
+        3
+        6
+        9
+ (3 rows)
+ 
+ drop function sc_test();
+ create or replace function sc_test() returns integer
+ as $$
+ declare
+   c cursor for select * from generate_series(1, 10);
+   x integer;
+ begin
+   open c;
+   move forward in c;                --should be at '5'
+   fetch relative 0 from c into x;     --fetch current position
+   if found then
+     return x;
+   end if;
+   close c;
+ end;
+ $$ language plpgsql;
+ select sc_test();
+  sc_test 
+ ---------
+        1
+ (1 row)
+ 
+ create or replace function sc_test() returns integer
+ as $$
+ declare
+   c cursor for select * from generate_series(1, 10);
+   x integer;
+ begin
+   open c;
+   move forward 5 in c;                --should be at '5'
+   fetch relative 0 from c into x;     --fetch current position
+   if found then
+     return x;
+   end if;
+   close c;
+ end;
+ $$ language plpgsql;
+ select sc_test();
+  sc_test 
+ ---------
+        5
+ (1 row)
+ 
  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-09-18 22:35:34.724609057 +0200
***************
*** 2513,2518 ****
--- 2513,2536 ----
  
  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,2540 ****
--- 2551,2619 ----
  
  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();
+ 
+ create or replace function sc_test() returns integer
+ as $$
+ declare
+   c cursor for select * from generate_series(1, 10);
+   x integer;
+ begin
+   open c;
+   move forward in c;                --should be at '5'
+   fetch relative 0 from c into x;     --fetch current position
+   if found then
+     return x;
+   end if;
+   close c;
+ end;
+ $$ language plpgsql;
+ 
+ select sc_test();
+ 
+ create or replace function sc_test() returns integer
+ as $$
+ declare
+   c cursor for select * from generate_series(1, 10);
+   x integer;
+ begin
+   open c;
+   move forward 5 in c;                --should be at '5'
+   fetch relative 0 from c into x;     --fetch current position
+   if found then
+     return x;
+   end if;
+   close c;
+ end;
+ $$ language plpgsql;
+ 
+ select sc_test();
+ 
  drop function sc_test();
  
+ 
  -- test qualified variable names
  
  create function pl_qual_names (param1 int) returns void as $$
#6Brendan Jurd
direvus@gmail.com
In reply to: Pavel Stehule (#5)
Re: patch: Review handling of MOVE and FETCH (ToDo)

2009/9/19 Pavel Stehule <pavel.stehule@gmail.com>:

2009/9/18 Selena Deckelmann <selenamarie@gmail.com>:

Hi!

John Naylor and I reviewed this patch. John created two test cases to
demonstrated issues described later in this email.  I've attached
those for reference.

On Thu, Aug 27, 2009 at 8:04 PM, Pavel Stehule <pavel.stehule@gmail.com> wrote:

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

Hi Selena and John,

Pavel's latest patch seems to address all the issues you raised in
your initial review. Do you have any comments on this new revision?
If you're happy that your issues have been resolved, please mark the
patch as Ready for Committer.

Cheers,
BJ

#7Pavel Stehule
pavel.stehule@gmail.com
In reply to: Pavel Stehule (#1)
1 attachment(s)
Re: patch: Review handling of MOVE and FETCH (ToDo)

2009/9/28 John Naylor <jcnaylor@gmail.com>:

Pavel,

It looks good. My last email didn't go to -hackers, since I wasn't
subscribed. I had to resend to -hackers so there will be a link for
the commitfest page. I think you might have to resend your latest
patch to the list. Sorry!

nothing, patch attached

Pavel

Show quoted text

In any case, I will say it's ready for commiter.

Thanks,
John

On Mon, Sep 28, 2009 at 2:07 AM, Pavel Stehule <pavel.stehule@gmail.com> wrote:

Hello

I am sending actualised patch as per John comment.

regards
Pavel Stehule

2009/9/26 John Naylor <jcnaylor@gmail.com>:

Hi,

Sorry, I didn't notice the attachment on Pavel's email, otherwise I
would have done this sooner! :)

I just applied and tested the new patch. Everything works great.

The only thing I would change now is some of the comments.

1). On line 289, one of the regression test comments got copied:

+   move forward in c;                --should be at '5'

change to:

+   move forward in c;                --should be at '1'

2). Lines 79/80:

+                                                                        errmsg("statement FETCH returns more rows."),
+                                                                        errhint("Multirows fetch are not allowed in PL/pgSQL.")));

This might sound better as "statement FETCH returns multiple rows.",
and "Multirow FETCH is not allowed in PL/pgSQL."

Everything else looks good to me.
John

Hi Selena and John,

Pavel's latest patch seems to address all the issues you raised in
your initial review.  Do you have any comments on this new revision?
If you're happy that your issues have been resolved, please mark the
patch as Ready for Committer.

Cheers,
BJ

Attachments:

plpgsql-move-fix090928.difftext/x-patch; charset=US-ASCII; name=plpgsql-move-fix090928.diffDownload
*** ./doc/src/sgml/plpgsql.sgml.orig	2009-09-28 09:38:55.711469112 +0200
--- ./doc/src/sgml/plpgsql.sgml	2009-09-28 09:39:24.613468923 +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 to <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
***************
*** 2678,2683 ****
--- 2678,2684 ----
  MOVE curs1;
  MOVE LAST FROM curs3;
  MOVE RELATIVE -2 FROM curs4;
+ MOVE FORWARD 2 FROM curs4;
  </programlisting>
         </para>
       </sect3>
*** ./src/pl/plpgsql/src/gram.y.orig	2009-09-28 09:38:55.713470217 +0200
--- ./src/pl/plpgsql/src/gram.y	2009-09-28 11:00:53.811467762 +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_multiple_rows)
+ 							ereport(ERROR,
+ 									(errcode(ERRCODE_SYNTAX_ERROR),
+ 									 errmsg("statement FETCH returns multiple rows"),
+ 									 errhint("Multirow FETCH is not allowed in PL/pgSQL.")));
  
  						fetch->lineno = $2;
  						fetch->rec		= rec;
***************
*** 2252,2257 ****
--- 2261,2271 ----
  }
  
  
+ /*
+  * Read FETCH or MOVE statement direction. By default, 
+  * cursor will only move one row. To MOVE more than one row
+  * at a time see complete_direction(). 
+  */
  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_multiple_rows = false;
  
  	/*
  	 * 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,2415 ----
  }
  
  
+ /*
+  * 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;
+ 
+ 	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_multiple_rows = true;
+ 		*check_FROM = true;
+ 		
+ 		return fetch;
+ 	}
+ 
+ 	plpgsql_push_back_token(tok);
+ 	fetch->expr = read_sql_expression2(K_FROM, K_IN,
+ 							   "FROM or IN",
+ 							   NULL);
+ 							   
+ 	/*
+ 	 * We don't allow multiple rows in PL/pgSQL's FETCH statement. This
+ 	 * limit is enforced by syntax. Only for sure one row directions
+ 	 * are allowed. Because result of expr should be greather than
+ 	 * one, we expect possible multiple rows and disable this direction
+ 	 * when current statement is FETCH statement.
+ 	 */   
+ 	fetch->returns_multiple_rows = true;
+ 	*check_FROM = false;
+ 	
+ 	return fetch;
+ }
+ 
  static PLpgSQL_stmt *
  make_return_stmt(int lineno)
  {
*** ./src/pl/plpgsql/src/plpgsql.h.orig	2009-09-28 09:38:55.715469366 +0200
--- ./src/pl/plpgsql/src/plpgsql.h	2009-09-28 09:39:24.653469785 +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_multiple_rows;		/* returns more than one rows? */
  } PLpgSQL_stmt_fetch;
  
  
*** ./src/test/regress/expected/plpgsql.out.orig	2009-09-28 09:38:55.719468503 +0200
--- ./src/test/regress/expected/plpgsql.out	2009-09-28 09:39:24.684468331 +0200
***************
*** 3027,3032 ****
--- 3027,3054 ----
  
  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
+ (2 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 ****
--- 3074,3149 ----
         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 
+ ---------
+        3
+        6
+        9
+ (3 rows)
+ 
+ drop function sc_test();
+ create or replace function sc_test() returns integer
+ as $$
+ declare
+   c cursor for select * from generate_series(1, 10);
+   x integer;
+ begin
+   open c;
+   move forward in c;                --should be at '5'
+   fetch relative 0 from c into x;     --fetch current position
+   if found then
+     return x;
+   end if;
+   close c;
+ end;
+ $$ language plpgsql;
+ select sc_test();
+  sc_test 
+ ---------
+        1
+ (1 row)
+ 
+ create or replace function sc_test() returns integer
+ as $$
+ declare
+   c cursor for select * from generate_series(1, 10);
+   x integer;
+ begin
+   open c;
+   move forward 5 in c;                --should be at '5'
+   fetch relative 0 from c into x;     --fetch current position
+   if found then
+     return x;
+   end if;
+   close c;
+ end;
+ $$ language plpgsql;
+ select sc_test();
+  sc_test 
+ ---------
+        5
+ (1 row)
+ 
  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-09-28 09:38:55.721469049 +0200
--- ./src/test/regress/sql/plpgsql.sql	2009-09-28 10:58:39.991468013 +0200
***************
*** 2513,2518 ****
--- 2513,2536 ----
  
  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,2540 ****
--- 2551,2619 ----
  
  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();
+ 
+ create or replace function sc_test() returns integer
+ as $$
+ declare
+   c cursor for select * from generate_series(1, 10);
+   x integer;
+ begin
+   open c;
+   move forward in c;                --should be at '1'
+   fetch relative 0 from c into x;     --fetch current position
+   if found then
+     return x;
+   end if;
+   close c;
+ end;
+ $$ language plpgsql;
+ 
+ select sc_test();
+ 
+ create or replace function sc_test() returns integer
+ as $$
+ declare
+   c cursor for select * from generate_series(1, 10);
+   x integer;
+ begin
+   open c;
+   move forward 5 in c;                --should be at '5'
+   fetch relative 0 from c into x;     --fetch current position
+   if found then
+     return x;
+   end if;
+   close c;
+ end;
+ $$ language plpgsql;
+ 
+ select sc_test();
+ 
  drop function sc_test();
  
+ 
  -- test qualified variable names
  
  create function pl_qual_names (param1 int) returns void as $$
#8Tom Lane
tgl@sss.pgh.pa.us
In reply to: Pavel Stehule (#7)
Re: patch: Review handling of MOVE and FETCH (ToDo)

Pavel Stehule <pavel.stehule@gmail.com> writes:

I am sending actualised patch as per John comment.

Applied with minor fixes (mostly around MOVE ALL).

regards, tom lane

#9Pavel Stehule
pavel.stehule@gmail.com
In reply to: Tom Lane (#8)
Re: patch: Review handling of MOVE and FETCH (ToDo)

2009/9/29 Tom Lane <tgl@sss.pgh.pa.us>:

Pavel Stehule <pavel.stehule@gmail.com> writes:

I am sending actualised patch as per John comment.

Applied with minor fixes (mostly around MOVE ALL).

thank you

Pavel

Show quoted text

                       regards, tom lane