diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index ae6dfbd8a5..a68799334a 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1,4 +1,4 @@
-
+
@@ -401,6 +401,13 @@
Does the access method support ordered scans?
+
+ amcanbackward
+ bool
+
+ Does the access method support backward scanning?
+
+
amcanunique
bool
diff --git a/doc/src/sgml/indexam.sgml b/doc/src/sgml/indexam.sgml
index 393ccfa2dd..db16c1d4ee 100644
--- a/doc/src/sgml/indexam.sgml
+++ b/doc/src/sgml/indexam.sgml
@@ -1,4 +1,4 @@
-
+
Index Access Method Interface Definition
@@ -474,15 +474,20 @@ amrestrpos (IndexScanDesc scan);
normally would. (This will only occur for access
methods that advertise they support ordered scans.) After the
first call, amgettuple> must be prepared to advance the scan in
- either direction from the most recently returned entry.
+ either direction from the most recently returned entry. (But if
+ pg_am>.amcanbackward> is false, all subsequent
+ calls will have the same direction as the first one.)
- The access method must support marking> a position in a scan
- and later returning to the marked position. The same position might be
- restored multiple times. However, only one position need be remembered
- per scan; a new ammarkpos> call overrides the previously
- marked position.
+ Access methods that support ordered scans must support marking> a
+ position in a scan and later returning to the marked position. The same
+ position might be restored multiple times. However, only one position need
+ be remembered per scan; a new ammarkpos> call overrides the
+ previously marked position. An access method that does not support
+ ordered scans should still provide mark and restore functions in
+ pg_am>, but it is sufficient to have them throw errors if
+ called.
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index 1de3f5a778..9b2e32576e 100644
--- a/src/backend/executor/execAmi.c
+++ b/src/backend/executor/execAmi.c
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/backend/executor/execAmi.c,v 1.99 2008/10/04 21:56:53 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execAmi.c,v 1.100 2008/10/17 22:10:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -42,6 +42,12 @@
#include "executor/nodeValuesscan.h"
#include "executor/nodeCtescan.h"
#include "executor/nodeWorktablescan.h"
+#include "nodes/nodeFuncs.h"
+#include "utils/syscache.h"
+
+
+static bool TargetListSupportsBackwardScan(List *targetlist);
+static bool IndexSupportsBackwardScan(Oid indexid);
/*
@@ -390,7 +396,8 @@ ExecSupportsBackwardScan(Plan *node)
{
case T_Result:
if (outerPlan(node) != NULL)
- return ExecSupportsBackwardScan(outerPlan(node));
+ return ExecSupportsBackwardScan(outerPlan(node)) &&
+ TargetListSupportsBackwardScan(node->targetlist);
else
return false;
@@ -403,29 +410,85 @@ ExecSupportsBackwardScan(Plan *node)
if (!ExecSupportsBackwardScan((Plan *) lfirst(l)))
return false;
}
+ /* need not check tlist because Append doesn't evaluate it */
return true;
}
case T_SeqScan:
- case T_IndexScan:
case T_TidScan:
case T_FunctionScan:
case T_ValuesScan:
case T_CteScan:
case T_WorkTableScan:
- return true;
+ return TargetListSupportsBackwardScan(node->targetlist);
+
+ case T_IndexScan:
+ return IndexSupportsBackwardScan(((IndexScan *) node)->indexid) &&
+ TargetListSupportsBackwardScan(node->targetlist);
case T_SubqueryScan:
- return ExecSupportsBackwardScan(((SubqueryScan *) node)->subplan);
+ return ExecSupportsBackwardScan(((SubqueryScan *) node)->subplan) &&
+ TargetListSupportsBackwardScan(node->targetlist);
case T_Material:
case T_Sort:
+ /* these don't evaluate tlist */
return true;
case T_Limit:
+ /* doesn't evaluate tlist */
return ExecSupportsBackwardScan(outerPlan(node));
default:
return false;
}
}
+
+/*
+ * If the tlist contains set-returning functions, we can't support backward
+ * scan, because the TupFromTlist code is direction-ignorant.
+ */
+static bool
+TargetListSupportsBackwardScan(List *targetlist)
+{
+ if (expression_returns_set((Node *) targetlist))
+ return false;
+ return true;
+}
+
+/*
+ * An IndexScan node supports backward scan only if the index's AM does.
+ */
+static bool
+IndexSupportsBackwardScan(Oid indexid)
+{
+ bool result;
+ HeapTuple ht_idxrel;
+ HeapTuple ht_am;
+ Form_pg_class idxrelrec;
+ Form_pg_am amrec;
+
+ /* Fetch the pg_class tuple of the index relation */
+ ht_idxrel = SearchSysCache(RELOID,
+ ObjectIdGetDatum(indexid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(ht_idxrel))
+ elog(ERROR, "cache lookup failed for relation %u", indexid);
+ idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
+
+ /* Fetch the pg_am tuple of the index' access method */
+ ht_am = SearchSysCache(AMOID,
+ ObjectIdGetDatum(idxrelrec->relam),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(ht_am))
+ elog(ERROR, "cache lookup failed for access method %u",
+ idxrelrec->relam);
+ amrec = (Form_pg_am) GETSTRUCT(ht_am);
+
+ result = amrec->amcanbackward;
+
+ ReleaseSysCache(ht_idxrel);
+ ReleaseSysCache(ht_am);
+
+ return result;
+}
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index cd9d06f65d..f242dcf469 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.498 2008/10/14 17:12:33 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.499 2008/10/17 22:10:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200810141
+#define CATALOG_VERSION_NO 200810171
#endif
diff --git a/src/include/catalog/pg_am.h b/src/include/catalog/pg_am.h
index a7a638e083..c2f4a49c76 100644
--- a/src/include/catalog/pg_am.h
+++ b/src/include/catalog/pg_am.h
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/pg_am.h,v 1.58 2008/09/15 18:43:41 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_am.h,v 1.59 2008/10/17 22:10:30 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@@ -41,6 +41,7 @@ CATALOG(pg_am,2601)
int2 amsupport; /* total number of support functions that this
* AM uses */
bool amcanorder; /* does AM support ordered scan results? */
+ bool amcanbackward; /* does AM support backward scan? */
bool amcanunique; /* does AM support UNIQUE indexes? */
bool amcanmulticol; /* does AM support multi-column indexes? */
bool amoptionalkey; /* can query omit key for the first column? */
@@ -75,48 +76,49 @@ typedef FormData_pg_am *Form_pg_am;
* compiler constants for pg_am
* ----------------
*/
-#define Natts_pg_am 25
+#define Natts_pg_am 26
#define Anum_pg_am_amname 1
#define Anum_pg_am_amstrategies 2
#define Anum_pg_am_amsupport 3
#define Anum_pg_am_amcanorder 4
-#define Anum_pg_am_amcanunique 5
-#define Anum_pg_am_amcanmulticol 6
-#define Anum_pg_am_amoptionalkey 7
-#define Anum_pg_am_amindexnulls 8
-#define Anum_pg_am_amsearchnulls 9
-#define Anum_pg_am_amstorage 10
-#define Anum_pg_am_amclusterable 11
-#define Anum_pg_am_amkeytype 12
-#define Anum_pg_am_aminsert 13
-#define Anum_pg_am_ambeginscan 14
-#define Anum_pg_am_amgettuple 15
-#define Anum_pg_am_amgetbitmap 16
-#define Anum_pg_am_amrescan 17
-#define Anum_pg_am_amendscan 18
-#define Anum_pg_am_ammarkpos 19
-#define Anum_pg_am_amrestrpos 20
-#define Anum_pg_am_ambuild 21
-#define Anum_pg_am_ambulkdelete 22
-#define Anum_pg_am_amvacuumcleanup 23
-#define Anum_pg_am_amcostestimate 24
-#define Anum_pg_am_amoptions 25
+#define Anum_pg_am_amcanbackward 5
+#define Anum_pg_am_amcanunique 6
+#define Anum_pg_am_amcanmulticol 7
+#define Anum_pg_am_amoptionalkey 8
+#define Anum_pg_am_amindexnulls 9
+#define Anum_pg_am_amsearchnulls 10
+#define Anum_pg_am_amstorage 11
+#define Anum_pg_am_amclusterable 12
+#define Anum_pg_am_amkeytype 13
+#define Anum_pg_am_aminsert 14
+#define Anum_pg_am_ambeginscan 15
+#define Anum_pg_am_amgettuple 16
+#define Anum_pg_am_amgetbitmap 17
+#define Anum_pg_am_amrescan 18
+#define Anum_pg_am_amendscan 19
+#define Anum_pg_am_ammarkpos 20
+#define Anum_pg_am_amrestrpos 21
+#define Anum_pg_am_ambuild 22
+#define Anum_pg_am_ambulkdelete 23
+#define Anum_pg_am_amvacuumcleanup 24
+#define Anum_pg_am_amcostestimate 25
+#define Anum_pg_am_amoptions 26
/* ----------------
* initial contents of pg_am
* ----------------
*/
-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 ));
+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 ));
DESCR("b-tree index access method");
#define BTREE_AM_OID 403
-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 ));
+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 ));
DESCR("hash index access method");
#define HASH_AM_OID 405
-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 ));
+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 ));
DESCR("GiST index access method");
#define GIST_AM_OID 783
-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 ));
+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 ));
DESCR("GIN index access method");
#define GIN_AM_OID 2742