Partial indexes work again, courtesy of Martijn van Oosterhout.

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.
This commit is contained in:
Tom Lane 2001-07-16 05:07:00 +00:00
parent 237e5dfa58
commit f31dc0ada7
32 changed files with 474 additions and 491 deletions

View 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.

View 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>

View 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;

View 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);

View 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;

View 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");
}

View 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;
}

View 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);
}

View 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;

View 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,15 +1625,30 @@ 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);
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);
/* no to join list, yes to namespace */
addRTEtoQuery(pstate, rte, false, true);
stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
}
qry->hasSubLinks = pstate->p_hasSubLinks;
stmt->rangetable = pstate->p_rtable;
qry->utilityStmt = (Node *) stmt;
@ -1646,30 +1656,6 @@ transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
return qry;
}
/*
* transformExtendStmt -
* transform the qualifications of the Extend Index Statement
*
*/
static Query *
transformExtendStmt(ParseState *pstate, ExtendStmt *stmt)
{
Query *qry;
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;
}
/*
* transformRuleStmt -
* transform a Create Rule Statement. The actions is a list of parse

View 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"; }

View 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},

View 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;

View 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)'

View 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;
/* 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;
List *selectivityQuals = indexQuals;
/*
* Always estimate at least one tuple and page are touched, even when
* 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;
andedQuals = make_ands_explicit(nconc(listCopy(index->indpred),
indexQuals));
selectivityQuals = canonicalize_qual(andedQuals, true);
}
/* Estimate the fraction of main-table tuples that will be visited */
*indexSelectivity = clauselist_selectivity(root, selectivityQuals,
lfirsti(rel->relids));
/*
* 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;
/*

View 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

View 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

View 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);

View 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 - ));

View 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);

View 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
@ -310,7 +310,6 @@
/* #define ACLDEBUG */
/* #define RTDEBUG */
/* #define GISTDEBUG */
/* #define OMIT_PARTIAL_INDEX */
/*
* defining unsafe floats will make float4 and float8 ops faster

View 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;

View 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);

View 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},

View 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"); }

View 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
--

View 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;

View 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)

View 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;

View 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

View File

@ -2,8 +2,6 @@
-- PORTALS_P2
--
-- EXTEND INDEX onek2_u1_prtl WHERE onek2.unique1 <= 60;
BEGIN;
DECLARE foo13 CURSOR FOR

View File

@ -55,26 +55,32 @@ SELECT onek.unique1, onek.string4
ORDER BY unique1 using <, string4 using >;
--
-- partial btree index
-- awk '{if($1<10){print $0;}else{next;}}' onek.data | sort +0n -1
-- test partial btree indexes
--
--SELECT onek2.* WHERE onek2.unique1 < 10;
-- 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;
--
-- 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;