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.
-<!-- $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>
</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.
<!--
-$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
-->
</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">
</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>
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term>GIST</term>
+ <listitem>
+ <para>
+ Generalized Index Search Trees.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</para>
</listitem>
</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>
</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.
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.
</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>
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>
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,
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>
* 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 $
*
*-------------------------------------------------------------------------
*/
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;
*
*
* 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
* 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,
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
*
pfree(predString);
}
else
- ii->ii_Predicate = NULL;
+ ii->ii_Predicate = NIL;
/* Other info */
ii->ii_Unique = indexStruct->indisunique;
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;
* 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);
* 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;
}
heap_endscan(scan);
- if (predicate != NULL)
+ if (predicate != NIL)
ExecDropTupleTable(tupleTable, true);
FreeExprContext(econtext);
*
*
* 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
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;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
/* 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,
}
/*
- * 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)
{
* structure
*/
indexInfo = makeNode(IndexInfo);
- indexInfo->ii_Predicate = (Node *) cnfPred;
+ indexInfo->ii_Predicate = cnfPred;
indexInfo->ii_FuncOid = InvalidOid;
indexInfo->ii_Unique = unique;
}
-/*
- * 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");
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
for (i = 0; i < numIndices; i++)
{
IndexInfo *indexInfo;
- Node *predicate;
+ List *predicate;
InsertIndexResult result;
if (relationDescs[i] == NULL)
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;
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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,
* 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)))
{
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;
items = ((Expr *) clause)->args;
foreach(item, items)
{
-
/*
* if any AND item implies the predicate, the whole clause
* does
items = predicate->args;
foreach(item, items)
{
-
/*
* if any item is not implied, the whole predicate is not
* implied
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;
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,
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
heap_endscan(scan);
-
/*
* 2. From the same opclass, find a strategy num for the clause_op
*/
/* 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)
{
/*
* 4. From the same opclass, find the operator for the test strategy
*/
-
ScanKeyEntryInitialize(&entry[2], 0,
Anum_pg_amop_amopstrategy,
F_INT2EQ,
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);
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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;
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
result = transformIndexStmt(pstate, (IndexStmt *) parseTree);
break;
- case T_ExtendStmt:
- result = transformExtendStmt(pstate, (ExtendStmt *) parseTree);
- break;
-
case T_RuleStmt:
result = transformRuleStmt(pstate, (RuleStmt *) parseTree);
break;
transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
{
Query *qry;
+ RangeTblEntry *rte;
qry = makeNode(Query);
qry->commandType = CMD_UTILITY;
/* take care of the where clause */
- stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
-
- qry->hasSubLinks = pstate->p_hasSubLinks;
-
- stmt->rangetable = pstate->p_rtable;
-
- qry->utilityStmt = (Node *) stmt;
-
- return qry;
-}
-
-/*
- * transformExtendStmt -
- * transform the qualifications of the Extend Index Statement
- *
- */
-static Query *
-transformExtendStmt(ParseState *pstate, ExtendStmt *stmt)
-{
- Query *qry;
+ if (stmt->whereClause)
+ {
+ /*
+ * Put the parent table into the rtable so that the WHERE clause can
+ * refer to its fields without qualification. Note that this only
+ * works if the parent table already exists --- so we can't easily
+ * support predicates on indexes created implicitly by CREATE TABLE.
+ * Fortunately, that's not necessary.
+ */
+ rte = addRangeTableEntry(pstate, stmt->relname, NULL, false, true);
- qry = makeNode(Query);
- qry->commandType = CMD_UTILITY;
+ /* no to join list, yes to namespace */
+ addRTEtoQuery(pstate, rte, false, true);
- /* take care of the where clause */
- stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
+ stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
+ }
qry->hasSubLinks = pstate->p_hasSubLinks;
-
stmt->rangetable = pstate->p_rtable;
qry->utilityStmt = (Node *) stmt;
+
return qry;
}
*
*
* 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
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,
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,
| DropPLangStmt
| DropTrigStmt
| DropUserStmt
- | ExtendStmt
| ExplainStmt
| FetchStmt
| GrantStmt
*
* 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;
n->accessMethod = $7;
n->indexParams = $9;
n->withClause = $11;
- n->whereClause = NULL;
+ n->whereClause = $12;
$$ = (Node *)n;
}
;
;
-/*****************************************************************************
- *
- * 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:
| EXCEPT { $$ = "except"; }
| EXISTS { $$ = "exists"; }
| EXPLAIN { $$ = "explain"; }
- | EXTEND { $$ = "extend"; }
| EXTRACT { $$ = "extract"; }
| FALSE_P { $$ = "false"; }
| FLOAT { $$ = "float"; }
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
{"execute", EXECUTE},
{"exists", EXISTS},
{"explain", EXPLAIN},
- {"extend", EXTEND},
{"extract", EXTRACT},
{"false", FALSE_P},
{"fetch", FETCH},
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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;
* 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.
*
#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"
}
+/* ----------
+ * 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)'
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#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"
{
double numIndexTuples;
double numIndexPages;
+ List *selectivityQuals = indexQuals;
- /* Estimate the fraction of main-table tuples that will be visited */
- *indexSelectivity = clauselist_selectivity(root, indexQuals,
- lfirsti(rel->relids));
+ /*
+ * If the index is partial, AND the index predicate with the explicitly
+ * given indexquals to produce a more accurate idea of the index
+ * restriction. This may produce redundant clauses, which we hope that
+ * cnfify and clauselist_selectivity will deal with intelligently.
+ *
+ * Note that index->indpred and indexQuals are both in implicit-AND
+ * form to start with, which we have to make explicit to hand to
+ * canonicalize_qual, and then we get back implicit-AND form again.
+ */
+ if (index->indpred != NIL)
+ {
+ Expr *andedQuals;
- /* Estimate the number of index tuples that will be visited */
- numIndexTuples = *indexSelectivity * index->tuples;
+ andedQuals = make_ands_explicit(nconc(listCopy(index->indpred),
+ indexQuals));
+ selectivityQuals = canonicalize_qual(andedQuals, true);
+ }
- /* Estimate the number of index pages that will be retrieved */
- numIndexPages = *indexSelectivity * index->pages;
+ /* Estimate the fraction of main-table tuples that will be visited */
+ *indexSelectivity = clauselist_selectivity(root, selectivityQuals,
+ lfirsti(rel->relids));
/*
- * Always estimate at least one tuple and page are touched, even when
+ * Estimate the number of tuples that will be visited. We do it in
+ * this rather peculiar-looking way in order to get the right answer
+ * for partial indexes. We can bound the number of tuples by the
+ * index size, in any case.
+ */
+ numIndexTuples = *indexSelectivity * rel->tuples;
+
+ if (numIndexTuples > index->tuples)
+ numIndexTuples = index->tuples;
+
+ /*
+ * Always estimate at least one tuple is touched, even when
* indexSelectivity estimate is tiny.
*/
if (numIndexTuples < 1.0)
numIndexTuples = 1.0;
- if (numIndexPages < 1.0)
+
+ /*
+ * Estimate the number of index pages that will be retrieved.
+ *
+ * For all currently-supported index types, the first page of the index
+ * is a metadata page, and we should figure on fetching that plus a
+ * pro-rated fraction of the remaining pages.
+ */
+ if (index->pages > 1 && index->tuples > 0)
+ {
+ numIndexPages = (numIndexTuples / index->tuples) * (index->pages - 1);
+ numIndexPages += 1; /* count the metapage too */
+ numIndexPages = ceil(numIndexPages);
+ }
+ else
numIndexPages = 1.0;
/*
*
*
* 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
*
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])
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
*
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 "
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++)
{
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;
{
/* 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
* 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
*
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
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
* 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
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 - ));
* 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 $
*
*-------------------------------------------------------------------------
*/
#include "tcop/dest.h"
/*
- * prototypes in defind.c
+ * prototypes in indexcmds.c
*/
extern void DefineIndex(char *heapRelationName,
char *indexRelationName,
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);
* or in config.h afterwards. Of course, if you edit config.h, then your
* changes will be overwritten the next time you run configure.
*
- * $Id: config.h.in,v 1.167 2001/07/11 19:03:07 tgl Exp $
+ * $Id: config.h.in,v 1.168 2001/07/16 05:07:00 tgl Exp $
*/
#ifndef CONFIG_H
/* #define ACLDEBUG */
/* #define RTDEBUG */
/* #define GISTDEBUG */
-/* #define OMIT_PARTIAL_INDEX */
/*
* defining unsafe floats will make float4 and float8 ops faster
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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?
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;
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
{"execute", EXECUTE},
{"exists", EXISTS},
{"explain", EXPLAIN},
- {"extend", EXTEND},
{"extract", EXTRACT},
{"false", FALSE_P},
{"fetch", FETCH},
{"type", TYPE_P},
{"union", UNION},
{"unique", UNIQUE},
+ {"unknown", UNKNOWN},
{"unlisten", UNLISTEN},
{"until", UNTIL},
{"update", UPDATE},
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
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,
%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 '^'
%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
| 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); }
*
* 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);
}
;
| /*EMPTY*/ { $$ = EMPTY; }
;
-/*****************************************************************************
- *
- * QUERY:
- * extend index <indexname> [where <qual>]
- *
- *****************************************************************************/
-
-ExtendStmt: EXTEND INDEX index_name where_clause
- {
- $$ = cat_str(3, make_str("extend index"), $3, $4);
- }
- ;
-
/*****************************************************************************
*
* 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);
| 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"); }
| 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"); }
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
--
--
-- PORTALS_P2
--
--- EXTEND INDEX onek2_u1_prtl WHERE onek2.unique1 <= 60;
BEGIN;
DECLARE foo13 CURSOR FOR
SELECT * FROM onek WHERE unique1 = 50;
num_exp_sqrt | t
num_exp_sub | t
onek | t
+ onek2 | t
pg_aggregate | t
pg_am | t
pg_amop | t
shighway | t
tenk1 | t
tenk2 | t
-(47 rows)
+(48 rows)
(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;
--
-- 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
-- PORTALS_P2
--
--- EXTEND INDEX onek2_u1_prtl WHERE onek2.unique1 <= 60;
-
BEGIN;
DECLARE foo13 CURSOR FOR
ORDER BY unique1 using <, string4 using >;
--
--- partial btree index
+-- test partial btree indexes
+--
+-- As of 7.2, planner probably won't pick an indexscan without stats,
+-- so ANALYZE first.
+--
+ANALYZE onek2;
+
+--
-- awk '{if($1<10){print $0;}else{next;}}' onek.data | sort +0n -1
--
---SELECT onek2.* WHERE onek2.unique1 < 10;
+SELECT onek2.* WHERE onek2.unique1 < 10;
--
--- partial btree index
-- awk '{if($1<20){print $1,$14;}else{next;}}' onek.data | sort +0nr -1
--
---SELECT onek2.unique1, onek2.stringu1
--- WHERE onek2.unique1 < 20
--- ORDER BY unique1 using >;
+SELECT onek2.unique1, onek2.stringu1
+ WHERE onek2.unique1 < 20
+ ORDER BY unique1 using >;
--
-- awk '{if($1>980){print $1,$14;}else{next;}}' onek.data | sort +1d -2
--
---SELECT onek2.unique1, onek2.stringu1
--- WHERE onek2.unique1 > 980
--- ORDER BY stringu1 using <;
-
+SELECT onek2.unique1, onek2.stringu1
+ WHERE onek2.unique1 > 980;
+
+
SELECT two, stringu1, ten, string4
INTO TABLE tmp
FROM onek;