Skip to content

Commit e4fb8ff

Browse files
committed
Add a new column to pg_am to specify whether an index AM supports backward
scanning; GiST and GIN do not, and it seems like too much trouble to make them do so. By teaching ExecSupportsBackwardScan() about this restriction, we ensure that the planner will protect a scroll cursor from the problem by adding a Materialize node. In passing, fix another longstanding bug in the same area: backwards scan of a plan with set-returning functions in the targetlist did not work either, since the TupFromTlist expansion code pays no attention to direction (and has no way to run a SRF backwards anyway). Again the fix is to make ExecSupportsBackwardScan check this restriction. Also adjust the index AM API specification to note that mark/restore support is unnecessary if the AM can't produce ordered output.
1 parent 2a64931 commit e4fb8ff

File tree

5 files changed

+119
-42
lines changed

5 files changed

+119
-42
lines changed

doc/src/sgml/catalogs.sgml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.178 2008/10/06 13:59:37 tgl Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.179 2008/10/17 22:10:29 tgl Exp $ -->
22
<!--
33
Documentation of the system catalogs, directed toward PostgreSQL developers
44
-->
@@ -401,6 +401,13 @@
401401
<entry>Does the access method support ordered scans?</entry>
402402
</row>
403403

404+
<row>
405+
<entry><structfield>amcanbackward</structfield></entry>
406+
<entry><type>bool</type></entry>
407+
<entry></entry>
408+
<entry>Does the access method support backward scanning?</entry>
409+
</row>
410+
404411
<row>
405412
<entry><structfield>amcanunique</structfield></entry>
406413
<entry><type>bool</type></entry>

doc/src/sgml/indexam.sgml

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.27 2008/08/14 18:47:58 tgl Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.28 2008/10/17 22:10:29 tgl Exp $ -->
22

33
<chapter id="indexam">
44
<title>Index Access Method Interface Definition</title>
@@ -474,15 +474,20 @@ amrestrpos (IndexScanDesc scan);
474474
normally would. (This will only occur for access
475475
methods that advertise they support ordered scans.) After the
476476
first call, <function>amgettuple</> must be prepared to advance the scan in
477-
either direction from the most recently returned entry.
477+
either direction from the most recently returned entry. (But if
478+
<structname>pg_am</>.<structfield>amcanbackward</> is false, all subsequent
479+
calls will have the same direction as the first one.)
478480
</para>
479481

480482
<para>
481-
The access method must support <quote>marking</> a position in a scan
482-
and later returning to the marked position. The same position might be
483-
restored multiple times. However, only one position need be remembered
484-
per scan; a new <function>ammarkpos</> call overrides the previously
485-
marked position.
483+
Access methods that support ordered scans must support <quote>marking</> a
484+
position in a scan and later returning to the marked position. The same
485+
position might be restored multiple times. However, only one position need
486+
be remembered per scan; a new <function>ammarkpos</> call overrides the
487+
previously marked position. An access method that does not support
488+
ordered scans should still provide mark and restore functions in
489+
<structname>pg_am</>, but it is sufficient to have them throw errors if
490+
called.
486491
</para>
487492

488493
<para>

src/backend/executor/execAmi.c

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
77
* Portions Copyright (c) 1994, Regents of the University of California
88
*
9-
* $PostgreSQL: pgsql/src/backend/executor/execAmi.c,v 1.99 2008/10/04 21:56:53 tgl Exp $
9+
* $PostgreSQL: pgsql/src/backend/executor/execAmi.c,v 1.100 2008/10/17 22:10:29 tgl Exp $
1010
*
1111
*-------------------------------------------------------------------------
1212
*/
@@ -42,6 +42,12 @@
4242
#include "executor/nodeValuesscan.h"
4343
#include "executor/nodeCtescan.h"
4444
#include "executor/nodeWorktablescan.h"
45+
#include "nodes/nodeFuncs.h"
46+
#include "utils/syscache.h"
47+
48+
49+
static bool TargetListSupportsBackwardScan(List *targetlist);
50+
static bool IndexSupportsBackwardScan(Oid indexid);
4551

4652

4753
/*
@@ -390,7 +396,8 @@ ExecSupportsBackwardScan(Plan *node)
390396
{
391397
case T_Result:
392398
if (outerPlan(node) != NULL)
393-
return ExecSupportsBackwardScan(outerPlan(node));
399+
return ExecSupportsBackwardScan(outerPlan(node)) &&
400+
TargetListSupportsBackwardScan(node->targetlist);
394401
else
395402
return false;
396403

@@ -403,29 +410,85 @@ ExecSupportsBackwardScan(Plan *node)
403410
if (!ExecSupportsBackwardScan((Plan *) lfirst(l)))
404411
return false;
405412
}
413+
/* need not check tlist because Append doesn't evaluate it */
406414
return true;
407415
}
408416

