Thread: Complex cursor won't rewind to 0-th row

Complex cursor won't rewind to 0-th row

From
Date:
Please CC me off-list, if at all possible.

I don't think I'm being stupid.

I even read the FAQ and TODO list as well as the link from "cursor" in the
TODO list about potential cursors outside transactions.

archive=3D> select version();
                          version
-----------------------------------------------------------
 PostgreSQL 7.3 on i686-pc-linux-gnu, compiled by GCC 2.96
(1 row)

BEGIN
declare foo cursor  for SELECT DISTINCT article.id, volume, number,
article.article, article.title , 0  + ( 0  + 1 * int4( (text ILIKE
'%destruction%') )  + 2 * int4(substring(text, 1, length(text)/10) ILIKE
'%destruction%')  + 1 * int4( (text ~* 'destruction.{0,20}Iraq') )  )  AS
points FROM article  , article_word, article_word_frequency   WHERE (TRUE=
=20
AND ( TRUE  AND  (text ILIKE '%destruction%')   AND  (text ~*
'destruction.{0,20}Iraq')   ) )  AND article_word.id =3D
article_word_frequency.word AND article_word_frequency.article =3D
article.id  AND (FALSE  OR article_word.word =3D 'iraq' )  ORDER BY  points
desc, volume, number, article.article  ;

DECLARE CURSOR
fetch 1 in foo;
  id   | volume | number | article |                    title=20=20=20=20=
=20=20=20=20=20=20=20=20=20
      | points
-------+--------+--------+---------+---------------------------------------=
------+--------
 10068 | 038    | 003    | 021     | International Agreements on Nuclear
Weapons |      4
(1 row)

fetch 9 in foo;

  id   | volume | number | article |=20=20=20=20=20=20=20=20=20=20=20=20=20=
=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20
                     title=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=
=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=
=20=20=20=20=20
              | points
-------+--------+--------+---------+---------------------------------------=
---------------------------------------------------------------------------=
------------+--------
 14422 | 048    | 001    | 018     | Iraq's Bomb:  Blueprints and
Artifacts=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=
=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=
=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20
                      |      4
 14555 | 048    | 005    | 013     | It's All over at Al Atheer=20=20=20=20=
=20=20=20=20=20=20=20
=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=
=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=
=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20
             |      4
 15636 | 051    | 006    | 033     | Has Iraq Come Clean at Last?=20=20=20=
=20=20=20=20=20=20
=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=
=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=
=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20
             |      4
 11320 | 041    | 001    | 009     | 1985 Outlook: Nuclear Proliferation:
The Pace Quickens=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=
=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=
=20=20=20=20=20=20=20=20=20=20=20=20
              |      2
 14426 | 048    | 001    | 022     | The Vision Thing:  David Shorr
reviews The Future Belongs to Freedom by Eduard Shevardnadze=20=20=20=20=20=
=20=20=20=20=20=20=20=20
                    |      2
 14559 | 048    | 005    | 017     | U.N. Dues:  The Price of Peace=20=20=
=20=20=20=20=20
=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=
=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=
=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20
             |      2
 15081 | 049    | 009    | 026     | Too Many Crooks?:  Michael Krepon
reviews Arms Control by Committee:  Managing Negotiations with the
Russians by George Bunn |      2
 15773 | 052    | 003    | 035     | Nuclear Excuses=20=20=20=20=20=20=20=
=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20
=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=
=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=
=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20
             |      2
(8 rows)

NOTE:  That would make 9 (not 8) rows total?

move backward 0 in foo;
MOVE 8

NOTE:  Arooo?  Shouldn't I have moved back 9 (not 8) rows?

move backward 1 in foo;
MOVE 0

I guess not...

fetch 1 in foo;
  id   | volume | number | article |                 title=20=20=20=20=20=
=20=20=20=20=20=20=20=20=20=20=20
 | points
-------+--------+--------+---------+---------------------------------------=
-+--------
 14422 | 048    | 001    | 018     | Iraq's Bomb:  Blueprints and
Artifacts |      4
(1 row)

Hey -- That wasn't my 0th row the first time around!

I simply CANNOT get back to the first article -- "International Agreements
on Nuclear Weapons" no matter what -- I can do all the "move" and "fetch"
I want, but after first going beyond the 0th row, PostgreSQL insists the
0th article is "Iraq's Bomb:  Blueprints and Artifacts" which just ain't
so.

This was repeatable with at least one, possibly two, other queries in this
data set.  (Though all involved 'Iraq' ...)

However, with a simpler data example, all goes well:

