]> granicus.if.org Git - postgresql/commitdiff
Partial indexes work again, courtesy of Martijn van Oosterhout.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 16 Jul 2001 05:07:00 +0000 (05:07 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 16 Jul 2001 05:07:00 +0000 (05:07 +0000)
Note: I didn't force an initdb, figuring that one today was enough.
However, there is a new function in pg_proc.h, and pg_dump won't be
able to dump partial indexes until you add that function.

32 files changed:
doc/src/sgml/indices.sgml
doc/src/sgml/ref/create_index.sgml
src/backend/bootstrap/bootstrap.c
src/backend/catalog/index.c
src/backend/commands/command.c
src/backend/commands/indexcmds.c
src/backend/executor/execUtils.c
src/backend/optimizer/path/indxpath.c
src/backend/optimizer/util/pathnode.c
src/backend/parser/analyze.c
src/backend/parser/gram.y
src/backend/parser/keywords.c
src/backend/tcop/utility.c
src/backend/utils/adt/ruleutils.c
src/backend/utils/adt/selfuncs.c
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/pg_dump.h
src/include/catalog/index.h
src/include/catalog/pg_proc.h
src/include/commands/defrem.h
src/include/config.h.in
src/include/nodes/execnodes.h
src/include/utils/builtins.h
src/interfaces/ecpg/preproc/keywords.c
src/interfaces/ecpg/preproc/preproc.y
src/test/regress/expected/create_index.out
src/test/regress/expected/portals_p2.out
src/test/regress/expected/sanity_check.out
src/test/regress/expected/select.out
src/test/regress/sql/create_index.sql
src/test/regress/sql/portals_p2.sql
src/test/regress/sql/select.sql

index abda7fd77fe241a2533213c0a5dddffb3277deb0..d81ce77517303ce44f0961aaf5c79a6232a8ce88 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $Header: /cvsroot/pgsql/doc/src/sgml/indices.sgml,v 1.19 2001/05/30 04:01:11 momjian Exp $ -->
+<!-- $Header: /cvsroot/pgsql/doc/src/sgml/indices.sgml,v 1.20 2001/07/16 05:06:57 tgl Exp $ -->
 
 <chapter id="indexes">
  <title id="indexes-title">Indexes</title>
@@ -603,22 +603,11 @@ CREATE MEMSTORE ON <replaceable>table</replaceable> COLUMNS <replaceable>cols</r
     </para>
    </note>
 
-   <note>
-    <title>Note</title>
-    <para>
-     Partial indexes are not currently supported by
-     <productname>PostgreSQL</productname>, but they were once supported
-     by its predecessor <productname>Postgres</productname>, and much
-     of the code is still there.  We hope to revive support for this
-     feature someday.
-    </para>
-   </note>
-
    <para>
     A <firstterm>partial index</firstterm>
     is an index built over a subset of a table; the subset is defined by
     a predicate.  <productname>Postgres</productname>
-    supported partial indexes with arbitrary
+    supports partial indexes with arbitrary
     predicates.  I believe IBM's <productname>DB2</productname>
     for AS/400 supports partial indexes
     using single-clause predicates.
index bc72fb1993aaeeea57355f8d62a78d8ca63c5a7b..e5194c255c64b66f32ce0cff6ca591659fad4dd6 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_index.sgml,v 1.19 2001/05/17 21:50:18 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_index.sgml,v 1.20 2001/07/16 05:06:57 tgl Exp $
 Postgres documentation
 -->
 
@@ -20,13 +20,15 @@ Postgres documentation
  </refnamediv>
  <refsynopsisdiv>
   <refsynopsisdivinfo>
-   <date>1999-07-20</date>
+   <date>2001-07-15</date>
   </refsynopsisdivinfo>
   <synopsis>
 CREATE [ UNIQUE ] INDEX <replaceable class="parameter">index_name</replaceable> ON <replaceable class="parameter">table</replaceable>
-    [ USING <replaceable class="parameter">acc_name</replaceable> ] ( <replaceable class="parameter">column</replaceable> [ <replaceable class="parameter">ops_name</replaceable> ] [, ...] )
+    [ USING <replaceable class="parameter">acc_method</replaceable> ] ( <replaceable class="parameter">column</replaceable> [ <replaceable class="parameter">ops_name</replaceable> ] [, ...] )
+    [ WHERE <replaceable class="parameter">predicate</replaceable> ]
 CREATE [ UNIQUE ] INDEX <replaceable class="parameter">index_name</replaceable> ON <replaceable class="parameter">table</replaceable>
-    [ USING <replaceable class="parameter">acc_name</replaceable> ] ( <replaceable class="parameter">func_name</replaceable>( <replaceable class="parameter">column</replaceable> [, ... ]) [ <replaceable class="parameter">ops_name</replaceable> ] )
+    [ USING <replaceable class="parameter">acc_method</replaceable> ] ( <replaceable class="parameter">func_name</replaceable>( <replaceable class="parameter">column</replaceable> [, ... ]) [ <replaceable class="parameter">ops_name</replaceable> ] )
+    [ WHERE <replaceable class="parameter">predicate</replaceable> ]
   </synopsis>
 
   <refsect2 id="R2-SQL-CREATEINDEX-1">
@@ -71,12 +73,12 @@ CREATE [ UNIQUE ] INDEX <replaceable class="parameter">index_name</replaceable>
      </varlistentry>
 
      <varlistentry>
-      <term><replaceable class="parameter">acc_name</replaceable></term>
+      <term><replaceable class="parameter">acc_method</replaceable></term>
       <listitem>
        <para>
        The name of the access method to be used for
        the index. The default access method is BTREE.
-       Postgres provides three access methods for indexes:
+       Postgres provides four access methods for indexes:
 
        <variablelist>
         <varlistentry>
@@ -106,6 +108,15 @@ CREATE [ UNIQUE ] INDEX <replaceable class="parameter">index_name</replaceable>
           </para>
          </listitem>
         </varlistentry>
+
+        <varlistentry>
+         <term>GIST</term>
+         <listitem>
+          <para>
+           Generalized Index Search Trees.
+          </para>
+         </listitem>
+        </varlistentry>
        </variablelist>
        </para>
       </listitem>
@@ -137,6 +148,15 @@ CREATE [ UNIQUE ] INDEX <replaceable class="parameter">index_name</replaceable>
        </para>
       </listitem>
      </varlistentry>
+
+     <varlistentry>
+      <term><replaceable class="parameter">predicate</replaceable></term>
+      <listitem>
+       <para>
+       Defines the constraint expression for a partial index.
+       </para>
+      </listitem>
+     </varlistentry>
     </variablelist>
    </para>
   </refsect2>
@@ -216,7 +236,7 @@ ERROR: Cannot create index: 'index_name' already exists.
   </para>
 
   <para>
-   Postgres provides btree, rtree and hash access methods for
+   Postgres provides btree, rtree, hash, and GiST access methods for
    indexes.  The btree access method is an implementation of
    Lehman-Yao high-concurrency btrees.  The rtree access method
    implements standard rtrees using Guttman's quadratic split algorithm.
@@ -227,6 +247,32 @@ ERROR: Cannot create index: 'index_name' already exists.
    access methods).
   </para>
 
+  <para>
+    When the <command>WHERE</command> clause is present, a
+    <firstterm>partial index</firstterm> is created.
+    A partial index is an index that contains entries for only a portion of
+    a table, usually a portion that is somehow more interesting than the
+    rest of the table. For example, if you have a table that contains both
+    billed and unbilled orders where the unbilled orders take up a small
+    fraction of the total table and yet that is an often used section, you
+    can improve performance by creating an index on just that portion.
+  </para>
+
+  <para>
+    The expression used in the <command>WHERE</command> clause may refer
+    only to columns of the underlying table (but it can use all columns,
+    not only the one(s) being indexed).  Currently, the
+    <productname>PostgreSQL</productname> planner can only devise query
+    plans that make use of a partial index when the predicate is built from
+    <command>AND</command> and <command>OR</command> combinations of
+    elements of the form
+    <firstterm>column</firstterm>
+    <firstterm>operator</firstterm>
+    <firstterm>constant</firstterm>.
+    However, more general predicates may still be useful in conjunction
+    with UNIQUE indexes, to enforce uniqueness over a subset of a table.
+  </para>
+
   <para>
    Use <xref linkend="sql-dropindex" endterm="sql-dropindex-title">
    to remove an index.
@@ -278,9 +324,10 @@ ERROR: Cannot create index: 'index_name' already exists.
    </para>
 
    <para>
-    Currently, only the btree access method supports multi-column
+    Currently, only the btree and gist access methods support multi-column
     indexes. Up to 16 keys may be specified by default (this limit
-    can be altered when building Postgres).
+    can be altered when building Postgres).  Only btree currently supports
+    unique indexes.
    </para>
 
   <para>
@@ -307,9 +354,9 @@ ERROR: Cannot create index: 'index_name' already exists.
       The difference between them is that <literal>bigbox_ops</literal>
       scales box coordinates down, to avoid floating-point exceptions from
       doing multiplication, addition, and subtraction on very large
-      floating-point coordinates.  If the field on which your rectangles lie
-      is about 20,000 units square or larger, you should use
-      <literal>bigbox_ops</literal>.
+      floating-point coordinates.  (Note: this was true some time ago,
+      but currently the two operator classes both use floating point
+      and are effectively identical.)
      </para>
     </listitem>
    </itemizedlist>
@@ -319,7 +366,7 @@ ERROR: Cannot create index: 'index_name' already exists.
     The following query shows all defined operator classes:
 
     <programlisting>
-SELECT am.amname AS acc_name,
+SELECT am.amname AS acc_method,
        opc.opcname AS ops_name,
        opr.oprname AS ops_comp
     FROM pg_am am, pg_amop amop,
@@ -327,7 +374,7 @@ SELECT am.amname AS acc_name,
     WHERE amop.amopid = am.oid AND
           amop.amopclaid = opc.oid AND
           amop.amopopr = opr.oid
-    ORDER BY acc_name, ops_name, ops_comp
+    ORDER BY acc_method, ops_name, ops_comp
     </programlisting>
    </para>
   </refsect2>
index ee1a4b7c31fe6bad3359490633a518aebedd65e4..7fc54779680cdc3e7ae4b7bb926014e7d1dbe8b9 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.111 2001/07/15 22:48:16 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.112 2001/07/16 05:06:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1100,8 +1100,9 @@ index_register(char *heap,
        newind->il_info = (IndexInfo *) palloc(sizeof(IndexInfo));
 
        memcpy(newind->il_info, indexInfo, sizeof(IndexInfo));
-       /* predicate will likely be null anyway, but may as well copy it */
-       newind->il_info->ii_Predicate = copyObject(indexInfo->ii_Predicate);
+       /* predicate will likely be null, but may as well copy it */
+       newind->il_info->ii_Predicate = (List *)
+               copyObject(indexInfo->ii_Predicate);
 
        newind->il_next = ILHead;
        ILHead = newind;
index f0fa73e83dd89fb3aee1d93bd22d0df173ff54c2..b48a13f9e176054c66a73269b8a565f44ef6a7e2 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.156 2001/07/15 22:48:17 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.157 2001/07/16 05:06:57 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -510,7 +510,7 @@ UpdateIndexRelation(Oid indexoid,
         * allocate a Form_pg_index big enough to hold the index-predicate (if
         * any) in string form
         */
-       if (indexInfo->ii_Predicate != NULL)
+       if (indexInfo->ii_Predicate != NIL)
        {
                predString = nodeToString(indexInfo->ii_Predicate);
                predText = DatumGetTextP(DirectFunctionCall1(textin,
@@ -586,87 +586,6 @@ UpdateIndexRelation(Oid indexoid,
        heap_freetuple(tuple);
 }
 
-/* ----------------------------------------------------------------
- *             UpdateIndexPredicate
- * ----------------------------------------------------------------
- */
-void
-UpdateIndexPredicate(Oid indexoid, Node *oldPred, Node *predicate)
-{
-       Node       *newPred;
-       char       *predString;
-       text       *predText;
-       Relation        pg_index;
-       HeapTuple       tuple;
-       HeapTuple       newtup;
-       int                     i;
-       Datum           values[Natts_pg_index];
-       char            nulls[Natts_pg_index];
-       char            replace[Natts_pg_index];
-
-       /*
-        * Construct newPred as a CNF expression equivalent to the OR of the
-        * original partial-index predicate ("oldPred") and the extension
-        * predicate ("predicate").
-        *
-        * This should really try to process the result to change things like
-        * "a>2 OR a>1" to simply "a>1", but for now all it does is make sure
-        * that if the extension predicate is NULL (i.e., it is being extended
-        * to be a complete index), then newPred will be NULL - in effect,
-        * changing "a>2 OR TRUE" to "TRUE". --Nels, Jan '93
-        */
-       newPred = NULL;
-       if (predicate != NULL)
-       {
-               newPred = (Node *) make_orclause(lcons(make_andclause((List *) predicate),
-                                                                 lcons(make_andclause((List *) oldPred),
-                                                                               NIL)));
-               newPred = (Node *) cnfify((Expr *) newPred, true);
-       }
-
-       /* translate the index-predicate to string form */
-       if (newPred != NULL)
-       {
-               predString = nodeToString(newPred);
-               predText = DatumGetTextP(DirectFunctionCall1(textin,
-                                                                                  CStringGetDatum(predString)));
-               pfree(predString);
-       }
-       else
-               predText = DatumGetTextP(DirectFunctionCall1(textin,
-                                                                                                  CStringGetDatum("")));
-
-       /* open the index system catalog relation */
-       pg_index = heap_openr(IndexRelationName, RowExclusiveLock);
-
-       tuple = SearchSysCache(INDEXRELID,
-                                                  ObjectIdGetDatum(indexoid),
-                                                  0, 0, 0);
-       if (!HeapTupleIsValid(tuple))
-               elog(ERROR, "UpdateIndexPredicate: cache lookup failed for index %u",
-                        indexoid);
-
-       for (i = 0; i < Natts_pg_index; i++)
-       {
-               nulls[i] = heap_attisnull(tuple, i + 1) ? 'n' : ' ';
-               replace[i] = ' ';
-               values[i] = (Datum) NULL;
-       }
-
-       replace[Anum_pg_index_indpred - 1] = 'r';
-       values[Anum_pg_index_indpred - 1] = PointerGetDatum(predText);
-
-       newtup = heap_modifytuple(tuple, pg_index, values, nulls, replace);
-
-       simple_heap_update(pg_index, &newtup->t_self, newtup);
-
-       heap_freetuple(newtup);
-       ReleaseSysCache(tuple);
-
-       heap_close(pg_index, RowExclusiveLock);
-       pfree(predText);
-}
-
 /* ----------------------------------------------------------------
  *             InitIndexStrategy
  *
@@ -1084,7 +1003,7 @@ BuildIndexInfo(HeapTuple indexTuple)
                pfree(predString);
        }
        else
-               ii->ii_Predicate = NULL;
+               ii->ii_Predicate = NIL;
 
        /* Other info */
        ii->ii_Unique = indexStruct->indisunique;
@@ -1684,7 +1603,7 @@ IndexBuildHeapScan(Relation heapRelation,
        Datum           attdata[INDEX_MAX_KEYS];
        char            nulls[INDEX_MAX_KEYS];
        double          reltuples;
-       Node       *predicate = indexInfo->ii_Predicate;
+       List       *predicate = indexInfo->ii_Predicate;
        TupleTable      tupleTable;
        TupleTableSlot *slot;
        ExprContext *econtext;
@@ -1708,7 +1627,7 @@ IndexBuildHeapScan(Relation heapRelation,
         * We construct the ExprContext anyway since we need a per-tuple
         * temporary memory context for function evaluation -- tgl July 00
         */
-       if (predicate != NULL)
+       if (predicate != NIL)
        {
                tupleTable = ExecCreateTupleTable(1);
                slot = ExecAllocTableSlot(tupleTable);
@@ -1831,12 +1750,12 @@ IndexBuildHeapScan(Relation heapRelation,
                 * VACUUM doesn't complain about tuple count mismatch for partial
                 * indexes.
                 */
-               if (predicate != NULL)
+               if (predicate != NIL)
                {
                        if (! tupleIsAlive)
                                continue;
                        ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
-                       if (!ExecQual((List *) predicate, econtext, false))
+                       if (!ExecQual(predicate, econtext, false))
                                continue;
                }
 
@@ -1865,7 +1784,7 @@ IndexBuildHeapScan(Relation heapRelation,
 
        heap_endscan(scan);
 
-       if (predicate != NULL)
+       if (predicate != NIL)
                ExecDropTupleTable(tupleTable, true);
        FreeExprContext(econtext);
 
index 4fcbeeceb6c5d804e940bf05f05d0dbf35005a93..744129f726fd57f2c3e04fb92112a9b9e0a98543 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.135 2001/07/15 22:48:17 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.136 2001/07/16 05:06:57 tgl Exp $
  *
  * NOTES
  *       The PerformAddAttribute() code, like most of the relation
@@ -1882,7 +1882,7 @@ AlterTableCreateToastTable(const char *relationName, bool silent)
        indexInfo->ii_NumIndexAttrs = 1;
        indexInfo->ii_NumKeyAttrs = 1;
        indexInfo->ii_KeyAttrNumbers[0] = 1;
-       indexInfo->ii_Predicate = NULL;
+       indexInfo->ii_Predicate = NIL;
        indexInfo->ii_FuncOid = InvalidOid;
        indexInfo->ii_Unique = false;
 
index 7398b0b0ce50aaa049ad9618dbd8524767b04159..6a85324bdc02cffd1a71b2548a057d5a90856dac 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.51 2001/07/15 22:48:17 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.52 2001/07/16 05:06:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -45,8 +45,6 @@
 
 /* non-export function prototypes */
 static void CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid);
-static void CheckPredExpr(Node *predicate, List *rangeTable, Oid baseRelOid);
-static void CheckPredClause(Expr *predicate, List *rangeTable, Oid baseRelOid);
 static void FuncIndexArgs(IndexInfo *indexInfo, Oid *classOidP,
                          IndexElem *funcIndex,
                          Oid relId,
@@ -144,12 +142,8 @@ DefineIndex(char *heapRelationName,
        }
 
        /*
-        * Convert the partial-index predicate from parsetree form to plan
-        * form, so it can be readily evaluated during index creation. Note:
-        * "predicate" comes in as a list containing (1) the predicate itself
-        * (a where_clause), and (2) a corresponding range table.
-        *
-        * [(1) is 'predicate' and (2) is 'rangetable' now. - ay 10/94]
+        * Convert the partial-index predicate from parsetree form to
+        * an implicit-AND qual expression, for easier evaluation at runtime.
         */
        if (predicate != NULL && rangetable != NIL)
        {
@@ -166,7 +160,7 @@ DefineIndex(char *heapRelationName,
         * structure
         */
        indexInfo = makeNode(IndexInfo);
-       indexInfo->ii_Predicate = (Node *) cnfPred;
+       indexInfo->ii_Predicate = cnfPred;
        indexInfo->ii_FuncOid = InvalidOid;
        indexInfo->ii_Unique = unique;
 
@@ -218,155 +212,30 @@ DefineIndex(char *heapRelationName,
 }
 
 
-/*
- * ExtendIndex
- *             Extends a partial index.
- */
-void
-ExtendIndex(char *indexRelationName, Expr *predicate, List *rangetable)
-{
-       Relation        heapRelation;
-       Relation        indexRelation;
-       Oid                     accessMethodId,
-                               indexId,
-                               relationId;
-       HeapTuple       tuple;
-       Form_pg_index index;
-       List       *cnfPred = NIL;
-       IndexInfo  *indexInfo;
-       Node       *oldPred;
-
-       /*
-        * Get index's relation id and access method id from pg_class
-        */
-       tuple = SearchSysCache(RELNAME,
-                                                  PointerGetDatum(indexRelationName),
-                                                  0, 0, 0);
-       if (!HeapTupleIsValid(tuple))
-               elog(ERROR, "ExtendIndex: index \"%s\" not found",
-                        indexRelationName);
-       indexId = tuple->t_data->t_oid;
-       accessMethodId = ((Form_pg_class) GETSTRUCT(tuple))->relam;
-       ReleaseSysCache(tuple);
-
-       /*
-        * Extract info from the pg_index tuple for the index
-        */
-       tuple = SearchSysCache(INDEXRELID,
-                                                  ObjectIdGetDatum(indexId),
-                                                  0, 0, 0);
-       if (!HeapTupleIsValid(tuple))
-               elog(ERROR, "ExtendIndex: relation \"%s\" is not an index",
-                        indexRelationName);
-       index = (Form_pg_index) GETSTRUCT(tuple);
-       Assert(index->indexrelid == indexId);
-       relationId = index->indrelid;
-       indexInfo = BuildIndexInfo(tuple);
-       oldPred = indexInfo->ii_Predicate;
-       ReleaseSysCache(tuple);
-
-       if (oldPred == NULL)
-               elog(ERROR, "ExtendIndex: \"%s\" is not a partial index",
-                        indexRelationName);
-
-       /*
-        * Convert the extension predicate from parsetree form to plan form,
-        * so it can be readily evaluated during index creation. Note:
-        * "predicate" comes in two parts (1) the predicate expression itself,
-        * and (2) a corresponding range table.
-        *
-        * XXX I think this code is broken --- index_build expects a single
-        * expression not a list --- tgl Jul 00
-        */
-       if (rangetable != NIL)
-       {
-               cnfPred = cnfify((Expr *) copyObject(predicate), true);
-               fix_opids((Node *) cnfPred);
-               CheckPredicate(cnfPred, rangetable, relationId);
-       }
-
-       /* pass new predicate to index_build */
-       indexInfo->ii_Predicate = (Node *) cnfPred;
-
-       /* Open heap and index rels, and get suitable locks */
-       heapRelation = heap_open(relationId, ShareLock);
-       indexRelation = index_open(indexId);
-
-       /* Obtain exclusive lock on it, just to be sure */
-       LockRelation(indexRelation, AccessExclusiveLock);
-
-       InitIndexStrategy(indexInfo->ii_NumIndexAttrs,
-                                         indexRelation, accessMethodId);
-
-       /*
-        * XXX currently BROKEN: if we want to support EXTEND INDEX, oldPred
-        * needs to be passed through to IndexBuildHeapScan.  We could do this
-        * without help from the index AMs if we added an oldPred field to the
-        * IndexInfo struct.  Currently I'm expecting that EXTEND INDEX will
-        * get removed, so I'm not going to do that --- tgl 7/14/01
-        */
-
-       index_build(heapRelation, indexRelation, indexInfo);
-
-       /* heap and index rels are closed as a side-effect of index_build */
-}
-
-
 /*
  * CheckPredicate
  *             Checks that the given list of partial-index predicates refer
- *             (via the given range table) only to the given base relation oid,
- *             and that they're in a form the planner can handle, i.e.,
- *             boolean combinations of "ATTR OP CONST" (yes, for now, the ATTR
- *             has to be on the left).
+ *             (via the given range table) only to the given base relation oid.
+ *
+ * This used to also constrain the form of the predicate to forms that
+ * indxpath.c could do something with.  However, that seems overly
+ * restrictive.  One useful application of partial indexes is to apply
+ * a UNIQUE constraint across a subset of a table, and in that scenario
+ * any evaluatable predicate will work.  So accept any predicate here
+ * (except ones requiring a plan), and let indxpath.c fend for itself.
  */
 
 static void
 CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid)
 {
-       List       *item;
-
-       foreach(item, predList)
-               CheckPredExpr(lfirst(item), rangeTable, baseRelOid);
-}
-
-static void
-CheckPredExpr(Node *predicate, List *rangeTable, Oid baseRelOid)
-{
-       List       *clauses = NIL,
-                          *clause;
-
-       if (is_opclause(predicate))
-       {
-               CheckPredClause((Expr *) predicate, rangeTable, baseRelOid);
-               return;
-       }
-       else if (or_clause(predicate) || and_clause(predicate))
-               clauses = ((Expr *) predicate)->args;
-       else
-               elog(ERROR, "Unsupported partial-index predicate expression type");
-
-       foreach(clause, clauses)
-               CheckPredExpr(lfirst(clause), rangeTable, baseRelOid);
-}
-
-static void
-CheckPredClause(Expr *predicate, List *rangeTable, Oid baseRelOid)
-{
-       Var                *pred_var;
-       Const      *pred_const;
-
-       pred_var = (Var *) get_leftop(predicate);
-       pred_const = (Const *) get_rightop(predicate);
-
-       if (!IsA(predicate->oper, Oper) ||
-               !IsA(pred_var, Var) ||
-               !IsA(pred_const, Const))
-               elog(ERROR, "Unsupported partial-index predicate clause type");
-
-       if (getrelid(pred_var->varno, rangeTable) != baseRelOid)
+       if (length(rangeTable) != 1 || getrelid(1, rangeTable) != baseRelOid)
                elog(ERROR,
-                "Partial-index predicates may refer only to the base relation");
+                        "Partial-index predicates may refer only to the base relation");
+
+       if (contain_subplans((Node *) predList))
+               elog(ERROR, "Cannot use subselect in index predicate");
+       if (contain_agg_clause((Node *) predList))
+               elog(ERROR, "Cannot use aggregate in index predicate");
 }
 
 
index 9465604b584ce3022d7016f908e5b6bc2ea8307e..2c76c9b7d987f03722de18961077d1bb4063b4f0 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.76 2001/07/15 22:48:17 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.77 2001/07/16 05:06:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -641,7 +641,7 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
        for (i = 0; i < numIndices; i++)
        {
                IndexInfo  *indexInfo;
-               Node       *predicate;
+               List       *predicate;
                InsertIndexResult result;
 
                if (relationDescs[i] == NULL)
@@ -649,10 +649,10 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 
                indexInfo = indexInfoArray[i];
                predicate = indexInfo->ii_Predicate;
-               if (predicate != NULL)
+               if (predicate != NIL)
                {
                        /* Skip this index-update if the predicate isn't satisfied */
-                       if (!ExecQual((List *) predicate, econtext, false))
+                       if (!ExecQual(predicate, econtext, false))
                                continue;
                }
 
index 477fa6ed5caf1b00cd6995ccdf3519030ef1fc4d..bdff8fe7e73cba75c8bbabd1141fda17888f75bc 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.108 2001/06/25 21:11:43 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.109 2001/07/16 05:06:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -195,8 +195,13 @@ create_index_paths(Query *root, RelOptInfo *rel)
                 * 4. Generate an indexscan path if there are relevant restriction
                 * clauses OR the index ordering is potentially useful for later
                 * merging or final output ordering.
+                *
+                * If there is a predicate, consider it anyway since the index
+                * predicate has already been found to match the query.
                 */
-               if (restrictclauses != NIL || useful_pathkeys != NIL)
+               if (restrictclauses != NIL ||
+                       useful_pathkeys != NIL ||
+                       index->indpred != NIL)
                        add_path(rel, (Path *)
                                         create_index_path(root, rel, index,
                                                                           restrictclauses,
@@ -974,18 +979,18 @@ pred_test(List *predicate_list, List *restrictinfo_list, List *joininfo_list)
         * clauses (those in restrictinfo_list). --Nels, Dec '92
         */
 
-       if (predicate_list == NULL)
+       if (predicate_list == NIL)
                return true;                    /* no predicate: the index is usable */
-       if (restrictinfo_list == NULL)
+       if (restrictinfo_list == NIL)
                return false;                   /* no restriction clauses: the test must
                                                                 * fail */
 
        foreach(pred, predicate_list)
        {
-
                /*
                 * if any clause is not implied, the whole predicate is not
-                * implied
+                * implied.  Note that checking for sub-ANDs here is redundant
+                * if the predicate has been cnfify()-ed.
                 */
                if (and_clause(lfirst(pred)))
                {
@@ -1011,15 +1016,16 @@ pred_test(List *predicate_list, List *restrictinfo_list, List *joininfo_list)
 static bool
 one_pred_test(Expr *predicate, List *restrictinfo_list)
 {
-       RestrictInfo *restrictinfo;
        List       *item;
 
        Assert(predicate != NULL);
        foreach(item, restrictinfo_list)
        {
-               restrictinfo = (RestrictInfo *) lfirst(item);
+               RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(item);
+
                /* if any clause implies the predicate, return true */
-               if (one_pred_clause_expr_test(predicate, (Node *) restrictinfo->clause))
+               if (one_pred_clause_expr_test(predicate,
+                                                                         (Node *) restrictinfo->clause))
                        return true;
        }
        return false;
@@ -1055,7 +1061,6 @@ one_pred_clause_expr_test(Expr *predicate, Node *clause)
                items = ((Expr *) clause)->args;
                foreach(item, items)
                {
-
                        /*
                         * if any AND item implies the predicate, the whole clause
                         * does
@@ -1102,7 +1107,6 @@ one_pred_clause_test(Expr *predicate, Node *clause)
                items = predicate->args;
                foreach(item, items)
                {
-
                        /*
                         * if any item is not implied, the whole predicate is not
                         * implied
@@ -1177,26 +1181,30 @@ clause_pred_clause_test(Expr *predicate, Node *clause)
                                test_strategy;
        Oper       *test_oper;
        Expr       *test_expr;
-       bool            test_result,
-                               isNull;
+       Datum           test_result;
+       bool            isNull;
        Relation        relation;
        HeapScanDesc scan;
        HeapTuple       tuple;
        ScanKeyData entry[3];
        Form_pg_amop aform;
+       ExprContext *econtext;
+
+       /* Check the basic form; for now, only allow the simplest case */
+       /* Note caller already verified is_opclause(predicate) */
+       if (!is_opclause(clause))
+               return false;
 
        pred_var = (Var *) get_leftop(predicate);
        pred_const = (Const *) get_rightop(predicate);
        clause_var = (Var *) get_leftop((Expr *) clause);
        clause_const = (Const *) get_rightop((Expr *) clause);
 
-       /* Check the basic form; for now, only allow the simplest case */
-       if (!is_opclause(clause) ||
-               !IsA(clause_var, Var) ||
+       if (!IsA(clause_var, Var) ||
                clause_const == NULL ||
                !IsA(clause_const, Const) ||
-               !IsA(predicate->oper, Oper) ||
                !IsA(pred_var, Var) ||
+               pred_const == NULL ||
                !IsA(pred_const, Const))
                return false;
 
@@ -1211,10 +1219,15 @@ clause_pred_clause_test(Expr *predicate, Node *clause)
        pred_op = ((Oper *) ((Expr *) predicate)->oper)->opno;
        clause_op = ((Oper *) ((Expr *) clause)->oper)->opno;
 
-
        /*
         * 1. Find a "btree" strategy number for the pred_op
+        *
+        * XXX consider using syscache lookups for these searches.  Right
+        * now we don't have caches that match all of the search conditions,
+        * but reconsider it after upcoming restructuring of pg_opclass.
         */
+       relation = heap_openr(AccessMethodOperatorRelationName, AccessShareLock);
+
        ScanKeyEntryInitialize(&entry[0], 0,
                                                   Anum_pg_amop_amopid,
                                                   F_OIDEQ,
@@ -1225,8 +1238,6 @@ clause_pred_clause_test(Expr *predicate, Node *clause)
                                                   F_OIDEQ,
                                                   ObjectIdGetDatum(pred_op));
 
-       relation = heap_openr(AccessMethodOperatorRelationName, AccessShareLock);
-
        /*
         * The following assumes that any given operator will only be in a
         * single btree operator class.  This is true at least for all the
@@ -1254,7 +1265,6 @@ clause_pred_clause_test(Expr *predicate, Node *clause)
 
        heap_endscan(scan);
 
-
        /*
         * 2. From the same opclass, find a strategy num for the clause_op
         */
@@ -1281,13 +1291,12 @@ clause_pred_clause_test(Expr *predicate, Node *clause)
 
        /* Get the restriction clause operator's strategy number (1 to 5) */
        clause_strategy = (StrategyNumber) aform->amopstrategy;
-       heap_endscan(scan);
 
+       heap_endscan(scan);
 
        /*
         * 3. Look up the "test" strategy number in the implication table
         */
-
        test_strategy = BT_implic_table[clause_strategy - 1][pred_strategy - 1];
        if (test_strategy == 0)
        {
@@ -1298,7 +1307,6 @@ clause_pred_clause_test(Expr *predicate, Node *clause)
        /*
         * 4. From the same opclass, find the operator for the test strategy
         */
-
        ScanKeyEntryInitialize(&entry[2], 0,
                                                   Anum_pg_amop_amopstrategy,
                                                   F_INT2EQ,
@@ -1329,19 +1337,20 @@ clause_pred_clause_test(Expr *predicate, Node *clause)
                                                 InvalidOid,    /* opid */
                                                 BOOLOID);              /* opresulttype */
        replace_opid(test_oper);
-
        test_expr = make_opclause(test_oper,
-                                                         copyObject(clause_const),
-                                                         copyObject(pred_const));
+                                                         (Var *) clause_const,
+                                                         (Var *) pred_const);
 
-       test_result = ExecEvalExpr((Node *) test_expr, NULL, &isNull, NULL);
+       econtext = MakeExprContext(NULL, TransactionCommandContext);
+       test_result = ExecEvalExpr((Node *) test_expr, econtext, &isNull, NULL);
+       FreeExprContext(econtext);
 
        if (isNull)
        {
                elog(DEBUG, "clause_pred_clause_test: null test result");
                return false;
        }
-       return test_result;
+       return DatumGetBool(test_result);
 }
 
 
index 7f1f3b402a40701bb9f22149f2df07f2cd644e09..1377fc06a499b7ef115bd637cd1cb685376cbae4 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.74 2001/06/05 05:26:04 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.75 2001/07/16 05:06:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -362,6 +362,13 @@ create_index_path(Query *root,
        pathnode->alljoinquals = false;
        pathnode->rows = rel->rows;
 
+       /*
+        * Not sure if this is necessary, but it should help if the
+        * statistics are too far off
+        */
+       if (index->indpred && index->tuples < pathnode->rows)
+               pathnode->rows = index->tuples;
+
        cost_index(&pathnode->path, root, rel, index, indexquals, false);
 
        return pathnode;
index 9d6dfeb69a7266ea5d30076c3b4f10b664bf45b5..186fa12450757a1d427b2bc5be34fe024a12d81d 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.192 2001/07/04 17:36:54 tgl Exp $
+ *     $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.193 2001/07/16 05:06:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -45,7 +45,6 @@ static Query *transformStmt(ParseState *pstate, Node *stmt);
 static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
 static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt);
 static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt);
-static Query *transformExtendStmt(ParseState *pstate, ExtendStmt *stmt);
 static Query *transformRuleStmt(ParseState *query, RuleStmt *stmt);
 static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
 static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt);
@@ -148,10 +147,6 @@ transformStmt(ParseState *pstate, Node *parseTree)
                        result = transformIndexStmt(pstate, (IndexStmt *) parseTree);
                        break;
 
-               case T_ExtendStmt:
-                       result = transformExtendStmt(pstate, (ExtendStmt *) parseTree);
-                       break;
-
                case T_RuleStmt:
                        result = transformRuleStmt(pstate, (RuleStmt *) parseTree);
                        break;
@@ -1630,43 +1625,34 @@ static Query *
 transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
 {
        Query      *qry;
+       RangeTblEntry *rte;
 
        qry = makeNode(Query);
        qry->commandType = CMD_UTILITY;
 
        /* take care of the where clause */
-       stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
-
-       qry->hasSubLinks = pstate->p_hasSubLinks;
-
-       stmt->rangetable = pstate->p_rtable;
-
-       qry->utilityStmt = (Node *) stmt;
-
-       return qry;
-}
-
-/*
- * transformExtendStmt -
- *       transform the qualifications of the Extend Index Statement
- *
- */
-static Query *
-transformExtendStmt(ParseState *pstate, ExtendStmt *stmt)
-{
-       Query      *qry;
+       if (stmt->whereClause)
+       {
+               /*
+                * Put the parent table into the rtable so that the WHERE clause can
+                * refer to its fields without qualification.  Note that this only
+                * works if the parent table already exists --- so we can't easily
+                * support predicates on indexes created implicitly by CREATE TABLE.
+                * Fortunately, that's not necessary.
+                */
+               rte = addRangeTableEntry(pstate, stmt->relname, NULL, false, true);
 
-       qry = makeNode(Query);
-       qry->commandType = CMD_UTILITY;
+               /* no to join list, yes to namespace */
+               addRTEtoQuery(pstate, rte, false, true);
 
-       /* take care of the where clause */
-       stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
+               stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
+       }
 
        qry->hasSubLinks = pstate->p_hasSubLinks;
-
        stmt->rangetable = pstate->p_rtable;
 
        qry->utilityStmt = (Node *) stmt;
+
        return qry;
 }
 
index b83ca3783fd9a17c9f68d4bc4735b6bbb9ff6084..99312049a83418ea237a8fc3672ec97b71f544fd 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.236 2001/07/12 18:02:59 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.237 2001/07/16 05:06:58 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -135,7 +135,7 @@ static void doNegateFloat(Value *v);
                CreateSchemaStmt, CreateSeqStmt, CreateStmt, CreateTrigStmt,
                CreateUserStmt, CreatedbStmt, CursorStmt, DefineStmt, DeleteStmt,
                DropGroupStmt, DropPLangStmt, DropSchemaStmt, DropStmt, DropTrigStmt,
-               DropUserStmt, DropdbStmt, ExplainStmt, ExtendStmt, FetchStmt,
+               DropUserStmt, DropdbStmt, ExplainStmt, FetchStmt,
                GrantStmt, IndexStmt, InsertStmt, ListenStmt, LoadStmt, LockStmt,
                NotifyStmt, OptimizableStmt, ProcedureStmt, ReindexStmt,
                RemoveAggrStmt, RemoveFuncStmt, RemoveOperStmt,
@@ -345,7 +345,7 @@ static void doNegateFloat(Value *v);
                BACKWARD, BEFORE, BINARY, BIT,
                CACHE, CHECKPOINT, CLUSTER, COMMENT, COPY, CREATEDB, CREATEUSER, CYCLE,
                DATABASE, DELIMITERS, DO,
-               EACH, ENCODING, EXCLUSIVE, EXPLAIN, EXTEND,
+               EACH, ENCODING, EXCLUSIVE, EXPLAIN,
                FORCE, FORWARD, FUNCTION, HANDLER,
                ILIKE, INCREMENT, INDEX, INHERITS, INSTEAD, ISNULL,
                LANCOMPILER, LIMIT, LISTEN, LOAD, LOCATION, LOCK_P,
@@ -450,7 +450,6 @@ stmt :      AlterSchemaStmt
                | DropPLangStmt
                | DropTrigStmt
                | DropUserStmt
-               | ExtendStmt
                | ExplainStmt
                | FetchStmt
                | GrantStmt
@@ -2392,14 +2391,14 @@ RevokeStmt:  REVOKE privileges ON opt_table relation_name_list FROM grantee_list
  *
  *             QUERY:
  *                             create index <indexname> on <relname>
- *                               using <access> "(" (<col> with <op>)+ ")" [with
- *                               <target_list>]
+ *                               [ using <access> ] "(" (<col> with <op>)+ ")"
+ *                               [ with <parameters> ]
+ *                               [ where <predicate> ]
  *
- *     [where <qual>] is not supported anymore
  *****************************************************************************/
 
 IndexStmt:     CREATE index_opt_unique INDEX index_name ON relation_name
-                       access_method_clause '(' index_params ')' opt_with
+                       access_method_clause '(' index_params ')' opt_with where_clause
                                {
                                        IndexStmt *n = makeNode(IndexStmt);
                                        n->unique = $2;
@@ -2408,7 +2407,7 @@ IndexStmt:        CREATE index_opt_unique INDEX index_name ON relation_name
                                        n->accessMethod = $7;
                                        n->indexParams = $9;
                                        n->withClause = $11;
-                                       n->whereClause = NULL;
+                                       n->whereClause = $12;
                                        $$ = (Node *)n;
                                }
                ;
@@ -2471,22 +2470,6 @@ opt_class:  class
                ;
 
 
-/*****************************************************************************
- *
- *             QUERY:
- *                             extend index <indexname> [where <qual>]
- *
- *****************************************************************************/
-
-ExtendStmt:  EXTEND INDEX index_name where_clause
-                               {
-                                       ExtendStmt *n = makeNode(ExtendStmt);
-                                       n->idxname = $3;
-                                       n->whereClause = $4;
-                                       $$ = (Node *)n;
-                               }
-               ;
-
 /*****************************************************************************
  *
  *             QUERY:
@@ -5775,7 +5758,6 @@ ColLabel:  ColId                                          { $$ = $1; }
                | EXCEPT                                                { $$ = "except"; }
                | EXISTS                                                { $$ = "exists"; }
                | EXPLAIN                                               { $$ = "explain"; }
-               | EXTEND                                                { $$ = "extend"; }
                | EXTRACT                                               { $$ = "extract"; }
                | FALSE_P                                               { $$ = "false"; }
                | FLOAT                                                 { $$ = "float"; }
index ccdfb88a2e2a0ec01d557203dcb0887aa9d4ad9a..014b1198503c4b704a2e6086d0ebd9a0388f611d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.93 2001/06/19 22:39:11 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.94 2001/07/16 05:06:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -109,7 +109,6 @@ static ScanKeyword ScanKeywords[] = {
        {"execute", EXECUTE},
        {"exists", EXISTS},
        {"explain", EXPLAIN},
-       {"extend", EXTEND},
        {"extract", EXTRACT},
        {"false", FALSE_P},
        {"fetch", FETCH},
index 5be50e7aade280377293ff4d7b3ba9b8aa49e2a5..a791a67715ee3389d96b5728c4426a3796d876ca 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.114 2001/06/18 16:13:21 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.115 2001/07/16 05:06:58 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -567,18 +567,6 @@ ProcessUtility(Node *parsetree,
                        DefineSequence((CreateSeqStmt *) parsetree);
                        break;
 
-               case T_ExtendStmt:
-                       {
-                               ExtendStmt *stmt = (ExtendStmt *) parsetree;
-
-                               set_ps_display(commandTag = "EXTEND");
-
-                               ExtendIndex(stmt->idxname,              /* index name */
-                                                       (Expr *) stmt->whereClause, /* where */
-                                                       stmt->rangetable);
-                       }
-                       break;
-
                case T_RemoveAggrStmt:
                        {
                                RemoveAggrStmt *stmt = (RemoveAggrStmt *) parsetree;
index 5f7cfead6c189f094364fa28e3878577716dc11d..afc2b63430bae8819fa19c611768dabb70145dda 100644 (file)
@@ -3,7 +3,7 @@
  *                             back to source text
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.79 2001/07/10 00:02:02 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.80 2001/07/16 05:06:59 tgl Exp $
  *
  *       This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -40,6 +40,7 @@
 #include <unistd.h>
 #include <fcntl.h>
 
+#include "catalog/heap.h"
 #include "catalog/pg_index.h"
 #include "catalog/pg_operator.h"
 #include "catalog/pg_shadow.h"
@@ -554,6 +555,64 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
 }
 
 
+/* ----------
+ * get_expr                    - Decompile an expression tree
+ *
+ * Input: an expression tree in nodeToString form, and a relation OID
+ *
+ * Output: reverse-listed expression
+ *
+ * Currently, the expression can only refer to a single relation, namely
+ * the one specified by the second parameter.  This is sufficient for
+ * partial indexes, column default expressions, etc.
+ * ----------
+ */
+Datum
+pg_get_expr(PG_FUNCTION_ARGS)
+{
+       text    *expr = PG_GETARG_TEXT_P(0);
+       Oid             relid = PG_GETARG_OID(1);
+       text    *result;
+       Node    *node;
+       List    *context;
+       char    *exprstr;
+       char    *relname;
+       char    *str;
+
+       /* Get the name for the relation */
+       relname = get_rel_name(relid);
+       if (relname == NULL)
+               PG_RETURN_NULL();               /* should we raise an error? */
+
+       /* Convert input TEXT object to C string */
+       exprstr = DatumGetCString(DirectFunctionCall1(textout,
+                                                                                                 PointerGetDatum(expr)));
+
+       /* Convert expression to node tree */
+       node = (Node *) stringToNode(exprstr);
+
+       /*
+        * If top level is a List, assume it is an implicit-AND structure,
+        * and convert to explicit AND.  This is needed for partial index
+        * predicates.
+        */
+       if (node && IsA(node, List))
+       {
+               node = (Node *) make_ands_explicit((List *) node);
+       }
+
+       /* Deparse */
+       context = deparse_context_for(relname, relid);
+       str = deparse_expression(node, context, false);
+       
+       /* Pass the result back as TEXT */
+       result = DatumGetTextP(DirectFunctionCall1(textin,
+                                                                                          CStringGetDatum(str)));
+
+       PG_RETURN_TEXT_P(result);
+}
+
+
 /* ----------
  * get_userbyid                        - Get a user name by usesysid and
  *                               fallback to 'unknown (UID=n)'
index 3ab3881c257039b15685f962dc9bc89fd9260f0a..e0cfeefaee41b627309de8129dc5572fbef25af3 100644 (file)
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.94 2001/06/25 21:11:44 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.95 2001/07/16 05:06:59 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -86,6 +86,7 @@
 #include "optimizer/cost.h"
 #include "optimizer/pathnode.h"
 #include "optimizer/plancat.h"
+#include "optimizer/prep.h"
 #include "parser/parse_func.h"
 #include "parser/parse_oper.h"
 #include "parser/parsetree.h"
@@ -2950,24 +2951,63 @@ genericcostestimate(Query *root, RelOptInfo *rel,
 {
        double          numIndexTuples;
        double          numIndexPages;
+       List       *selectivityQuals = indexQuals;
 
-       /* Estimate the fraction of main-table tuples that will be visited */
-       *indexSelectivity = clauselist_selectivity(root, indexQuals,
-                                                                                          lfirsti(rel->relids));
+       /*
+        * If the index is partial, AND the index predicate with the explicitly
+        * given indexquals to produce a more accurate idea of the index
+        * restriction.  This may produce redundant clauses, which we hope that
+        * cnfify and clauselist_selectivity will deal with intelligently.
+        *
+        * Note that index->indpred and indexQuals are both in implicit-AND
+        * form to start with, which we have to make explicit to hand to
+        * canonicalize_qual, and then we get back implicit-AND form again.
+        */
+       if (index->indpred != NIL)
+       {
+               Expr   *andedQuals;
 
-       /* Estimate the number of index tuples that will be visited */
-       numIndexTuples = *indexSelectivity * index->tuples;
+               andedQuals = make_ands_explicit(nconc(listCopy(index->indpred),
+                                                                                         indexQuals));
+               selectivityQuals = canonicalize_qual(andedQuals, true);
+       }
 
-       /* Estimate the number of index pages that will be retrieved */
-       numIndexPages = *indexSelectivity * index->pages;
+       /* Estimate the fraction of main-table tuples that will be visited */
+       *indexSelectivity = clauselist_selectivity(root, selectivityQuals,
+                                                                                          lfirsti(rel->relids));
 
        /*
-        * Always estimate at least one tuple and page are touched, even when
+        * Estimate the number of tuples that will be visited.  We do it in
+        * this rather peculiar-looking way in order to get the right answer
+        * for partial indexes.  We can bound the number of tuples by the
+        * index size, in any case.
+        */
+       numIndexTuples = *indexSelectivity * rel->tuples;
+
+       if (numIndexTuples > index->tuples)
+               numIndexTuples = index->tuples;
+
+       /*
+        * Always estimate at least one tuple is touched, even when
         * indexSelectivity estimate is tiny.
         */
        if (numIndexTuples < 1.0)
                numIndexTuples = 1.0;
-       if (numIndexPages < 1.0)
+
+       /*
+        * Estimate the number of index pages that will be retrieved.
+        *
+        * For all currently-supported index types, the first page of the index
+        * is a metadata page, and we should figure on fetching that plus a
+        * pro-rated fraction of the remaining pages.
+        */
+       if (index->pages > 1 && index->tuples > 0)
+       {
+               numIndexPages = (numIndexTuples / index->tuples) * (index->pages - 1);
+               numIndexPages += 1;             /* count the metapage too */
+               numIndexPages = ceil(numIndexPages);
+       }
+       else
                numIndexPages = 1.0;
 
        /*
index a73bb7b5c8b5b3849af714985fe6b56d7bfc16b7..531fd9e497c957baef77111824d732eda4be5ded 100644 (file)
@@ -22,7 +22,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.213 2001/07/03 20:21:49 petere Exp $
+ *       $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.214 2001/07/16 05:06:59 tgl Exp $
  *
  * Modifications - 6/10/96 - dave@bensoft.com - version 1.13.dhb
  *
@@ -1758,6 +1758,8 @@ clearIndInfo(IndInfo *ind, int numIndexes)
                        free(ind[i].indisunique);
                if (ind[i].indisprimary)
                        free(ind[i].indisprimary);
+               if (ind[i].indpred)
+                       free(ind[i].indpred);
                for (a = 0; a < INDEX_MAX_KEYS; ++a)
                {
                        if (ind[i].indkey[a])
@@ -2887,10 +2889,10 @@ getIndexes(int *numIndexes)
        int                     i_indoid;
        int                     i_oid;
        int                     i_indisprimary;
+       int                     i_indpred;
 
        /*
-        * find all the user-defined indexes. We do not handle partial
-        * indexes.
+        * find all the user-defined indexes.
         *
         * Notice we skip indexes on system classes
         *
@@ -2902,7 +2904,7 @@ getIndexes(int *numIndexes)
        appendPQExpBuffer(query,
                                          "SELECT i.oid, t1.oid as indoid, t1.relname as indexrelname, t2.relname as indrelname, "
                                          "i.indproc, i.indkey, i.indclass, "
-                                 "a.amname as indamname, i.indisunique, i.indisprimary "
+                                 "a.amname as indamname, i.indisunique, i.indisprimary, i.indpred "
                                        "from pg_index i, pg_class t1, pg_class t2, pg_am a "
                                   "WHERE t1.oid = i.indexrelid and t2.oid = i.indrelid "
                                          "and t1.relam = a.oid and i.indexrelid > '%u'::oid "
@@ -2938,6 +2940,7 @@ getIndexes(int *numIndexes)
        i_indclass = PQfnumber(res, "indclass");
        i_indisunique = PQfnumber(res, "indisunique");
        i_indisprimary = PQfnumber(res, "indisprimary");
+       i_indpred = PQfnumber(res, "indpred");
 
        for (i = 0; i < ntups; i++)
        {
@@ -2955,6 +2958,7 @@ getIndexes(int *numIndexes)
                                                  INDEX_MAX_KEYS);
                indinfo[i].indisunique = strdup(PQgetvalue(res, i, i_indisunique));
                indinfo[i].indisprimary = strdup(PQgetvalue(res, i, i_indisprimary));
+               indinfo[i].indpred = strdup(PQgetvalue(res, i, i_indpred));
        }
        PQclear(res);
        return indinfo;
@@ -4435,13 +4439,46 @@ dumpIndexes(Archive *fout, IndInfo *indinfo, int numIndexes,
                        {
                                /* need 2 printf's here cuz fmtId has static return area */
                                appendPQExpBuffer(q, " %s", fmtId(funcname, false));
-                               appendPQExpBuffer(q, " (%s) %s );\n", attlist->data,
+                               appendPQExpBuffer(q, " (%s) %s )", attlist->data,
                                                                  fmtId(classname[0], force_quotes));
                                free(funcname);
                                free(classname[0]);
                        }
                        else
-                               appendPQExpBuffer(q, " %s );\n", attlist->data);
+                               appendPQExpBuffer(q, " %s )", attlist->data);
+                               
+                       if (*indinfo[i].indpred) /* If there is an index predicate */
+                       {
+                               int numRows;
+                               PQExpBuffer pred = createPQExpBuffer();
+                               
+                               appendPQExpBuffer(pred, "SELECT pg_get_expr(indpred,indrelid) as pred FROM pg_index WHERE oid = %s",
+                                                                 indinfo[i].oid);
+                               res = PQexec(g_conn, pred->data);
+                               if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
+                               {
+                                       fprintf(stderr, "dumpIndices(): SELECT (indpred) failed.  "
+                                                       "Explanation from backend: '%s'.\n",
+                                                       PQerrorMessage(g_conn));
+                                       exit_nicely();
+                               }
+       
+                               /* Sanity: Check we got only one tuple */
+                               numRows = PQntuples(res);
+                               if (numRows != 1)
+                               {
+                                       fprintf(stderr, "dumpIndices(): SELECT (indpred) for index %s returned %d tuples. Expected 1.\n",
+                                                       indinfo[i].indrelname, numRows);
+                                       exit_nicely();
+                               }
+       
+                               appendPQExpBuffer(q, " WHERE %s",
+                                                                 PQgetvalue(res, 0, PQfnumber(res, "pred")));
+
+                               PQclear(res);
+                               destroyPQExpBuffer( pred );
+                       }
+                       appendPQExpBuffer(q, ";\n");
 
                        /*
                         * We make the index belong to the owner of its table, which
index a1a3bebd3955f08010640fe8411e587b00c76410..503d10f63dcdc5f32d833b9dd4b4807c70a145d9 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_dump.h,v 1.65 2001/07/03 20:21:50 petere Exp $
+ * $Id: pg_dump.h,v 1.66 2001/07/16 05:06:59 tgl Exp $
  *
  * Modifications - 6/12/96 - dave@bensoft.com - version 1.13.dhb.2
  *
@@ -147,6 +147,7 @@ typedef struct _indInfo
        char       *indclass[INDEX_MAX_KEYS];           /* opclass of the keys */
        char       *indisunique;        /* is this index unique? */
        char       *indisprimary;       /* is this a PK index? */
+       char       *indpred;            /* index predicate */
 } IndInfo;
 
 typedef struct _aggInfo
index f93de9c2e90d3729eb37b9dfeae68e404a940a25..0937cf9d929455dd58221b8565c6bc7e0ea08c73 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: index.h,v 1.36 2001/07/15 22:48:18 tgl Exp $
+ * $Id: index.h,v 1.37 2001/07/16 05:06:59 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,8 +30,6 @@ typedef void (*IndexBuildCallback) (Relation index,
 extern Form_pg_am AccessMethodObjectIdGetForm(Oid accessMethodObjectId,
                                                        MemoryContext resultCxt);
 
-extern void UpdateIndexPredicate(Oid indexoid, Node *oldPred, Node *predicate);
-
 extern void InitIndexStrategy(int numatts,
                                  Relation indexRelation,
                                  Oid accessMethodObjectId);
index f249fcf2d9d9dc958cae1a47b5b376169b0a6a3c..4acbe30e8eeb3f2989bc7b994f011dd53c5bc721 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_proc.h,v 1.197 2001/07/15 22:48:18 tgl Exp $
+ * $Id: pg_proc.h,v 1.198 2001/07/16 05:06:59 tgl Exp $
  *
  * NOTES
  *       The script catalog/genbki.sh reads this file and generates .bki
@@ -2146,6 +2146,9 @@ DATA(insert OID = 1642 (  pg_get_userbyid    PGUID 12 f t f t 1 f 19 "23" 100 0
 DESCR("user name by UID (with fallback)");
 DATA(insert OID = 1643 (  pg_get_indexdef         PGUID 12 f t f t 1 f 25 "26" 100 0 0 100  pg_get_indexdef - ));
 DESCR("index description");
+DATA(insert OID = 1716 (  pg_get_expr             PGUID 12 f t f t 2 f 25 "25 26" 100 0 0 100  pg_get_expr - ));
+DESCR("deparse an encoded expression");
+
 
 /* Generic referential integrity constraint triggers */
 DATA(insert OID = 1644 (  RI_FKey_check_ins            PGUID 12 f t f t 0 f 0 "" 100 0 0 100  RI_FKey_check_ins - ));
index 98ba76613a1809d5380d190eb29aeb88eaed3d90..47230e9144be2d0ca2eee8d3b5b7a14e57520769 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: defrem.h,v 1.22 2001/01/24 19:43:23 momjian Exp $
+ * $Id: defrem.h,v 1.23 2001/07/16 05:07:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,7 +18,7 @@
 #include "tcop/dest.h"
 
 /*
- * prototypes in defind.c
+ * prototypes in indexcmds.c
  */
 extern void DefineIndex(char *heapRelationName,
                        char *indexRelationName,
@@ -29,9 +29,6 @@ extern void DefineIndex(char *heapRelationName,
                        bool primary,
                        Expr *predicate,
                        List *rangetable);
-extern void ExtendIndex(char *indexRelationName,
-                       Expr *predicate,
-                       List *rangetable);
 extern void RemoveIndex(char *name);
 extern void ReindexIndex(const char *indexRelationName, bool force);
 extern void ReindexTable(const char *relationName, bool force);
index 941cfe8a0663501ca1f14e8db608c1fc09c7e556..70e9d6aa92b99a2f3f23a51dfbbed1520a69392d 100644 (file)
@@ -8,7 +8,7 @@
  * or in config.h afterwards.  Of course, if you edit config.h, then your
  * changes will be overwritten the next time you run configure.
  *
- * $Id: config.h.in,v 1.167 2001/07/11 19:03:07 tgl Exp $
+ * $Id: config.h.in,v 1.168 2001/07/16 05:07:00 tgl Exp $
  */
 
 #ifndef CONFIG_H
 /* #define ACLDEBUG */
 /* #define RTDEBUG */
 /* #define GISTDEBUG */
-/* #define OMIT_PARTIAL_INDEX */
 
 /*
  * defining unsafe floats will make float4 and float8 ops faster
index 1dc68c192fee13882693a02bdac43ee545237c5b..4e1bc683d5be5b7ff50926d2d8ce1431180f6d13 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: execnodes.h,v 1.61 2001/06/01 02:41:36 tgl Exp $
+ * $Id: execnodes.h,v 1.62 2001/07/16 05:07:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -34,7 +34,7 @@
  *             NumKeyAttrs                     number of key attributes for this index
  *                                                     (ie, number of attrs from underlying relation)
  *             KeyAttrNumbers          underlying-rel attribute numbers used as keys
- *             Predicate                       partial-index predicate, or NULL if none
+ *             Predicate                       partial-index predicate, or NIL if none
  *             FuncOid                         OID of function, or InvalidOid if not f. index
  *             FuncInfo                        fmgr lookup data for function, if FuncOid valid
  *             Unique                          is it a unique index?
@@ -46,7 +46,7 @@ typedef struct IndexInfo
        int                     ii_NumIndexAttrs;
        int                     ii_NumKeyAttrs;
        AttrNumber      ii_KeyAttrNumbers[INDEX_MAX_KEYS];
-       Node       *ii_Predicate;
+       List       *ii_Predicate;
        Oid                     ii_FuncOid;
        FmgrInfo        ii_FuncInfo;
        bool            ii_Unique;
index efb5c3dcb22cc1a9d81fed646efb8cb52eeefaa6..150acabc0a3a65b9e3ac6d079d9d2611da38a9e9 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: builtins.h,v 1.157 2001/07/11 22:14:03 momjian Exp $
+ * $Id: builtins.h,v 1.158 2001/07/16 05:07:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -337,6 +337,7 @@ extern Datum pg_get_ruledef(PG_FUNCTION_ARGS);
 extern Datum pg_get_viewdef(PG_FUNCTION_ARGS);
 extern Datum pg_get_indexdef(PG_FUNCTION_ARGS);
 extern Datum pg_get_userbyid(PG_FUNCTION_ARGS);
+extern Datum pg_get_expr(PG_FUNCTION_ARGS);
 extern char *deparse_expression(Node *expr, List *dpcontext,
                                   bool forceprefix);
 extern List *deparse_context_for(char *relname, Oid relid);
index d7ee1d3455c41e671437cc67d3bc5fbe3a3beb3e..5792017871b9f24406e1a4a8eaa3a2ce79eb8699 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/keywords.c,v 1.41 2001/06/01 06:23:19 meskes Exp $
+ *       $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/keywords.c,v 1.42 2001/07/16 05:07:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -109,7 +109,6 @@ static ScanKeyword ScanKeywords[] = {
        {"execute", EXECUTE},
        {"exists", EXISTS},
        {"explain", EXPLAIN},
-       {"extend", EXTEND},
        {"extract", EXTRACT},
        {"false", FALSE_P},
        {"fetch", FETCH},
@@ -265,6 +264,7 @@ static ScanKeyword ScanKeywords[] = {
        {"type", TYPE_P},
        {"union", UNION},
        {"unique", UNIQUE},
+       {"unknown", UNKNOWN},
        {"unlisten", UNLISTEN},
        {"until", UNTIL},
        {"update", UPDATE},
index 902b17741219261268b1fd243e9bd995dd6327fd..88330ad3c1de5a9d5af76a53b01ed43e8ca6f059 100644 (file)
@@ -193,7 +193,7 @@ make_name(void)
                 SCHEMA, SCROLL, SECOND_P, SELECT, SESSION, SESSION_USER, SET, SOME, SUBSTRING,
                 TABLE, TEMPORARY, THEN, TIME, TIMESTAMP, TIMEZONE_HOUR,
                TIMEZONE_MINUTE, TO, TRAILING, TRANSACTION, TRIM, TRUE_P,
-                UNION, UNIQUE, UPDATE, USER, USING,
+                UNION, UNIQUE, UNKNOWN, UPDATE, USER, USING,
                 VALUES, VARCHAR, VARYING, VIEW,
                 WHEN, WHERE, WITH, WITHOUT, WORK, YEAR_P, ZONE
 
@@ -217,7 +217,7 @@ make_name(void)
                BACKWARD, BEFORE, BINARY, BIT, CACHE, CHECKPOINT, CLUSTER,
                COMMENT, COPY, CREATEDB, CREATEUSER, CYCLE, DATABASE,
                DELIMITERS, DO, EACH, ENCODING, EXCLUSIVE, EXPLAIN,
-               EXTEND, FORCE, FORWARD, FUNCTION, HANDLER, INCREMENT,
+               FORCE, FORWARD, FUNCTION, HANDLER, INCREMENT,
                INDEX, INHERITS, INSTEAD, ISNULL, LANCOMPILER, LIMIT,
                LISTEN, UNLISTEN, LOAD, LOCATION, LOCK_P, MAXVALUE,
                MINVALUE, MODE, MOVE, NEW, NOCREATEDB, NOCREATEUSER,
@@ -260,7 +260,7 @@ make_name(void)
 %left          Op                              /* multi-character ops and user-defined operators */
 %nonassoc      NOTNULL
 %nonassoc      ISNULL
-%nonassoc      IS NULL_P TRUE_P FALSE_P 
+%nonassoc      IS NULL_P TRUE_P FALSE_P UNKNOWN
 %left          '+' '-'
 %left          '*' '/' '%'
 %left          '^'
@@ -312,7 +312,7 @@ make_name(void)
 %type  <str>   RuleActionStmtOrEmpty RuleActionMulti func_as reindex_type
 %type  <str>    RuleStmt opt_column opt_name oper_argtypes
 %type  <str>    MathOp RemoveFuncStmt aggr_argtype for_update_clause
-%type  <str>    RemoveAggrStmt ExtendStmt opt_procedural select_no_parens
+%type  <str>    RemoveAggrStmt opt_procedural select_no_parens
 %type  <str>    RemoveOperStmt RenameStmt all_Op
 %type  <str>    VariableSetStmt var_value zone_value VariableShowStmt
 %type  <str>    VariableResetStmt AlterTableStmt DropUserStmt from_list
@@ -414,7 +414,6 @@ stmt:  AlterSchemaStmt                      { output_statement($1, 0, NULL, connection); }
                | DropPLangStmt         { output_statement($1, 0, NULL, connection); }
                | DropTrigStmt          { output_statement($1, 0, NULL, connection); }
                | DropUserStmt          { output_statement($1, 0, NULL, connection); }
-               | ExtendStmt            { output_statement($1, 0, NULL, connection); }
                | ExplainStmt           { output_statement($1, 0, NULL, connection); }
                | FetchStmt             { output_statement($1, 1, NULL, connection); }
                | GrantStmt             { output_statement($1, 0, NULL, connection); }
@@ -1801,18 +1800,16 @@ RevokeStmt:  REVOKE privileges ON opt_table relation_name_list FROM grantee_list
  *
  *             QUERY:
  *                             create index <indexname> on <relname>
- *                               using <access> "(" (<col> with <op>)+ ")" [with
- *                               <target_list>]
+ *                               [ using <access> ] "(" (<col> with <op>)+ ")"
+ *                               [ with <parameters> ]
+ *                               [ where <predicate> ]
  *
- *     [where <qual>] is not supported anymore
  *****************************************************************************/
 
 IndexStmt:     CREATE index_opt_unique INDEX index_name ON relation_name
-                       access_method_clause '(' index_params ')' opt_with
+                       access_method_clause '(' index_params ')' opt_with where_clause
                                {
-                                       /* should check that access_method is valid,
-                                          etc ... but doesn't */
-                                       $$ = cat_str(11, make_str("create"), $2, make_str("index"), $4, make_str("on"), $6, $7, make_str("("), $9, make_str(")"), $11);
+                                       $$ = cat_str(12, make_str("create"), $2, make_str("index"), $4, make_str("on"), $6, $7, make_str("("), $9, make_str(")"), $11, $12);
                                }
                ;
 
@@ -1866,19 +1863,6 @@ opt_class:  class                        {
                | /*EMPTY*/                     { $$ = EMPTY; }
                ;
 
-/*****************************************************************************
- *
- *             QUERY:
- *                             extend index <indexname> [where <qual>]
- *
- *****************************************************************************/
-
-ExtendStmt:  EXTEND INDEX index_name where_clause
-                               {
-                                       $$ = cat_str(3, make_str("extend index"), $3, $4);
-                               }
-               ;
-
 
 /*****************************************************************************
  *
@@ -3324,15 +3308,23 @@ a_expr:  c_expr
                 *  but let's make them expressions to allow the optimizer
                 *  a chance to eliminate them if a_expr is a constant string.
                 * - thomas 1997-12-22
+                *
+                *  Created BooleanTest Node type, and changed handling
+                *  for NULL inputs
+                * - jec 2001-06-18
                 */
                | a_expr IS TRUE_P
                                {       $$ = cat2_str($1, make_str("is true")); }
-               | a_expr IS NOT FALSE_P
-                               {       $$ = cat2_str($1, make_str("is not false")); }
-               | a_expr IS FALSE_P
-                               {       $$ = cat2_str($1, make_str("is false")); }
                | a_expr IS NOT TRUE_P
                                {       $$ = cat2_str($1, make_str("is not true")); }
+               | a_expr IS FALSE_P
+                               {       $$ = cat2_str($1, make_str("is false")); }
+               | a_expr IS NOT FALSE_P
+                               {       $$ = cat2_str($1, make_str("is not false")); }
+               | a_expr IS UNKNOWN
+                               {       $$ = cat2_str($1, make_str("is unknown")); }
+               | a_expr IS NOT UNKNOWN
+                               {       $$ = cat2_str($1, make_str("is not unknown")); }
                | a_expr BETWEEN b_expr AND b_expr      %prec BETWEEN
                                {
                                        $$ = cat_str(5, $1, make_str("between"), $3, make_str("and"), $5); 
@@ -5152,7 +5144,6 @@ ECPGColLabel:  ECPGColId  { $$ = $1; }
                | EXCEPT        { $$ = make_str("except"); }
                | EXISTS        { $$ = make_str("exists"); }
                | EXPLAIN       { $$ = make_str("explain"); }
-               | EXTEND        { $$ = make_str("extend"); }
                | EXTRACT       { $$ = make_str("extract"); }
                | FALSE_P       { $$ = make_str("false"); }
                | FOR           { $$ = make_str("for"); }
@@ -5217,6 +5208,7 @@ ECPGColLabel:  ECPGColId  { $$ = $1; }
                | TRIM          { $$ = make_str("trim"); }
                | TRUE_P        { $$ = make_str("true"); }
                | UNIQUE        { $$ = make_str("unique"); }
+               | UNKNOWN       { $$ = make_str("unknown"); }
                | USER          { $$ = make_str("user"); }
                | USING         { $$ = make_str("using"); }
                | VACUUM        { $$ = make_str("vacuum"); }
index 3e727517741142dc5d0cf7e7d9425638b33460d4..06eeeecdd4377425cf83754cbc989e107e316ee9 100644 (file)
@@ -32,16 +32,13 @@ CREATE INDEX bt_txt_index ON bt_txt_heap USING btree (seqno text_ops);
 CREATE INDEX bt_f8_index ON bt_f8_heap USING btree (seqno float8_ops);
 --
 -- BTREE partial indices
--- partial indices are not supported in PostgreSQL
 --
---CREATE INDEX onek2_u1_prtl ON onek2 USING btree(unique1 int4_ops)
---     where onek2.unique1 < 20 or onek2.unique1 > 980;
---CREATE INDEX onek2_u2_prtl ON onek2 USING btree(unique2 int4_ops)
---     where onek2.stringu1 < 'B';
--- EXTEND INDEX onek2_u2_prtl where onek2.stringu1 < 'C';
--- EXTEND INDEX onek2_u2_prtl;
--- CREATE INDEX onek2_stu1_prtl ON onek2 USING btree(stringu1 name_ops)
---     where onek2.stringu1 >= 'J' and onek2.stringu1 < 'K';
+CREATE INDEX onek2_u1_prtl ON onek2 USING btree(unique1 int4_ops)
+       where unique1 < 20 or unique1 > 980;
+CREATE INDEX onek2_u2_prtl ON onek2 USING btree(unique2 int4_ops)
+       where stringu1 < 'B';
+CREATE INDEX onek2_stu1_prtl ON onek2 USING btree(stringu1 name_ops)
+       where onek2.stringu1 >= 'J' and onek2.stringu1 < 'K';
 --
 -- RTREE
 -- 
index 558397f5c9d9afb1c96862ee0e1de5b42c3c8c58..7a9cf6967466552fb7431debabd551a4634b7c37 100644 (file)
@@ -1,7 +1,6 @@
 --
 -- PORTALS_P2
 --
--- EXTEND INDEX onek2_u1_prtl WHERE onek2.unique1 <= 60;
 BEGIN;
 DECLARE foo13 CURSOR FOR 
    SELECT * FROM onek WHERE unique1 = 50;
index 5f5911c09c632f1739587cea276eb46b77dc6b67..ac0e344c747c68026616bff71c5e340cf40e935b 100644 (file)
@@ -29,6 +29,7 @@ SELECT relname, relhasindex
  num_exp_sqrt        | t
  num_exp_sub         | t
  onek                | t
+ onek2               | t
  pg_aggregate        | t
  pg_am               | t
  pg_amop             | t
@@ -57,5 +58,5 @@ SELECT relname, relhasindex
  shighway            | t
  tenk1               | t
  tenk2               | t
-(47 rows)
+(48 rows)
 
index ed6cbac1df06d61e884e60393f32683154d17373..7974d141c54143bc3b88a202fe1d53e2b7a0524b 100644 (file)
@@ -205,24 +205,88 @@ SELECT onek.unique1, onek.string4
 (20 rows)
 
 --
--- partial btree index
+-- test partial btree indexes
+--
+-- As of 7.2, planner probably won't pick an indexscan without stats,
+-- so ANALYZE first.
+--
+ANALYZE onek2;
+--
 -- awk '{if($1<10){print $0;}else{next;}}' onek.data | sort +0n -1
 --
---SELECT onek2.* WHERE onek2.unique1 < 10;
+SELECT onek2.* WHERE onek2.unique1 < 10;
+ unique1 | unique2 | two | four | ten | twenty | hundred | thousand | twothousand | fivethous | tenthous | odd | even | stringu1 | stringu2 | string4 
+---------+---------+-----+------+-----+--------+---------+----------+-------------+-----------+----------+-----+------+----------+----------+---------
+       0 |     998 |   0 |    0 |   0 |      0 |       0 |        0 |           0 |         0 |        0 |   0 |    1 | AAAAAA   | KMBAAA   | OOOOxx
+       1 |     214 |   1 |    1 |   1 |      1 |       1 |        1 |           1 |         1 |        1 |   2 |    3 | BAAAAA   | GIAAAA   | OOOOxx
+       2 |     326 |   0 |    2 |   2 |      2 |       2 |        2 |           2 |         2 |        2 |   4 |    5 | CAAAAA   | OMAAAA   | OOOOxx
+       3 |     431 |   1 |    3 |   3 |      3 |       3 |        3 |           3 |         3 |        3 |   6 |    7 | DAAAAA   | PQAAAA   | VVVVxx
+       4 |     833 |   0 |    0 |   4 |      4 |       4 |        4 |           4 |         4 |        4 |   8 |    9 | EAAAAA   | BGBAAA   | HHHHxx
+       5 |     541 |   1 |    1 |   5 |      5 |       5 |        5 |           5 |         5 |        5 |  10 |   11 | FAAAAA   | VUAAAA   | HHHHxx
+       6 |     978 |   0 |    2 |   6 |      6 |       6 |        6 |           6 |         6 |        6 |  12 |   13 | GAAAAA   | QLBAAA   | OOOOxx
+       7 |     647 |   1 |    3 |   7 |      7 |       7 |        7 |           7 |         7 |        7 |  14 |   15 | HAAAAA   | XYAAAA   | VVVVxx
+       8 |     653 |   0 |    0 |   8 |      8 |       8 |        8 |           8 |         8 |        8 |  16 |   17 | IAAAAA   | DZAAAA   | HHHHxx
+       9 |      49 |   1 |    1 |   9 |      9 |       9 |        9 |           9 |         9 |        9 |  18 |   19 | JAAAAA   | XBAAAA   | HHHHxx
+(10 rows)
+
 --
--- partial btree index
 -- awk '{if($1<20){print $1,$14;}else{next;}}' onek.data | sort +0nr -1
 --
---SELECT onek2.unique1, onek2.stringu1
---    WHERE onek2.unique1 < 20 
---    ORDER BY unique1 using >;
+SELECT onek2.unique1, onek2.stringu1
+    WHERE onek2.unique1 < 20 
+    ORDER BY unique1 using >;
+ unique1 | stringu1 
+---------+----------
+      19 | TAAAAA
+      18 | SAAAAA
+      17 | RAAAAA
+      16 | QAAAAA
+      15 | PAAAAA
+      14 | OAAAAA
+      13 | NAAAAA
+      12 | MAAAAA
+      11 | LAAAAA
+      10 | KAAAAA
+       9 | JAAAAA
+       8 | IAAAAA
+       7 | HAAAAA
+       6 | GAAAAA
+       5 | FAAAAA
+       4 | EAAAAA
+       3 | DAAAAA
+       2 | CAAAAA
+       1 | BAAAAA
+       0 | AAAAAA
+(20 rows)
+
 --
 -- awk '{if($1>980){print $1,$14;}else{next;}}' onek.data | sort +1d -2
 --
---SELECT onek2.unique1, onek2.stringu1
---   WHERE onek2.unique1 > 980
---   ORDER BY stringu1 using <;
-       
+SELECT onek2.unique1, onek2.stringu1
+   WHERE onek2.unique1 > 980;
+ unique1 | stringu1 
+---------+----------
+     981 | TLAAAA
+     982 | ULAAAA
+     983 | VLAAAA
+     984 | WLAAAA
+     985 | XLAAAA
+     986 | YLAAAA
+     987 | ZLAAAA
+     988 | AMAAAA
+     989 | BMAAAA
+     990 | CMAAAA
+     991 | DMAAAA
+     992 | EMAAAA
+     993 | FMAAAA
+     994 | GMAAAA
+     995 | HMAAAA
+     996 | IMAAAA
+     997 | JMAAAA
+     998 | KMAAAA
+     999 | LMAAAA
+(19 rows)
+
 SELECT two, stringu1, ten, string4
    INTO TABLE tmp
    FROM onek;
index 2ebc7ef3c3a08585406b5d1170b8ec0ac73b19b9..888edafe7502507f973d8b4deb5c3f98705d62c4 100644 (file)
@@ -50,20 +50,15 @@ CREATE INDEX bt_f8_index ON bt_f8_heap USING btree (seqno float8_ops);
 
 --
 -- BTREE partial indices
--- partial indices are not supported in PostgreSQL
 --
---CREATE INDEX onek2_u1_prtl ON onek2 USING btree(unique1 int4_ops)
---     where onek2.unique1 < 20 or onek2.unique1 > 980;
+CREATE INDEX onek2_u1_prtl ON onek2 USING btree(unique1 int4_ops)
+       where unique1 < 20 or unique1 > 980;
 
---CREATE INDEX onek2_u2_prtl ON onek2 USING btree(unique2 int4_ops)
---     where onek2.stringu1 < 'B';
+CREATE INDEX onek2_u2_prtl ON onek2 USING btree(unique2 int4_ops)
+       where stringu1 < 'B';
 
--- EXTEND INDEX onek2_u2_prtl where onek2.stringu1 < 'C';
-
--- EXTEND INDEX onek2_u2_prtl;
-
--- CREATE INDEX onek2_stu1_prtl ON onek2 USING btree(stringu1 name_ops)
---     where onek2.stringu1 >= 'J' and onek2.stringu1 < 'K';
+CREATE INDEX onek2_stu1_prtl ON onek2 USING btree(stringu1 name_ops)
+       where onek2.stringu1 >= 'J' and onek2.stringu1 < 'K';
 
 --
 -- RTREE
index c1c92e1cf2b60e8752e2cce69c693905942e096e..12bd903e5f160ed742a3908d6df7711346388ed3 100644 (file)
@@ -2,8 +2,6 @@
 -- PORTALS_P2
 --
 
--- EXTEND INDEX onek2_u1_prtl WHERE onek2.unique1 <= 60;
-
 BEGIN;
 
 DECLARE foo13 CURSOR FOR 
index 42b664eaaee200cc465d70a32817a359474564cd..ee9389dc59706c645735332123687c548bb01b74 100644 (file)
@@ -55,26 +55,32 @@ SELECT onek.unique1, onek.string4
    ORDER BY unique1 using <, string4 using >;
 
 --
--- partial btree index
+-- test partial btree indexes
+--
+-- As of 7.2, planner probably won't pick an indexscan without stats,
+-- so ANALYZE first.
+--
+ANALYZE onek2;
+
+--
 -- awk '{if($1<10){print $0;}else{next;}}' onek.data | sort +0n -1
 --
---SELECT onek2.* WHERE onek2.unique1 < 10;
+SELECT onek2.* WHERE onek2.unique1 < 10;
 
 --
--- partial btree index
 -- awk '{if($1<20){print $1,$14;}else{next;}}' onek.data | sort +0nr -1
 --
---SELECT onek2.unique1, onek2.stringu1
---    WHERE onek2.unique1 < 20 
---    ORDER BY unique1 using >;
+SELECT onek2.unique1, onek2.stringu1
+    WHERE onek2.unique1 < 20 
+    ORDER BY unique1 using >;
 
 --
 -- awk '{if($1>980){print $1,$14;}else{next;}}' onek.data | sort +1d -2
 --
---SELECT onek2.unique1, onek2.stringu1
---   WHERE onek2.unique1 > 980
---   ORDER BY stringu1 using <;
-       
+SELECT onek2.unique1, onek2.stringu1
+   WHERE onek2.unique1 > 980;
+
+
 SELECT two, stringu1, ten, string4
    INTO TABLE tmp
    FROM onek;