409417
case T_SeqScan:
410-
case T_IndexScan:
411418
case T_TidScan:
412419
case T_FunctionScan:
413420
case T_ValuesScan:
414421
case T_CteScan:
415422
case T_WorkTableScan:
416-
return true;
423+
return TargetListSupportsBackwardScan(node->targetlist);
424+
425+
case T_IndexScan:
426+
return IndexSupportsBackwardScan(((IndexScan *) node)->indexid) &&
427+
TargetListSupportsBackwardScan(node->targetlist);
417428

418429
case T_SubqueryScan:
419-
return ExecSupportsBackwardScan(((SubqueryScan *) node)->subplan);
430+
return ExecSupportsBackwardScan(((SubqueryScan *) node)->subplan) &&
431+
TargetListSupportsBackwardScan(node->targetlist);
420432

421433
case T_Material:
422434
case T_Sort:
435+
/* these don't evaluate tlist */
423436
return true;
424437

425438
case T_Limit:
439+
/* doesn't evaluate tlist */
426440
return ExecSupportsBackwardScan(outerPlan(node));
427441

428442
default:
429443
return false;
430444
}
431445
}
446+
447+
/*
448+
* If the tlist contains set-returning functions, we can't support backward
449+
* scan, because the TupFromTlist code is direction-ignorant.
450+
*/
451+
static bool
452+
TargetListSupportsBackwardScan(List *targetlist)
453+
{
454+
if (expression_returns_set((Node *) targetlist))
455+
return false;
456+
return true;
457+
}
458+
459+
/*
460+
* An IndexScan node supports backward scan only if the index's AM does.
461+
*/
462+
static bool
463+
IndexSupportsBackwardScan(Oid indexid)
464+
{
465+
bool result;
466+
HeapTuple ht_idxrel;
467+
HeapTuple ht_am;
468+
Form_pg_class idxrelrec;
469+
Form_pg_am amrec;
470+
471+
/* Fetch the pg_class tuple of the index relation */
472+
ht_idxrel = SearchSysCache(RELOID,
473+
ObjectIdGetDatum(indexid),
474+
0, 0, 0);
475+
if (!HeapTupleIsValid(ht_idxrel))
476+
elog(ERROR, "cache lookup failed for relation %u", indexid);
477+
idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
478+
479+
/* Fetch the pg_am tuple of the index' access method */
480+
ht_am = SearchSysCache(AMOID,
481+
ObjectIdGetDatum(idxrelrec->relam),
482+
0, 0, 0);
483+
if (!HeapTupleIsValid(ht_am))
484+
elog(ERROR, "cache lookup failed for access method %u",
485+
idxrelrec->relam);
486+
amrec = (Form_pg_am) GETSTRUCT(ht_am);
487+
488+
result = amrec->amcanbackward;
489+
490+
ReleaseSysCache(ht_idxrel);
491+
ReleaseSysCache(ht_am);
492+
493+
return result;
494+
}

src/include/catalog/catversion.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
3838
* Portions Copyright (c) 1994, Regents of the University of California
3939
*
40-
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.498 2008/10/14 17:12:33 tgl Exp $
40+
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.499 2008/10/17 22:10:30 tgl Exp $
4141
*
4242
*-------------------------------------------------------------------------
4343
*/
@@ -53,6 +53,6 @@
5353
*/
5454

5555
/* yyyymmddN */
56-
#define CATALOG_VERSION_NO 200810141
56+
#define CATALOG_VERSION_NO 200810171
5757

5858
#endif

