Well, getting closer. Maybe I should start version numbering the patches?

Things changed in this version:

* As Tom Lane pointed out, what to do with EXTEND INDEX. I've decided to
  remove it since there is no defined syntax for it and you cannot make an
  index more restrictive with it. Since the names of indicies don't matter,
  you may as well drop the index and make a new one if you want to change
  the predicate.

  At this stage I have only removed it from the grammer because complete
  removal would require many line-by-line changes and would end up with a
  patch much larger than this one. I'd prefer to submit that as a separate
  patch after this one is completed.

* Updates to the documentation as well as comments within the code. If you
  see a spot I missed, let me know.

* Changes to the cost estimation. This is much better than before but still
  needs some refining since index_selectivity != rel_selectivity. Feedback
  welcomed.

Issues still needing to be fixed:

* Make it pg_dump-able. I've tried to extract the expression out the of
  system tables by using stringToNode and deparse_expression but it doesn't
  seem to work. I keep getting the error: "get_names_for_var: bogus
  varlevelsup 0". Anyone know what's going on? See attachment <<expr.c>>.
  
* VACUUM still complains about tuple count mismatch. Anyone have a good idea
  on how to deal with this?

* The regression tests need to be changed to deal with the newly added
  regression tests for these indicies.  

Oh, and it appears I accidently changed the allowed syntax a bit. You used
to have to qualify each field in the partial index predicate with the name
of the relation. That's no longer required.

Everyone still alive out there?
-- 
Martijn van Oosterhout <[EMAIL PROTECTED]>
http://svana.org/kleptog/
> It would be nice if someone came up with a certification system that
> actually separated those who can barely regurgitate what they crammed over
> the last few weeks from those who command secret ninja networking powers.
diff --exclude gram.c -ur postgresql-7.1.orig/doc/TODO postgresql-7.1/doc/TODO
--- postgresql-7.1.orig/doc/TODO        Mon Apr  9 04:11:09 2001
+++ postgresql-7.1/doc/TODO     Sat Jul  7 15:06:55 2001
@@ -144,7 +144,7 @@
   fails index can't store constant parameters
 * -Allow SQL function indexes
 * Add FILLFACTOR to index creation
-* Re-enable partial indexes
+* -Re-enable partial indexes
 * Allow inherited tables to inherit index, UNIQUE constraint, and primary key
   [inheritance]
 * Prevent pg_attribute from having duplicate oids for indexes (Tom)
diff --exclude gram.c -ur postgresql-7.1.orig/doc/src/sgml/catalogs.sgml 
postgresql-7.1/doc/src/sgml/catalogs.sgml
--- postgresql-7.1.orig/doc/src/sgml/catalogs.sgml      Mon Apr  2 05:17:30 2001
+++ postgresql-7.1/doc/src/sgml/catalogs.sgml   Sat Jul  7 15:01:17 2001
@@ -1053,7 +1053,7 @@
       <entry>indpred</entry>
       <entry><type>text</type></entry>
       <entry></entry>
-      <entry>Query plan for partial index predicate (not functional)</entry>
+      <entry>Query plan for partial index predicate</entry>
      </row>
     </tbody>
    </tgroup>
diff --exclude gram.c -ur postgresql-7.1.orig/doc/src/sgml/indices.sgml 
postgresql-7.1/doc/src/sgml/indices.sgml
--- postgresql-7.1.orig/doc/src/sgml/indices.sgml       Sat Feb 24 05:11:55 2001
+++ postgresql-7.1/doc/src/sgml/indices.sgml    Sat Jul  7 15:06:09 2001
@@ -557,11 +557,12 @@
    <note>
     <title>Note</title>
     <para>
-     Partial indices 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.
+     For a long time partial indicies were not supported by
+     <productname>PostgreSQL</productname>, but they were once supported by
+     its predecessor <productname>Postgres</productname>, and much of the
+     code was still there. Currently (July 2001) there is some work underway
+     to revive this feature. See the pgsql-general mailing list archives for
+     details.
     </para>
    </note>
 
diff --exclude gram.c -ur postgresql-7.1.orig/doc/src/sgml/ref/create_index.sgml 
postgresql-7.1/doc/src/sgml/ref/create_index.sgml
--- postgresql-7.1.orig/doc/src/sgml/ref/create_index.sgml      Mon Jan 29 13:53:33 
2001
+++ postgresql-7.1/doc/src/sgml/ref/create_index.sgml   Tue Jul 10 00:34:22 2001
@@ -25,8 +25,10 @@
   <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> ] [, ...] )
+    [ WHERE <replaceable class="parameter">expr</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> ] )
+    [ WHERE <replaceable class="parameter">expr</replaceable> ]
   </synopsis>
 
   <refsect2 id="R2-SQL-CREATEINDEX-1">
@@ -137,6 +139,15 @@
        </para>
       </listitem>
      </varlistentry>
+
+     <varlistentry>
+      <term><replaceable class="parameter">expr</replaceable></term>
+      <listitem>
+       <para>
+       Defines the expression for a partial index.
+       </para>
+      </listitem>
+     </varlistentry>
     </variablelist>
    </para>
   </refsect2>
@@ -225,6 +236,23 @@
    of these access methods are fully dynamic and do not have to be
    optimized periodically (as is the case with, for example, static hash
    access methods).
+  </para>
+
+  <para>
+    When the <command>WHERE</command> clause is present, this defines a
+    partial index. A partial index is an index that only covers 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 order 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 is restricted
+    to forms the planner can easily use. Each element can only consist of
+    <command>ATTR OP CONST</command> and these can only be joined by
+    <command>AND</command> and <command>OR</command> operators.
   </para>
 
   <para>
diff --exclude gram.c -ur postgresql-7.1.orig/src/backend/access/gist/gist.c 
postgresql-7.1/src/backend/access/gist/gist.c
--- postgresql-7.1.orig/src/backend/access/gist/gist.c  Sat Mar 24 11:54:34 2001
+++ postgresql-7.1/src/backend/access/gist/gist.c       Wed Jul  4 22:05:47 2001
@@ -193,7 +193,7 @@
                 */
                if (oldPred != NULL)
                {
-                       slot->val = htup;
+                       ExecStoreTuple( htup, slot, InvalidBuffer, false );
                        if (ExecQual((List *) oldPred, econtext, false))
                        {
                                nitups++;
@@ -207,7 +207,7 @@
                 */
                if (pred != NULL)
                {
-                       slot->val = htup;
+                       ExecStoreTuple( htup, slot, InvalidBuffer, false );
                        if (!ExecQual((List *) pred, econtext, false))
                                continue;
                }
diff --exclude gram.c -ur postgresql-7.1.orig/src/backend/access/hash/hash.c 
postgresql-7.1/src/backend/access/hash/hash.c
--- postgresql-7.1.orig/src/backend/access/hash/hash.c  Sat Mar 24 11:54:34 2001
+++ postgresql-7.1/src/backend/access/hash/hash.c       Wed Jul  4 22:06:10 2001
@@ -128,7 +128,7 @@
                 */
                if (oldPred != NULL)
                {
-                       slot->val = htup;
+                       ExecStoreTuple( htup, slot, InvalidBuffer, false );
                        if (ExecQual((List *) oldPred, econtext, false))
                        {
                                nitups++;
@@ -142,7 +142,7 @@
                 */
                if (pred != NULL)
                {
-                       slot->val = htup;
+                       ExecStoreTuple( htup, slot, InvalidBuffer, false );
                        if (!ExecQual((List *) pred, econtext, false))
                                continue;
                }
diff --exclude gram.c -ur postgresql-7.1.orig/src/backend/access/nbtree/nbtree.c 
postgresql-7.1/src/backend/access/nbtree/nbtree.c
--- postgresql-7.1.orig/src/backend/access/nbtree/nbtree.c      Sat Mar 24 11:54:35 
2001
+++ postgresql-7.1/src/backend/access/nbtree/nbtree.c   Wed Jul  4 21:59:55 2001
@@ -206,7 +206,8 @@
                 */
                if (oldPred != NULL)
                {
-                       slot->val = htup;
+                        /* Invalid buffer should be ok, index shouldn't go away, i 
+hope */
+                       ExecStoreTuple( htup, slot, InvalidBuffer, false );
                        if (ExecQual((List *) oldPred, econtext, false))
                        {
                                nitups++;
@@ -220,7 +221,8 @@
                 */
                if (pred != NULL)
                {
-                       slot->val = htup;
+                        /* Invalid buffer should be ok, index shouldn't go away, i 
+hope */
+                       ExecStoreTuple( htup, slot, InvalidBuffer, false );
                        if (!ExecQual((List *) pred, econtext, false))
                                continue;
                }
diff --exclude gram.c -ur postgresql-7.1.orig/src/backend/access/rtree/rtree.c 
postgresql-7.1/src/backend/access/rtree/rtree.c
--- postgresql-7.1.orig/src/backend/access/rtree/rtree.c        Sat Mar 24 11:54:35 
2001
+++ postgresql-7.1/src/backend/access/rtree/rtree.c     Wed Jul  4 22:07:13 2001
@@ -182,7 +182,7 @@
                 */
                if (oldPred != NULL)
                {
-                       slot->val = htup;
+                       ExecStoreTuple( htup, slot, InvalidBuffer, false );
                        if (ExecQual((List *) oldPred, econtext, false))
                        {
                                nitups++;
@@ -196,7 +196,7 @@
                 */
                if (pred != NULL)
                {
-                       slot->val = htup;
+                       ExecStoreTuple( htup, slot, InvalidBuffer, false );
                        if (!ExecQual((List *) pred, econtext, false))
                                continue;
                }
diff --exclude gram.c -ur postgresql-7.1.orig/src/backend/optimizer/path/indxpath.c 
postgresql-7.1/src/backend/optimizer/path/indxpath.c
--- postgresql-7.1.orig/src/backend/optimizer/path/indxpath.c   Sat Mar 24 11:54:40 
2001
+++ postgresql-7.1/src/backend/optimizer/path/indxpath.c        Wed Jul  4 19:59:53 
+2001
@@ -196,8 +196,10 @@
                 * 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 clause may be 
+useful
                 */
-               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,
@@ -1153,6 +1155,8 @@
        ScanKeyData entry[3];
        Form_pg_amop aform;
 
+       ExprContext *econtext;
+
        pred_var = (Var *) get_leftop(predicate);
        pred_const = (Const *) get_rightop(predicate);
        clause_var = (Var *) get_leftop((Expr *) clause);
@@ -1302,7 +1306,8 @@
                                                          copyObject(clause_const),
                                                          copyObject(pred_const));
 
-       test_result = ExecEvalExpr((Node *) test_expr, NULL, &isNull, NULL);
+       econtext = MakeExprContext(NULL, TransactionCommandContext);
+       test_result = ExecEvalExpr((Node *) test_expr, econtext, &isNull, NULL);
 
        if (isNull)
        {
diff --exclude gram.c -ur postgresql-7.1.orig/src/backend/optimizer/util/pathnode.c 
postgresql-7.1/src/backend/optimizer/util/pathnode.c
--- postgresql-7.1.orig/src/backend/optimizer/util/pathnode.c   Sat Mar 24 11:54:42 
2001
+++ postgresql-7.1/src/backend/optimizer/util/pathnode.c        Wed Jul  4 20:27:42 
+2001
@@ -362,6 +362,11 @@
        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;
diff --exclude gram.c -ur postgresql-7.1.orig/src/backend/parser/analyze.c 
postgresql-7.1/src/backend/parser/analyze.c
--- postgresql-7.1.orig/src/backend/parser/analyze.c    Sat Mar 24 11:54:42 2001
+++ postgresql-7.1/src/backend/parser/analyze.c Wed Jul  4 23:03:16 2001
@@ -1525,6 +1525,12 @@
 {
        Query      *qry;
 
+       /* Add the table to the range table so that the WHERE clause can use the 
+fields */
+       /* no inheritence, yes we can use fields from relation */
+       RangeTblEntry *rte = addRangeTableEntry( pstate, stmt->relname, NULL, false, 
+true );
+       /* no to join list, yes to namespace */
+       addRTEtoQuery( pstate, rte, false, true );
+       
        qry = makeNode(Query);
        qry->commandType = CMD_UTILITY;
 
diff --exclude gram.c -ur postgresql-7.1.orig/src/backend/parser/gram.y 
postgresql-7.1/src/backend/parser/gram.y
--- postgresql-7.1.orig/src/backend/parser/gram.y       Sat Feb 24 05:12:06 2001
+++ postgresql-7.1/src/backend/parser/gram.y    Tue Jul 10 00:20:28 2001
@@ -446,7 +446,7 @@
                | DropPLangStmt
                | DropTrigStmt
                | DropUserStmt
-               | ExtendStmt
+/*             | ExtendStmt */
                | ExplainStmt
                | FetchStmt
                | GrantStmt
@@ -2309,11 +2309,10 @@
  *                               using <access> "(" (<col> with <op>)+ ")" [with
  *                               <target_list>]
  *
- *     [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;
@@ -2322,7 +2321,7 @@
                                        n->accessMethod = $7;
                                        n->indexParams = $9;
                                        n->withClause = $11;
-                                       n->whereClause = NULL;
+                                       n->whereClause = $12;
                                        $$ = (Node *)n;
                                }
                ;
@@ -2390,8 +2389,9 @@
  *             QUERY:
  *                             extend index <indexname> [where <qual>]
  *
+ * Removed. No longer supported. (July 2001)
  *****************************************************************************/
-
+/*
 ExtendStmt:  EXTEND INDEX index_name where_clause
                                {
                                        ExtendStmt *n = makeNode(ExtendStmt);
@@ -2400,7 +2400,7 @@
                                        $$ = (Node *)n;
                                }
                ;
-
+*/
 /*****************************************************************************
  *
  *             QUERY:
diff --exclude gram.c -ur postgresql-7.1.orig/src/backend/utils/adt/selfuncs.c 
postgresql-7.1/src/backend/utils/adt/selfuncs.c
--- postgresql-7.1.orig/src/backend/utils/adt/selfuncs.c        Sat Mar 24 11:54:50 
2001
+++ postgresql-7.1/src/backend/utils/adt/selfuncs.c     Sun Jul  8 11:01:10 2001
@@ -2103,18 +2103,26 @@
        Cost       *indexStartupCost = (Cost *) PG_GETARG_POINTER(4);
        Cost       *indexTotalCost = (Cost *) PG_GETARG_POINTER(5);
        Selectivity *indexSelectivity = (Selectivity *) PG_GETARG_POINTER(6);
+       Selectivity  thisIndexSelectivity;
        double          numIndexTuples;
        double          numIndexPages;
 
+       /* Create the list of all relevent clauses by including any index predicates */
+       List *selectQuals = nconc( listCopy( index->indpred ), indexQuals );
+       
        /* Estimate the fraction of main-table tuples that will be visited */
-       *indexSelectivity = clauselist_selectivity(root, indexQuals,
+       *indexSelectivity = clauselist_selectivity(root, selectQuals,
                                                                                       
    lfirsti(rel->relids));
 
+       /* Estimate the fraction of index tuples to be visited (for partial indicies) 
+*/
+       /* This is a simple way of doing it. Should we call clauselist_selectivity 
+again? */
+       thisIndexSelectivity = *indexSelectivity * rel->tuples / index->tuples;
+       
        /* Estimate the number of index tuples that will be visited */
-       numIndexTuples = *indexSelectivity * index->tuples;
+       numIndexTuples = thisIndexSelectivity * index->tuples;
 
        /* Estimate the number of index pages that will be retrieved */
-       numIndexPages = *indexSelectivity * index->pages;
+       numIndexPages = thisIndexSelectivity * index->pages;
 
        /*
         * Always estimate at least one tuple and page are touched, even when
diff --exclude gram.c -ur postgresql-7.1.orig/src/test/regress/sql/create_index.sql 
postgresql-7.1/src/test/regress/sql/create_index.sql
--- postgresql-7.1.orig/src/test/regress/sql/create_index.sql   Thu Feb 17 14:40:02 
2000
+++ postgresql-7.1/src/test/regress/sql/create_index.sql        Sat Jul  7 15:32:19 
+2001
@@ -50,20 +50,19 @@
 
 --
 -- 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 onek2.unique1 < 20 or onek2.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 onek2.stringu1 < 'B';
 
--- EXTEND INDEX onek2_u2_prtl where onek2.stringu1 < 'C';
+EXTEND INDEX onek2_u2_prtl where onek2.stringu1 < 'C';
 
--- EXTEND INDEX onek2_u2_prtl;
+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
#include "postgres.h"
#include <string.h>
#include "fmgr.h"
#include "lib/stringinfo.h"
#include "nodes/nodes.h"
#include "nodes/pg_list.h"

char *
deparse_expression(Node *expr, List *dpcontext, bool forceprefix);

PG_FUNCTION_INFO_V1(pg_get_expr);

/* ----------
 * get_expr			- Turn a node expression into a expression
 * Used to get the expr used in partial indicies
 * ----------
 */
Datum
pg_get_expr(PG_FUNCTION_ARGS)
{
	text			*expr = PG_GETARG_TEXT_P(0);
	StringInfoData buf;
	text			*result;
	List	*list;
	List	*node;
	char	*str;
	int	len;

	/*
	 * Get the rules definition and put it into executors memory
	 */
	initStringInfo(&buf);
	list = (List*)stringToNode( VARDATA(expr) );
	if( list->type != T_List )
		PG_RETURN_NULL();
		
	foreach( node, list )
	{
		str = deparse_expression( lfirst(node), NULL, true);
		appendStringInfo( &buf, str );
	}
	
	len = buf.len + VARHDRSZ;
	result = palloc(len);
	VARATT_SIZEP(result) = len;
	memcpy(VARDATA(result), buf.data, buf.len);
	pfree(buf.data);

	PG_RETURN_TEXT_P(result);
}


---------------------------(end of broadcast)---------------------------
TIP 4: Don't 'kill -9' the postmaster

Reply via email to