Index: doc/TODO =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/doc/TODO,v retrieving revision 1.511 diff -u -r1.511 TODO --- doc/TODO 2001/07/10 21:36:09 1.511 +++ doc/TODO 2001/07/11 15:01:27 @@ -100,7 +100,7 @@ * Allow CREATE INDEX zman_index ON test (date_trunc( 'day', zman ) datetime_ops) fails index can't store constant parameters * Add FILLFACTOR to index creation -* Re-enable partial indexes +* -Re-enable partial indexes * Allow inherited tables to inherit index, UNIQUE constraint, and primary key, foreign key [inheritance] * UNIQUE INDEX on base column not honored on inserts from inherited table Index: doc/src/sgml/catalogs.sgml =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/doc/src/sgml/catalogs.sgml,v retrieving revision 2.18 diff -u -r2.18 catalogs.sgml --- doc/src/sgml/catalogs.sgml 2001/06/12 05:55:48 2.18 +++ doc/src/sgml/catalogs.sgml 2001/07/11 15:01:27 @@ -1045,7 +1045,7 @@ indpred text - Query plan for partial index predicate (not functional) + Query plan for partial index predicate Index: doc/src/sgml/indices.sgml =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/doc/src/sgml/indices.sgml,v retrieving revision 1.19 diff -u -r1.19 indices.sgml --- doc/src/sgml/indices.sgml 2001/05/30 04:01:11 1.19 +++ doc/src/sgml/indices.sgml 2001/07/11 15:01:28 @@ -606,11 +606,12 @@ Note - Partial indexes are not currently supported by - PostgreSQL, but they were once supported - by its predecessor Postgres, and much - of the code is still there. We hope to revive support for this - feature someday. + For a long time partial indices were not supported by + PostgreSQL, but they were once supported by + its predecessor Postgres, 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. Index: doc/src/sgml/ref/create_index.sgml =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/doc/src/sgml/ref/create_index.sgml,v retrieving revision 1.19 diff -u -r1.19 create_index.sgml --- doc/src/sgml/ref/create_index.sgml 2001/05/17 21:50:18 1.19 +++ doc/src/sgml/ref/create_index.sgml 2001/07/11 15:01:28 @@ -25,8 +25,10 @@ CREATE [ UNIQUE ] INDEX index_name ON table [ USING acc_name ] ( column [ ops_name ] [, ...] ) + [ WHERE expr ] CREATE [ UNIQUE ] INDEX index_name ON table [ USING acc_name ] ( func_name( column [, ... ]) [ ops_name ] ) + [ WHERE expr ] @@ -137,6 +139,15 @@ + + + expr + + + Defines the expression for a partial index. + + + @@ -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). + + + + When the WHERE 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. + + + + The expression used in the WHERE clause is restricted + to forms the planner can easily use. Each element can only consist of + ATTR OP CONST and these can only be joined by + AND and OR operators. Index: src/backend/access/gist/gist.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/access/gist/gist.c,v retrieving revision 1.79 diff -u -r1.79 gist.c --- src/backend/access/gist/gist.c 2001/06/11 05:00:56 1.79 +++ src/backend/access/gist/gist.c 2001/07/11 15:01:28 @@ -217,7 +217,7 @@ */ if (oldPred != NULL) { - slot->val = htup; + ExecStoreTuple( htup, slot, InvalidBuffer, false ); if (ExecQual((List *) oldPred, econtext, false)) { nitups += 1.0; @@ -231,7 +231,7 @@ */ if (pred != NULL) { - slot->val = htup; + ExecStoreTuple( htup, slot, InvalidBuffer, false ); if (!ExecQual((List *) pred, econtext, false)) continue; } Index: src/backend/access/hash/hash.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/access/hash/hash.c,v retrieving revision 1.51 diff -u -r1.51 hash.c --- src/backend/access/hash/hash.c 2001/05/07 00:43:15 1.51 +++ src/backend/access/hash/hash.c 2001/07/11 15:01:28 @@ -128,7 +128,7 @@ */ if (oldPred != NULL) { - slot->val = htup; + ExecStoreTuple( htup, slot, InvalidBuffer, false ); if (ExecQual((List *) oldPred, econtext, false)) { nitups += 1.0; @@ -142,7 +142,7 @@ */ if (pred != NULL) { - slot->val = htup; + ExecStoreTuple( htup, slot, InvalidBuffer, false ); if (!ExecQual((List *) pred, econtext, false)) continue; } Index: src/backend/access/nbtree/nbtree.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/access/nbtree/nbtree.c,v retrieving revision 1.81 diff -u -r1.81 nbtree.c --- src/backend/access/nbtree/nbtree.c 2001/05/18 21:24:17 1.81 +++ src/backend/access/nbtree/nbtree.c 2001/07/11 15:01:29 @@ -202,7 +202,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 += 1.0; @@ -216,7 +217,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; } Index: src/backend/access/rtree/rtree.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/access/rtree/rtree.c,v retrieving revision 1.62 diff -u -r1.62 rtree.c --- src/backend/access/rtree/rtree.c 2001/05/07 00:43:16 1.62 +++ src/backend/access/rtree/rtree.c 2001/07/11 15:01:29 @@ -182,7 +182,7 @@ */ if (oldPred != NULL) { - slot->val = htup; + ExecStoreTuple( htup, slot, InvalidBuffer, false ); if (ExecQual((List *) oldPred, econtext, false)) { nitups += 1.0; @@ -196,7 +196,7 @@ */ if (pred != NULL) { - slot->val = htup; + ExecStoreTuple( htup, slot, InvalidBuffer, false ); if (!ExecQual((List *) pred, econtext, false)) continue; } Index: src/backend/commands/vacuum.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/commands/vacuum.c,v retrieving revision 1.201 diff -u -r1.201 vacuum.c --- src/backend/commands/vacuum.c 2001/07/02 20:50:46 1.201 +++ src/backend/commands/vacuum.c 2001/07/11 15:01:30 @@ -48,6 +48,8 @@ #include "utils/relcache.h" #include "utils/syscache.h" #include "utils/temprel.h" +#include "executor/executor.h" +#include "tcop/pquery.h" #include "pgstat.h" @@ -153,8 +155,6 @@ static void vpage_insert(VacPageList vacpagelist, VacPage vpnew); static void get_indices(Relation relation, int *nindices, Relation **Irel); static void close_indices(int nindices, Relation *Irel); -static IndexInfo **get_index_desc(Relation onerel, int nindices, - Relation *Irel); static void *vac_bsearch(const void *key, const void *base, size_t nelem, size_t size, int (*compar) (const void *, const void *)); @@ -1089,10 +1089,7 @@ HeapTupleData tuple, newtup; TupleDesc tupdesc; - IndexInfo **indexInfo = NULL; - Datum idatum[INDEX_MAX_KEYS]; - char inulls[INDEX_MAX_KEYS]; - InsertIndexResult iresult; + VacPageListData Nvacpagelist; VacPage cur_page = NULL, last_vacuum_page, @@ -1112,6 +1109,11 @@ chain_tuple_moved; VacRUsage ru0; + ResultRelInfo *resultRelInfo; + EState *estate = CreateExecutorState(); /* for ExecInsertTuples() */ + TupleTable tupleTable; + TupleTableSlot *slot; + init_rusage(&ru0); myXID = GetCurrentTransactionId(); @@ -1119,8 +1121,26 @@ tupdesc = RelationGetDescr(onerel); - if (Irel != (Relation *) NULL) /* preparation for index' inserts */ - indexInfo = get_index_desc(onerel, nindices, Irel); + /* + * We need a ResultRelInfo so we can use the regular executor's + * index-entry-making machinery. (There used to be a huge amount of + * code here that basically duplicated execUtils.c ...) + */ + resultRelInfo = makeNode(ResultRelInfo); + resultRelInfo->ri_RangeTableIndex = 1; /* dummy */ + resultRelInfo->ri_RelationDesc = onerel; + /* resultRelInfo->ri_TrigDesc = rel->trigdesc; Don't need this I think */ + + ExecOpenIndices(resultRelInfo); + + estate->es_result_relations = resultRelInfo; + estate->es_num_result_relations = 1; + estate->es_result_relation_info = resultRelInfo; + + /* Set up a dummy tuple table too */ + tupleTable = ExecCreateTupleTable(1); + slot = ExecAllocTableSlot(tupleTable); + ExecSetSlotDescriptor(slot, tupdesc, false); Nvacpagelist.num_pages = 0; num_fraged_pages = fraged_pages->num_pages; @@ -1647,32 +1667,9 @@ if (Irel != (Relation *) NULL) { - - /* - * XXX using CurrentMemoryContext here means - * intra-vacuum memory leak for functional - * indexes. Should fix someday. - * - * XXX This code fails to handle partial indexes! - * Probably should change it to use - * ExecOpenIndices. - */ - for (i = 0; i < nindices; i++) - { - FormIndexDatum(indexInfo[i], - &newtup, - tupdesc, - CurrentMemoryContext, - idatum, - inulls); - iresult = index_insert(Irel[i], - idatum, - inulls, - &newtup.t_self, - onerel); - if (iresult) - pfree(iresult); - } + ExecStoreTuple(&newtup, slot, InvalidBuffer, false); + if (resultRelInfo->ri_NumIndices > 0) + ExecInsertIndexTuples(slot, &(newtup.t_self), estate, false); } WriteBuffer(cur_buffer); WriteBuffer(Cbuf); @@ -1780,32 +1777,12 @@ LockBuffer(buf, BUFFER_LOCK_UNLOCK); /* insert index' tuples if needed */ + if (Irel != (Relation *) NULL) { - - /* - * XXX using CurrentMemoryContext here means intra-vacuum - * memory leak for functional indexes. Should fix someday. - * - * XXX This code fails to handle partial indexes! Probably - * should change it to use ExecOpenIndices. - */ - for (i = 0; i < nindices; i++) - { - FormIndexDatum(indexInfo[i], - &newtup, - tupdesc, - CurrentMemoryContext, - idatum, - inulls); - iresult = index_insert(Irel[i], - idatum, - inulls, - &newtup.t_self, - onerel); - if (iresult) - pfree(iresult); - } + ExecStoreTuple(&newtup, slot, InvalidBuffer, false); + if (resultRelInfo->ri_NumIndices > 0) + ExecInsertIndexTuples(slot, &(newtup.t_self), estate, false); } } /* walk along page */ @@ -2095,11 +2072,9 @@ vacrelstats->rel_pages = blkno; /* set new number of blocks */ } - if (Irel != (Relation *) NULL) /* pfree index' allocations */ - { - close_indices(nindices, Irel); - pfree(indexInfo); - } + ExecDropTupleTable(tupleTable, true); + + ExecCloseIndices(resultRelInfo); pfree(vacpage); if (vacrelstats->vtlinks != NULL) @@ -2649,35 +2624,6 @@ pfree(Irel); } - - -/* - * Obtain IndexInfo data for each index on the rel - */ -static IndexInfo ** -get_index_desc(Relation onerel, int nindices, Relation *Irel) -{ - IndexInfo **indexInfo; - int i; - HeapTuple cachetuple; - - indexInfo = (IndexInfo **) palloc(nindices * sizeof(IndexInfo *)); - - for (i = 0; i < nindices; i++) - { - cachetuple = SearchSysCache(INDEXRELID, - ObjectIdGetDatum(RelationGetRelid(Irel[i])), - 0, 0, 0); - if (!HeapTupleIsValid(cachetuple)) - elog(ERROR, "get_index_desc: index %u not found", - RelationGetRelid(Irel[i])); - indexInfo[i] = BuildIndexInfo(cachetuple); - ReleaseSysCache(cachetuple); - } - - return indexInfo; -} - static bool enough_space(VacPage vacpage, Size len) Index: src/backend/optimizer/path/indxpath.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v retrieving revision 1.108 diff -u -r1.108 indxpath.c --- src/backend/optimizer/path/indxpath.c 2001/06/25 21:11:43 1.108 +++ src/backend/optimizer/path/indxpath.c 2001/07/11 15:01:31 @@ -195,8 +195,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, @@ -1185,6 +1187,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); @@ -1334,7 +1338,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) { Index: src/backend/optimizer/util/pathnode.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v retrieving revision 1.74 diff -u -r1.74 pathnode.c --- src/backend/optimizer/util/pathnode.c 2001/06/05 05:26:04 1.74 +++ src/backend/optimizer/util/pathnode.c 2001/07/11 15:01:31 @@ -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; Index: src/backend/parser/analyze.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/parser/analyze.c,v retrieving revision 1.192 diff -u -r1.192 analyze.c --- src/backend/parser/analyze.c 2001/07/04 17:36:54 1.192 +++ src/backend/parser/analyze.c 2001/07/11 15:01:33 @@ -1631,6 +1631,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; Index: src/backend/parser/gram.y =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/parser/gram.y,v retrieving revision 2.235 diff -u -r2.235 gram.y --- src/backend/parser/gram.y 2001/07/10 22:09:28 2.235 +++ src/backend/parser/gram.y 2001/07/11 15:01:34 @@ -135,7 +135,7 @@ 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, @@ -447,7 +447,7 @@ | DropPLangStmt | DropTrigStmt | DropUserStmt - | ExtendStmt +/* | ExtendStmt */ | ExplainStmt | FetchStmt | GrantStmt @@ -2385,11 +2385,10 @@ * using "(" ( with )+ ")" [with * ] * - * [where ] 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; @@ -2398,7 +2397,7 @@ n->accessMethod = $7; n->indexParams = $9; n->withClause = $11; - n->whereClause = NULL; + n->whereClause = $12; $$ = (Node *)n; } ; @@ -2466,8 +2465,9 @@ * QUERY: * extend index [where ] * + * Removed. No longer supported. (July 2001) *****************************************************************************/ - +/* ExtendStmt: EXTEND INDEX index_name where_clause { ExtendStmt *n = makeNode(ExtendStmt); @@ -2476,7 +2476,7 @@ $$ = (Node *)n; } ; - +*/ /***************************************************************************** * * QUERY: Index: src/backend/utils/adt/ruleutils.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v retrieving revision 1.79 diff -u -r1.79 ruleutils.c --- src/backend/utils/adt/ruleutils.c 2001/07/10 00:02:02 1.79 +++ src/backend/utils/adt/ruleutils.c 2001/07/11 15:01:36 @@ -43,6 +43,7 @@ #include "catalog/pg_index.h" #include "catalog/pg_operator.h" #include "catalog/pg_shadow.h" +#include "catalog/heap.h" #include "commands/view.h" #include "executor/spi.h" #include "lib/stringinfo.h" @@ -551,6 +552,78 @@ elog(ERROR, "get_viewdef: SPI_finish() failed"); PG_RETURN_TEXT_P(indexdef); +} + +/* ---------- + * get_expr - Turn a node expression into a expression + * Used to get the expr used in partial indices + * ---------- + */ +Datum +pg_get_expr(PG_FUNCTION_ARGS) +{ + text *expr = PG_GETARG_TEXT_P(0); + text *relname = PG_GETARG_TEXT_P(1); + + StringInfoData buf; + text *result; + List *list; + List *node; + char *str; + int len; + int relid; + List *context; + + char *pexpr, *prelname; + + /* I'm surprised there's no pre-canned function for this */ + prelname = palloc( VARSIZE( relname ) - VARHDRSZ + 1 ); + bzero( prelname, VARSIZE( relname ) - VARHDRSZ + 1 ); + memcpy( prelname, VARDATA( relname ), VARSIZE( relname ) - VARHDRSZ ); + + /* Get the OID for the given relation */ + relid = RelnameFindRelid( prelname ); + + if( relid == InvalidOid ) + { + pfree( prelname ); + PG_RETURN_NULL(); + } + + context = deparse_context_for( prelname, relid ); + + initStringInfo(&buf); + pexpr = palloc( VARSIZE( expr ) - VARHDRSZ + 1 ); + bzero( pexpr, VARSIZE( expr ) - VARHDRSZ + 1 ); + memcpy( pexpr, VARDATA( expr ), VARSIZE( expr ) - VARHDRSZ ); + list = (List*)stringToNode( pexpr ); + if( list->type != T_List ) /* Result doesn't match what we're looking for? */ + { + pfree( prelname ); + pfree( pexpr ); + PG_RETURN_NULL(); + } + + /* Deparse each expression in the list and AND them together */ + foreach( node, list ) + { + str = deparse_expression( lfirst(node), context, false ); + appendStringInfo( &buf, str ); + if( node->next ) + appendStringInfo( &buf, " AND " ); + } + + /* Pass the result back */ + len = buf.len + VARHDRSZ; + result = palloc(len); + VARATT_SIZEP(result) = len; + memcpy(VARDATA(result), buf.data, buf.len); + + pfree( buf.data ); + pfree( prelname ); + pfree( pexpr ); + + PG_RETURN_TEXT_P(result); } Index: src/backend/utils/adt/selfuncs.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v retrieving revision 1.94 diff -u -r1.94 selfuncs.c --- src/backend/utils/adt/selfuncs.c 2001/06/25 21:11:44 1.94 +++ src/backend/utils/adt/selfuncs.c 2001/07/11 15:01:38 @@ -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,18 +2951,34 @@ { double numIndexTuples; double numIndexPages; + Selectivity thisIndexSelectivity; - /* Estimate the fraction of main-table tuples that will be visited */ - *indexSelectivity = clauselist_selectivity(root, indexQuals, - lfirsti(rel->relids)); - - /* Estimate the number of index tuples that will be visited */ - numIndexTuples = *indexSelectivity * index->tuples; - - /* Estimate the number of index pages that will be retrieved */ - numIndexPages = *indexSelectivity * index->pages; - - /* + /* Create the list of all relevent clauses by including any index predicates */ + /* Both indpred and indexQuals are implicit-AND lists */ + List *selectQuals = cnfify( (Expr *)nconc( listCopy( index->indpred ), indexQuals ), false ); + + /* Estimate the fraction of main-table tuples that will be visited */ + *indexSelectivity = clauselist_selectivity(root, selectQuals, + lfirsti(rel->relids)); + + /* Estimate the fraction of index tuples to be visited (for partial indexes) */ + /* This is a simple way of doing it. Should we call clauselist_selectivity again? */ + thisIndexSelectivity = *indexSelectivity * rel->tuples / index->tuples; + + /* Cap the index selectivity for partial indices */ + if( thisIndexSelectivity > 1 ) + { + thisIndexSelectivity = 1; + *indexSelectivity = (float) index->tuples / (float) rel->tuples; + } + + /* Estimate the number of index tuples that will be visited */ + numIndexTuples = thisIndexSelectivity * index->tuples; + + /* Estimate the number of index pages that will be retrieved */ + numIndexPages = thisIndexSelectivity * index->pages; + + /* * Always estimate at least one tuple and page are touched, even when * indexSelectivity estimate is tiny. */ Index: src/bin/pg_dump/pg_dump.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v retrieving revision 1.213 diff -u -r1.213 pg_dump.c --- src/bin/pg_dump/pg_dump.c 2001/07/03 20:21:49 1.213 +++ src/bin/pg_dump/pg_dump.c 2001/07/11 15:01:40 @@ -1758,6 +1758,8 @@ 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,6 +2889,7 @@ int i_indoid; int i_oid; int i_indisprimary; + int i_indpred; /* * find all the user-defined indexes. We do not handle partial @@ -2902,7 +2905,7 @@ 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 +2941,7 @@ 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 +2959,7 @@ 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 +4440,45 @@ { /* 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(); + + resetPQExpBuffer(pred); + appendPQExpBuffer(pred, "SELECT pg_get_expr('%s','%s') as pred;", + indinfo[i].indpred, + indinfo[i].indrelname); + 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: src/bin/pg_dump/pg_dump.h =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/bin/pg_dump/pg_dump.h,v retrieving revision 1.65 diff -u -r1.65 pg_dump.h --- src/bin/pg_dump/pg_dump.h 2001/07/03 20:21:50 1.65 +++ src/bin/pg_dump/pg_dump.h 2001/07/11 15:01:40 @@ -147,6 +147,7 @@ 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: src/include/catalog/pg_proc.h =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/include/catalog/pg_proc.h,v retrieving revision 1.195 diff -u -r1.195 pg_proc.h --- src/include/catalog/pg_proc.h 2001/06/22 19:16:24 1.195 +++ src/include/catalog/pg_proc.h 2001/07/11 15:01:42 @@ -2147,6 +2147,9 @@ 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 25" 100 0 0 100 pg_get_expr - )); +DESCR("deparse an encoded predicate"); + /* 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: src/include/utils/builtins.h =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/include/utils/builtins.h,v retrieving revision 1.156 diff -u -r1.156 builtins.h --- src/include/utils/builtins.h 2001/06/25 21:11:45 1.156 +++ src/include/utils/builtins.h 2001/07/11 15:01:43 @@ -337,6 +337,7 @@ 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: src/test/regress/sql/create_index.sql =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/test/regress/sql/create_index.sql,v retrieving revision 1.7 diff -u -r1.7 create_index.sql --- src/test/regress/sql/create_index.sql 2000/02/17 03:40:02 1.7 +++ src/test/regress/sql/create_index.sql 2001/07/11 15:01:44 @@ -50,20 +50,15 @@ -- -- 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