src/include/catalog/pg_am.h

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
99
* Portions Copyright (c) 1994, Regents of the University of California
1010
*
11-
* $PostgreSQL: pgsql/src/include/catalog/pg_am.h,v 1.58 2008/09/15 18:43:41 tgl Exp $
11+
* $PostgreSQL: pgsql/src/include/catalog/pg_am.h,v 1.59 2008/10/17 22:10:30 tgl Exp $
1212
*
1313
* NOTES
1414
* the genbki.sh script reads this file and generates .bki
@@ -41,6 +41,7 @@ CATALOG(pg_am,2601)
4141
int2 amsupport; /* total number of support functions that this
4242
* AM uses */
4343
bool amcanorder; /* does AM support ordered scan results? */
44+
bool amcanbackward; /* does AM support backward scan? */
4445
bool amcanunique; /* does AM support UNIQUE indexes? */
4546
bool amcanmulticol; /* does AM support multi-column indexes? */
4647
bool amoptionalkey; /* can query omit key for the first column? */
@@ -75,48 +76,49 @@ typedef FormData_pg_am *Form_pg_am;
7576
* compiler constants for pg_am
7677
* ----------------
7778
*/
78-
#define Natts_pg_am 25
79+
#define Natts_pg_am 26
7980
#define Anum_pg_am_amname 1
8081
#define Anum_pg_am_amstrategies 2
8182
#define Anum_pg_am_amsupport 3
8283
#define Anum_pg_am_amcanorder 4
83-
#define Anum_pg_am_amcanunique 5
84-
#define Anum_pg_am_amcanmulticol 6
85-
#define Anum_pg_am_amoptionalkey 7
86-
#define Anum_pg_am_amindexnulls 8
87-
#define Anum_pg_am_amsearchnulls 9
88-
#define Anum_pg_am_amstorage 10
89-
#define Anum_pg_am_amclusterable 11
90-
#define Anum_pg_am_amkeytype 12
91-
#define Anum_pg_am_aminsert 13
92-
#define Anum_pg_am_ambeginscan 14
93-
#define Anum_pg_am_amgettuple 15
94-
#define Anum_pg_am_amgetbitmap 16
95-
#define Anum_pg_am_amrescan 17
96-
#define Anum_pg_am_amendscan 18
97-
#define Anum_pg_am_ammarkpos 19
98-
#define Anum_pg_am_amrestrpos 20
99-
#define Anum_pg_am_ambuild 21
100-
#define Anum_pg_am_ambulkdelete 22
101-
#define Anum_pg_am_amvacuumcleanup 23
102-
#define Anum_pg_am_amcostestimate 24
103-
#define Anum_pg_am_amoptions 25
84+
#define Anum_pg_am_amcanbackward 5
85+
#define Anum_pg_am_amcanunique 6
86+
#define Anum_pg_am_amcanmulticol 7
87+
#define Anum_pg_am_amoptionalkey 8
88+
#define Anum_pg_am_amindexnulls 9
89+
#define Anum_pg_am_amsearchnulls 10
90+
#define Anum_pg_am_amstorage 11
91+
#define Anum_pg_am_amclusterable 12
92+
#define Anum_pg_am_amkeytype 13
93+
#define Anum_pg_am_aminsert 14
94+
#define Anum_pg_am_ambeginscan 15
95+
#define Anum_pg_am_amgettuple 16
96+
#define Anum_pg_am_amgetbitmap 17
97+
#define Anum_pg_am_amrescan 18
98+
#define Anum_pg_am_amendscan 19
99+
#define Anum_pg_am_ammarkpos 20
100+
#define Anum_pg_am_amrestrpos 21
101+
#define Anum_pg_am_ambuild 22
102+
#define Anum_pg_am_ambulkdelete 23
103+
#define Anum_pg_am_amvacuumcleanup 24
104+
#define Anum_pg_am_amcostestimate 25
105+
#define Anum_pg_am_amoptions 26
104106

105107
/* ----------------
106108
* initial contents of pg_am
107109
* ----------------
108110
*/
109111

110-
DATA(insert OID = 403 ( btree 5 1 t t t t t t f t 0 btinsert btbeginscan btgettuple btgetbitmap btrescan btendscan btmarkpos btrestrpos btbuild btbulkdelete btvacuumcleanup btcostestimate btoptions ));
112+
DATA(insert OID = 403 ( btree 5 1 t t t t t t t f t 0 btinsert btbeginscan btgettuple btgetbitmap btrescan btendscan btmarkpos btrestrpos btbuild btbulkdelete btvacuumcleanup btcostestimate btoptions ));
111113
DESCR("b-tree index access method");
112114
#define BTREE_AM_OID 403
113-
DATA(insert OID = 405 ( hash 1 1 f f f f f f f f 23 hashinsert hashbeginscan hashgettuple hashgetbitmap hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbulkdelete hashvacuumcleanup hashcostestimate hashoptions ));
115+
DATA(insert OID = 405 ( hash 1 1 f t f f f f f f f 23 hashinsert hashbeginscan hashgettuple hashgetbitmap hashrescan hashendscan hashmarkpos hashrestrpos hashbuild hashbulkdelete hashvacuumcleanup hashcostestimate hashoptions ));
114116
DESCR("hash index access method");
115117
#define HASH_AM_OID 405
116-
DATA(insert OID = 783 ( gist 0 7 f f t t t t t t 0 gistinsert gistbeginscan gistgettuple gistgetbitmap gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbulkdelete gistvacuumcleanup gistcostestimate gistoptions ));
118+
DATA(insert OID = 783 ( gist 0 7 f f f t t t t t t 0 gistinsert gistbeginscan gistgettuple gistgetbitmap gistrescan gistendscan gistmarkpos gistrestrpos gistbuild gistbulkdelete gistvacuumcleanup gistcostestimate gistoptions ));
117119
DESCR("GiST index access method");
118120
#define GIST_AM_OID 783
119-
DATA(insert OID = 2742 ( gin 0 5 f f t t f f t f 0 gininsert ginbeginscan gingettuple gingetbitmap ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbulkdelete ginvacuumcleanup gincostestimate ginoptions ));
121+
DATA(insert OID = 2742 ( gin 0 5 f f f t t f f t f 0 gininsert ginbeginscan gingettuple gingetbitmap ginrescan ginendscan ginmarkpos ginrestrpos ginbuild ginbulkdelete ginvacuumcleanup gincostestimate ginoptions ));
120122
DESCR("GIN index access method");
121123
#define GIN_AM_OID 2742
122124

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy