diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index ae6dfbd8a5589f69a6ef094fbbe1418b4c3c1426..a68799334add93bb962b4d47d106f6871606ee0e 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.178 2008/10/06 13:59:37 tgl Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.179 2008/10/17 22:10:29 tgl Exp $ --> <!-- Documentation of the system catalogs, directed toward PostgreSQL developers --> @@ -401,6 +401,13 @@ <entry>Does the access method support ordered scans?</entry> </row> + <row> + <entry><structfield>amcanbackward</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Does the access method support backward scanning?</entry> + </row> + <row> <entry><structfield>amcanunique</structfield></entry> <entry><type>bool</type></entry> diff --git a/doc/src/sgml/indexam.sgml b/doc/src/sgml/indexam.sgml index 393ccfa2dd15d3e6be9f466f4bdb0264bf486935..db16c1d4ee5aa3ee41e161aa01bbc51480266223 100644 --- a/doc/src/sgml/indexam.sgml +++ b/doc/src/sgml/indexam.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.27 2008/08/14 18:47:58 tgl Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.28 2008/10/17 22:10:29 tgl Exp $ --> <chapter id="indexam"> <title>Index Access Method Interface Definition</title> @@ -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, <function>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 + <structname>pg_am</>.<structfield>amcanbackward</> is false, all subsequent + calls will have the same direction as the first one.) </para> <para> - The access method must support <quote>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 <function>ammarkpos</> call overrides the previously - marked position. + Access methods that support ordered scans must support <quote>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 <function>ammarkpos</> call overrides the + previously marked position. An access method that does not support + ordered scans should still provide mark and restore functions in + <structname>pg_am</>, but it is sufficient to have them throw errors if + called. </para> <para> diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c index 1de3f5a778efedac7a6daf329a19ba53039f579e..9b2e32576e19139d5c51722da3edcfb1ce8e6023 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 cd9d06f65d7ab4f7335f1bcc1a38211dee7dd17e..f242dcf469e813ca32897b9e335e734971beba01 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 a7a638e083b70b8fab376d09c74ae158c0ace830..c2f4a49c7600889f071db6fc7bc1d06d474e379c 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