archive=3D> create sequence test_id;
CREATE SEQUENCE
archive=3D> create table test (test_id int4 default nextval('test_id'), test
text);
CREATE TABLE
archive=3D> insert into test(test) values( '1');
INSERT 25170548 1
archive=3D> insert into test(test) values( '2');
INSERT 25170549 1
archive=3D> insert into test(test) values( '3');
INSERT 25170550 1
archive=3D> insert into test(test) values( '4');
INSERT 25170551 1
archive=3D> insert into test(test) values( '5');
INSERT 25170552 1
archive=3D> insert into test(test) values( '6');
INSERT 25170553 1
archive=3D> insert into test(test) values( '7');
INSERT 25170554 1
archive=3D> insert into test(test) values( '8');
INSERT 25170555 1
archive=3D> insert into test(test) values( '9');
INSERT 25170556 1
archive=3D> insert into test(test) values( '10');
INSERT 25170557 1
archive=3D> begin;
BEGIN
archive=3D> declare foo cursor for select test_id, test from test where
test_id > 5 and test_id < 9 order by test_id;
DECLARE CURSOR
archive=3D> fetch 1 in foo;
 test_id | test
---------+------
       6 | 6
(1 row)

archive=3D> fetch 9 in foo;
 test_id | test
---------+------
       7 | 7
       8 | 8
(2 rows)

archive=3D> move backward 10 in foo;
MOVE 3
archive=3D> fetch 1 in foo;
 test_id | test
---------+------
       6 | 6
(1 row)

archive=3D> fetch 1 in foo;
 test_id | test
---------+------
       7 | 7
(1 row)

archive=3D> move backward 0 in foo;
MOVE 1
archive=3D> move backward 2 in foo;
MOVE 0
archive=3D> fetch 1 in foo;
 test_id | test
---------+------
       6 | 6
(1 row)

archive=3D> rollback;
ROLLBACK
archive=3D>

I can only guess that either something horribly funky in the data
(non-ASCII characters?) or the JOIN of the tables is at fault.

Schema and gzipped data (92 Meg) available at:
http://bulletinarchive.org/pg_dump/

Any Ideas?
Please CC me off-list, if at all possible.
I really need to be able to reliably get back to the 0th row...

Re: Complex cursor won't rewind to 0-th row

From
Tom Lane
Date:
<typea@l-i-e.com> writes:
> I simply CANNOT get back to the first article -- "International Agreements
> on Nuclear Weapons" no matter what -- I can do all the "move" and "fetch"
> I want, but after first going beyond the 0th row, PostgreSQL insists the
> 0th article is "Iraq's Bomb:  Blueprints and Artifacts" which just ain't
> so.

It didn't work that way for me, but I do see a bug here: reversing
direction after reaching either end of the query result misses the
last or first row, if the top plan node is a UNIQUE.

In general, a lot of complex plans do not work very well in the backward
direction.  UNIQUE seems easy to fix, however.  Attached is a patch for
7.3.

            regards, tom lane


*** src/backend/executor/nodeUnique.c.orig    Thu Jun 20 16:29:28 2002
--- src/backend/executor/nodeUnique.c    Sun Feb  2 14:02:57 2003
***************
*** 58,63 ****
--- 58,68 ----
      /*
       * now loop, returning only non-duplicate tuples. We assume that the
       * tuples arrive in sorted order so we can detect duplicates easily.
+      *
+      * We return the first tuple from each group of duplicates (or the
+      * last tuple of each group, when moving backwards).  At either end
+      * of the subplan, clear priorTuple so that we correctly return the
+      * first/last tuple when reversing direction.
       */
      for (;;)
      {
***************
*** 66,75 ****
           */
          slot = ExecProcNode(outerPlan, (Plan *) node);
          if (TupIsNull(slot))
              return NULL;

          /*
!          * Always return the first tuple from the subplan.
           */
          if (uniquestate->priorTuple == NULL)
              break;
--- 71,86 ----
           */
          slot = ExecProcNode(outerPlan, (Plan *) node);
          if (TupIsNull(slot))
+         {
+             /* end of subplan; reset in case we change direction */
+             if (uniquestate->priorTuple != NULL)
+                 heap_freetuple(uniquestate->priorTuple);
+             uniquestate->priorTuple = NULL;
              return NULL;
+         }

          /*
!          * Always return the first/last tuple from the subplan.
           */
          if (uniquestate->priorTuple == NULL)
              break;

Re: Complex cursor won't rewind to 0-th row

From
typea@l-i-e.com
Date:
So I've finally gotten around to applying this patch for cursors...

http://developer.postgresql.org/cvsweb.cgi/pgsql-server/src/backend/executor/nodeUnique.c
1.34.2.1

Now it's POSSIBLE that I've managed to screw up something fundamentally
simple in the patch/make/install process, so step by step...

I downloaded src/backend/executor/nodeUnique.c version 1.34.2.1 and put it
into the source tree.  The "tab" character and newlines got munged along
the way, but that was easy enough to fix even for an idiot like me :-)

I did make and various files got re-compiled including, but not limited
to, nodeUnique.o, postgres, pg_dump, psql, etc.

Pretty much what this naive user would expect to be re-compiled got
re-compiled...

I stopped Postgres, and used ps auxwww | grep post to be damn sure it was
stopped, then restarted it.

I've triple checked that the 'postgres' binary being called is the one
that just got installed, and not some old old version or something.  Ditto
for psql monitor used to test.

I still can't seem to get cursors to behave correctly:

archive=> begin;
BEGIN
archive=> declare foo cursor for select id, title from article where id = 1;
DECLARE CURSOR
archive=> fetch 1 in foo;
 id |               title
----+------------------------------------
  1 | The American and Russian Proposals
(1 row)

archive=> move backward 1 in foo;
MOVE 0
archive=> fetch 1 in foo;
 id | title
----+-------
(0 rows)

archive=>

After moving backward 1, I ought to be able to fetch the first (and only)
row again, and I can't.

In the event that my example query above is "too simple" for nodeUnique.c
to be relevant, here is the REAL query I'm running:

SELECT DISTINCT ON (points, volume, number, article.article) article.ID,
article.abstract, substring(article.text, 1, 250) AS text,
article.cover_date, article.volume, article.number, article.article,
article.title , 0 + 3 * int4( (title ILIKE '%albert einstein%') ) + (1 *
int4( title ILIKE '%albert%' )) + (3 * int4( (title ILIKE '%albert%' AND
title ILIKE '%einstein%' AND ((title ~* 'albert.{0,20}einstein') OR (title
~* 'einstein.{0,20}albert'))) )) + (1 * int4( title ILIKE '%einstein%' ))
+ (3 * int4( (title ILIKE '%einstein%' AND title ILIKE '%albert%' AND
((title ~* 'einstein.{0,20}albert') OR (title ~*
'albert.{0,20}einstein'))) )) + 2 * int4(substring(text, 1, 20) ILIKE
'%dead%') AS points, int4(length(artwork) > 0) as has_artwork FROM article
, article_word_frequency AS article_word_frequency_dead WHERE (TRUE AND (
title ILIKE '%albert%' ) AND ( title ILIKE '%einstein%' ) AND
article_word_frequency_dead.article = article.id AND
article_word_frequency_dead.word = 44667 ) ORDER BY points desc, volume,
number, article.article

int4() is a user-defined function that simply returns 1/0 for true/false

The above query returns exactly one row in our database.

Attempting a binary search on the cursor to find the last row lets me
determine how many rows there are very rapidly.  Alas, then I can't
actually GET my row to display it. :-(

Please CC me on replies, as I'm not really a hard-core reader here...

I'll have to revert once again to doing a select count(*) before the
cursor which makes the application about 1.5 times slower than it should
be...

Sorry to be such a PITA.

PostgreSQL Rocks!

Tom Lane wrote:
> <typea@l-i-e.com> writes:
>> I simply CANNOT get back to the first article -- "International
>> Agreements
>> on Nuclear Weapons" no matter what -- I can do all the "move" and
>> "fetch"
>> I want, but after first going beyond the 0th row, PostgreSQL insists the
>> 0th article is "Iraq's Bomb:  Blueprints and Artifacts" which just ain't
>> so.
>
> It didn't work that way for me, but I do see a bug here: reversing
> direction after reaching either end of the query result misses the
> last or first row, if the top plan node is a UNIQUE.
>
> In general, a lot of complex plans do not work very well in the backward
> direction.  UNIQUE seems easy to fix, however.  Attached is a patch for
> 7.3.
>
>             regards, tom lane
>
>
> *** src/backend/executor/nodeUnique.c.orig    Thu Jun 20 16:29:28 2002
> --- src/backend/executor/nodeUnique.c    Sun Feb  2 14:02:57 2003
> ***************
> *** 58,63 ****
> --- 58,68 ----
>       /*
>        * now loop, returning only non-duplicate tuples. We assume that the
>        * tuples arrive in sorted order so we can detect duplicates easily.
> +      *
> +      * We return the first tuple from each group of duplicates (or the
> +      * last tuple of each group, when moving backwards).  At either end
> +      * of the subplan, clear priorTuple so that we correctly return the
> +      * first/last tuple when reversing direction.
>        */
>       for (;;)
>       {
> ***************
> *** 66,75 ****
>            */
>           slot = ExecProcNode(outerPlan, (Plan *) node);
>           if (TupIsNull(slot))
>               return NULL;
>
>           /*
> !          * Always return the first tuple from the subplan.
>            */
>           if (uniquestate->priorTuple == NULL)
>               break;
> --- 71,86 ----
>            */
>           slot = ExecProcNode(outerPlan, (Plan *) node);
>           if (TupIsNull(slot))
> +         {
> +             /* end of subplan; reset in case we change direction */
> +             if (uniquestate->priorTuple != NULL)
> +                 heap_freetuple(uniquestate->priorTuple);
> +             uniquestate->priorTuple = NULL;
>               return NULL;
> +         }
>
>           /*
> !          * Always return the first/last tuple from the subplan.
>            */
>           if (uniquestate->priorTuple == NULL)
>               break;
>

Re: Complex cursor won't rewind to 0-th row

From
Tom Lane
Date:
typea@l-i-e.com writes:
> So I've finally gotten around to applying this patch for cursors...
> http://developer.postgresql.org/cvsweb.cgi/pgsql-server/src/backend/executor/nodeUnique.c
> 1.34.2.1

Why would you think that patch alone would solve all the issues with
running cursors backwards?  It certainly will not make the world safe
for backing up cursors on joins.

7.4 should fix this for you, otherwise not.

            regards, tom lane