Commit fc8d970c authored by Tom Lane's avatar Tom Lane

Replace functional-index facility with expressional indexes. Any column

of an index can now be a computed expression instead of a simple variable.
Restrictions on expressions are the same as for predicates (only immutable
functions, no sub-selects).  This fixes problems recently introduced with
inlining SQL functions, because the inlining transformation is applied to
both expression trees so the planner can still match them up.  Along the
way, improve efficiency of handling index predicates (both predicates and
index expressions are now cached by the relcache) and fix 7.3 oversight
that didn't record dependencies of predicate expressions.
parent e5f19598
...@@ -1492,10 +1492,7 @@ get_pkey_attnames(Oid relid, int16 *numatts) ...@@ -1492,10 +1492,7 @@ get_pkey_attnames(Oid relid, int16 *numatts)
/* we're only interested if it is the primary key */ /* we're only interested if it is the primary key */
if (index->indisprimary == TRUE) if (index->indisprimary == TRUE)
{ {
i = 0; *numatts = index->indnatts;
while (index->indkey[i++] != 0)
(*numatts)++;
if (*numatts > 0) if (*numatts > 0)
{ {
result = (char **) palloc(*numatts * sizeof(char *)); result = (char **) palloc(*numatts * sizeof(char *));
......
<!-- <!--
Documentation of the system catalogs, directed toward PostgreSQL developers Documentation of the system catalogs, directed toward PostgreSQL developers
$Header: /cvsroot/pgsql/doc/src/sgml/catalogs.sgml,v 2.70 2003/05/08 22:19:55 tgl Exp $ $Header: /cvsroot/pgsql/doc/src/sgml/catalogs.sgml,v 2.71 2003/05/28 16:03:55 tgl Exp $
--> -->
<chapter id="catalogs"> <chapter id="catalogs">
...@@ -1933,26 +1933,18 @@ ...@@ -1933,26 +1933,18 @@
<entry>The OID of the <structname>pg_class</> entry for the table this index is for</entry> <entry>The OID of the <structname>pg_class</> entry for the table this index is for</entry>
</row> </row>
<row>
<entry><structfield>indproc</structfield></entry>
<entry><type>regproc</type></entry>
<entry><literal>pg_proc.oid</literal></entry>
<entry>The function's OID if this is a functional index,
else zero</entry>
</row>
<row> <row>
<entry><structfield>indkey</structfield></entry> <entry><structfield>indkey</structfield></entry>
<entry><type>int2vector</type></entry> <entry><type>int2vector</type></entry>
<entry>pg_attribute.attnum</entry> <entry>pg_attribute.attnum</entry>
<entry> <entry>
This is an array of up to This is an array of <structfield>indnatts</structfield> (up to
<symbol>INDEX_MAX_KEYS</symbol> values that indicate which <symbol>INDEX_MAX_KEYS</symbol>) values that indicate which
table columns this index pertains to. For example a value of table columns this index indexes. For example a value of
<literal>1 3</literal> would mean that the first and the third <literal>1 3</literal> would mean that the first and the third table
column make up the index key. For a functional index, these columns make up the index key. A zero in this array indicates that the
columns are the inputs to the function, and the function's return corresponding index attribute is an expression over the table columns,
value is the index key. rather than a simple column reference.
</entry> </entry>
</row> </row>
...@@ -1961,17 +1953,18 @@ ...@@ -1961,17 +1953,18 @@
<entry><type>oidvector</type></entry> <entry><type>oidvector</type></entry>
<entry>pg_opclass.oid</entry> <entry>pg_opclass.oid</entry>
<entry> <entry>
For each column in the index key this contains a reference to For each column in the index key this contains the OID of
the operator class to use. See the operator class to use. See
<structname>pg_opclass</structname> for details. <structname>pg_opclass</structname> for details.
</entry> </entry>
</row> </row>
<row> <row>
<entry><structfield>indisclustered</structfield></entry> <entry><structfield>indnatts</structfield></entry>
<entry><type>bool</type></entry> <entry><type>int2</type></entry>
<entry></entry> <entry></entry>
<entry>If true, the table was last clustered on this index.</entry> <entry>The number of columns in the index (duplicates
<literal>pg_class.relnatts</literal>)</entry>
</row> </row>
<row> <row>
...@@ -1990,19 +1983,28 @@ ...@@ -1990,19 +1983,28 @@
</row> </row>
<row> <row>
<entry><structfield>indreference</structfield></entry> <entry><structfield>indisclustered</structfield></entry>
<entry><type>oid</type></entry> <entry><type>bool</type></entry>
<entry></entry> <entry></entry>
<entry>unused</entry> <entry>If true, the table was last clustered on this index.</entry>
</row>
<row>
<entry><structfield>indexprs</structfield></entry>
<entry><type>text</type></entry>
<entry></entry>
<entry>Expression trees (in <function>nodeToString()</function> representation)
for index attributes that are not simple column references. This is a
list with one element for each zero entry in <structfield>indkey</>.
Null if all index attributes are simple references.</entry>
</row> </row>
<row> <row>
<entry><structfield>indpred</structfield></entry> <entry><structfield>indpred</structfield></entry>
<entry><type>text</type></entry> <entry><type>text</type></entry>
<entry></entry> <entry></entry>
<entry>Expression tree (in the form of a <function>nodeToString()</function> representation) <entry>Expression tree (in <function>nodeToString()</function> representation)
for partial index predicate. Empty string if not a partial for partial index predicate. Null if not a partial index.</entry>
index.</entry>
</row> </row>
</tbody> </tbody>
</tgroup> </tgroup>
......
This diff is collapsed.
<!-- <!--
$Header: /cvsroot/pgsql/doc/src/sgml/plpgsql.sgml,v 1.18 2003/04/27 22:21:22 tgl Exp $ $Header: /cvsroot/pgsql/doc/src/sgml/plpgsql.sgml,v 1.19 2003/05/28 16:03:55 tgl Exp $
--> -->
<chapter id="plpgsql"> <chapter id="plpgsql">
...@@ -136,9 +136,10 @@ END; ...@@ -136,9 +136,10 @@ END;
<para> <para>
Except for input/output conversion and calculation functions Except for input/output conversion and calculation functions
for user-defined types, anything that can be defined in C language for user-defined types, anything that can be defined in C language
functions can also be done with <application>PL/pgSQL</application>. For example, it is possible to functions can also be done with <application>PL/pgSQL</application>.
For example, it is possible to
create complex conditional computation functions and later use create complex conditional computation functions and later use
them to define operators or use them in functional indexes. them to define operators or use them in index expressions.
</para> </para>
<sect2 id="plpgsql-advantages"> <sect2 id="plpgsql-advantages">
......
<!-- <!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_index.sgml,v 1.38 2003/04/22 10:08:08 petere Exp $ $Header: /cvsroot/pgsql/doc/src/sgml/ref/create_index.sgml,v 1.39 2003/05/28 16:03:55 tgl Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
...@@ -16,12 +16,8 @@ PostgreSQL documentation ...@@ -16,12 +16,8 @@ PostgreSQL documentation
<refsynopsisdiv> <refsynopsisdiv>
<synopsis> <synopsis>
CREATE [ UNIQUE ] INDEX <replaceable class="parameter">index_name</replaceable> ON <replaceable class="parameter">table</replaceable> CREATE [ UNIQUE ] INDEX <replaceable class="parameter">index_name</replaceable> ON <replaceable class="parameter">table</replaceable> [ USING <replaceable class="parameter">method</replaceable> ]
[ USING <replaceable class="parameter">method</replaceable> ] ( <replaceable class="parameter">column</replaceable> [ <replaceable class="parameter">ops_name</replaceable> ] [, ...] ) ( { <replaceable class="parameter">column</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ <replaceable class="parameter">opclass</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">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> ] [ WHERE <replaceable class="parameter">predicate</replaceable> ]
</synopsis> </synopsis>
</refsynopsisdiv> </refsynopsisdiv>
...@@ -32,25 +28,22 @@ CREATE [ UNIQUE ] INDEX <replaceable class="parameter">index_name</replaceable> ...@@ -32,25 +28,22 @@ CREATE [ UNIQUE ] INDEX <replaceable class="parameter">index_name</replaceable>
<para> <para>
<command>CREATE INDEX</command> constructs an index <replaceable <command>CREATE INDEX</command> constructs an index <replaceable
class="parameter">index_name</replaceable> on the specified table. class="parameter">index_name</replaceable> on the specified table.
Indexes are primarily used to enhance database performance. But Indexes are primarily used to enhance database performance (though
inappropriate use will result in slower performance. inappropriate use will result in slower performance).
</para> </para>
<para> <para>
In the first syntax shown above, the key field(s) for the The key field(s) for the index are specified as column names,
index are specified as column names. or alternatively as expressions written in parentheses.
Multiple fields can be specified if the index method supports Multiple fields can be specified if the index method supports
multicolumn indexes. multicolumn indexes.
</para> </para>
<para> <para>
In the second syntax shown above, an index is defined on the result An index field can be an expression computed from the values of
of a user-specified function <replaceable one or more columns of the table row. This feature can be used
class="parameter">func_name</replaceable> applied to one or more to obtain fast access to data based on some transformation of
columns of a single table. These <firstterm>functional the basic data. For example, an index computed on
indexes</firstterm> can be used to obtain fast access to data based
on operators that would normally require some transformation to apply
them to the base data. For example, a functional index on
<literal>upper(col)</> would allow the clause <literal>upper(col)</> would allow the clause
<literal>WHERE upper(col) = 'JIM'</> to use an index. <literal>WHERE upper(col) = 'JIM'</> to use an index.
</para> </para>
...@@ -84,6 +77,7 @@ CREATE [ UNIQUE ] INDEX <replaceable class="parameter">index_name</replaceable> ...@@ -84,6 +77,7 @@ CREATE [ UNIQUE ] INDEX <replaceable class="parameter">index_name</replaceable>
only to columns of the underlying table (but it can use all columns, only to columns of the underlying table (but it can use all columns,
not only the one(s) being indexed). Presently, subqueries and not only the one(s) being indexed). Presently, subqueries and
aggregate expressions are also forbidden in <literal>WHERE</literal>. aggregate expressions are also forbidden in <literal>WHERE</literal>.
The same restrictions apply to index fields that are expressions.
</para> </para>
<para> <para>
...@@ -92,8 +86,8 @@ CREATE [ UNIQUE ] INDEX <replaceable class="parameter">index_name</replaceable> ...@@ -92,8 +86,8 @@ CREATE [ UNIQUE ] INDEX <replaceable class="parameter">index_name</replaceable>
their arguments and never on any outside influence (such as their arguments and never on any outside influence (such as
the contents of another table or the current time). This restriction the contents of another table or the current time). This restriction
ensures that the behavior of the index is well-defined. To use a ensures that the behavior of the index is well-defined. To use a
user-defined function in an index, remember to mark the function immutable user-defined function in an index expression or <literal>WHERE</literal>
when you create it. clause, remember to mark the function immutable when you create it.
</para> </para>
</refsect1> </refsect1>
...@@ -156,19 +150,22 @@ CREATE [ UNIQUE ] INDEX <replaceable class="parameter">index_name</replaceable> ...@@ -156,19 +150,22 @@ CREATE [ UNIQUE ] INDEX <replaceable class="parameter">index_name</replaceable>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><replaceable class="parameter">ops_name</replaceable></term> <term><replaceable class="parameter">expression</replaceable></term>
<listitem> <listitem>
<para> <para>
An associated operator class. See below for details. An expression based on one or more columns of the table. The
expression usually must be written with surrounding parentheses,
as shown in the syntax. However, the parentheses may be omitted
if the expression has the form of a function call.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><replaceable class="parameter">func_name</replaceable></term> <term><replaceable class="parameter">opclass</replaceable></term>
<listitem> <listitem>
<para> <para>
A function, which returns a value that can be indexed. The name of an operator class. See below for details.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -177,7 +174,7 @@ CREATE [ UNIQUE ] INDEX <replaceable class="parameter">index_name</replaceable> ...@@ -177,7 +174,7 @@ CREATE [ UNIQUE ] INDEX <replaceable class="parameter">index_name</replaceable>
<term><replaceable class="parameter">predicate</replaceable></term> <term><replaceable class="parameter">predicate</replaceable></term>
<listitem> <listitem>
<para> <para>
Defines the constraint expression for a partial index. The constraint expression for a partial index.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
......
<!-- <!--
$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.191 2003/05/26 18:58:26 tgl Exp $ $Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.192 2003/05/28 16:03:55 tgl Exp $
--> -->
<appendix id="release"> <appendix id="release">
...@@ -24,6 +24,7 @@ CDATA means the content is "SGML-free", so you can write without ...@@ -24,6 +24,7 @@ CDATA means the content is "SGML-free", so you can write without
worries about funny characters. worries about funny characters.
--> -->
<literallayout><![CDATA[ <literallayout><![CDATA[
Functional indexes have been generalized into expressional indexes
CHAR(n) to TEXT conversion automatically strips trailing blanks CHAR(n) to TEXT conversion automatically strips trailing blanks
Pattern matching operations can use indexes regardless of locale Pattern matching operations can use indexes regardless of locale
New frontend/backend protocol supports many long-requested features New frontend/backend protocol supports many long-requested features
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/bootstrap/bootparse.y,v 1.57 2003/05/27 17:49:45 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/bootstrap/bootparse.y,v 1.58 2003/05/28 16:03:55 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -273,7 +273,7 @@ boot_index_param: ...@@ -273,7 +273,7 @@ boot_index_param:
{ {
IndexElem *n = makeNode(IndexElem); IndexElem *n = makeNode(IndexElem);
n->name = LexIDStr($1); n->name = LexIDStr($1);
n->funcname = n->args = NIL; /* no func indexes */ n->expr = NULL;
n->opclass = makeList1(makeString(LexIDStr($2))); n->opclass = makeList1(makeString(LexIDStr($2)));
$$ = n; $$ = n;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.158 2003/05/27 17:49:45 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.159 2003/05/28 16:03:55 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1169,6 +1169,10 @@ index_register(Oid heap, ...@@ -1169,6 +1169,10 @@ index_register(Oid heap,
newind->il_info = (IndexInfo *) palloc(sizeof(IndexInfo)); newind->il_info = (IndexInfo *) palloc(sizeof(IndexInfo));
memcpy(newind->il_info, indexInfo, sizeof(IndexInfo)); memcpy(newind->il_info, indexInfo, sizeof(IndexInfo));
/* expressions will likely be null, but may as well copy it */
newind->il_info->ii_Expressions = (List *)
copyObject(indexInfo->ii_Expressions);
newind->il_info->ii_ExpressionsState = NIL;
/* predicate will likely be null, but may as well copy it */ /* predicate will likely be null, but may as well copy it */
newind->il_info->ii_Predicate = (List *) newind->il_info->ii_Predicate = (List *)
copyObject(indexInfo->ii_Predicate); copyObject(indexInfo->ii_Predicate);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.24 2003/05/27 17:49:45 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.25 2003/05/28 16:03:55 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -850,6 +850,91 @@ recordDependencyOnExpr(const ObjectAddress *depender, ...@@ -850,6 +850,91 @@ recordDependencyOnExpr(const ObjectAddress *depender,
term_object_addresses(&context.addrs); term_object_addresses(&context.addrs);
} }
/*
* recordDependencyOnSingleRelExpr - find expression dependencies
*
* As above, but only one relation is expected to be referenced (with
* varno = 1 and varlevelsup = 0). Pass the relation OID instead of a
* range table. An additional frammish is that dependencies on that
* relation (or its component columns) will be marked with 'self_behavior',
* whereas 'behavior' is used for everything else.
*/
void
recordDependencyOnSingleRelExpr(const ObjectAddress *depender,
Node *expr, Oid relId,
DependencyType behavior,
DependencyType self_behavior)
{
find_expr_references_context context;
RangeTblEntry rte;
init_object_addresses(&context.addrs);
/* We gin up a rather bogus rangetable list to handle Vars */
MemSet(&rte, 0, sizeof(rte));
rte.type = T_RangeTblEntry;
rte.rtekind = RTE_RELATION;
rte.relid = relId;
context.rtables = makeList1(makeList1(&rte));
/* Scan the expression tree for referenceable objects */
find_expr_references_walker(expr, &context);
/* Remove any duplicates */
eliminate_duplicate_dependencies(&context.addrs);
/* Separate self-dependencies if necessary */
if (behavior != self_behavior && context.addrs.numrefs > 0)
{
ObjectAddresses self_addrs;
ObjectAddress *outobj;
int oldref,
outrefs;
init_object_addresses(&self_addrs);
outobj = context.addrs.refs;
outrefs = 0;
for (oldref = 0; oldref < context.addrs.numrefs; oldref++)
{
ObjectAddress *thisobj = context.addrs.refs + oldref;
if (thisobj->classId == RelOid_pg_class &&
thisobj->objectId == relId)
{
/* Move this ref into self_addrs */
add_object_address(OCLASS_CLASS, relId, thisobj->objectSubId,
&self_addrs);
}
else
{
/* Keep it in context.addrs */
outobj->classId = thisobj->classId;
outobj->objectId = thisobj->objectId;
outobj->objectSubId = thisobj->objectSubId;
outobj++;
outrefs++;
}
}
context.addrs.numrefs = outrefs;
/* Record the self-dependencies */
recordMultipleDependencies(depender,
self_addrs.refs, self_addrs.numrefs,
self_behavior);
term_object_addresses(&self_addrs);
}
/* Record the external dependencies */
recordMultipleDependencies(depender,
context.addrs.refs, context.addrs.numrefs,
behavior);
term_object_addresses(&context.addrs);
}
/* /*
* Recursively search an expression tree for object references. * Recursively search an expression tree for object references.
* *
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.244 2003/05/12 00:17:02 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.245 2003/05/28 16:03:55 tgl Exp $
* *
* *
* INTERFACE ROUTINES * INTERFACE ROUTINES
...@@ -1885,48 +1885,31 @@ RemoveStatistics(Relation rel, AttrNumber attnum) ...@@ -1885,48 +1885,31 @@ RemoveStatistics(Relation rel, AttrNumber attnum)
static void static void
RelationTruncateIndexes(Oid heapId) RelationTruncateIndexes(Oid heapId)
{ {
Relation indexRelation; Relation heapRelation;
ScanKeyData entry; List *indlist;
SysScanDesc scan;
HeapTuple indexTuple;
/* Scan pg_index to find indexes on specified heap */
indexRelation = heap_openr(IndexRelationName, AccessShareLock);
ScanKeyEntryInitialize(&entry, 0,
Anum_pg_index_indrelid,
F_OIDEQ,
ObjectIdGetDatum(heapId));
scan = systable_beginscan(indexRelation, IndexIndrelidIndex, true,
SnapshotNow, 1, &entry);
while (HeapTupleIsValid(indexTuple = systable_getnext(scan)))
{
Form_pg_index indexform = (Form_pg_index) GETSTRUCT(indexTuple);
Oid indexId;
IndexInfo *indexInfo;
Relation heapRelation,
currentIndex;
/*
* For each index, fetch info needed for index_build
*/
indexId = indexform->indexrelid;
indexInfo = BuildIndexInfo(indexform);
/* /*
* We have to re-open the heap rel each time through this loop * Open the heap rel. We need grab no lock because we assume
* because index_build will close it again. We need grab no lock, * heap_truncate is holding an exclusive lock on the heap rel.
* however, because we assume heap_truncate is holding an
* exclusive lock on the heap rel.
*/ */
heapRelation = heap_open(heapId, NoLock); heapRelation = heap_open(heapId, NoLock);
/* Ask the relcache to produce a list of the indexes of the rel */
foreach(indlist, RelationGetIndexList(heapRelation))
{
Oid indexId = lfirsto(indlist);
Relation currentIndex;
IndexInfo *indexInfo;
/* Open the index relation */ /* Open the index relation */
currentIndex = index_open(indexId); currentIndex = index_open(indexId);
/* Obtain exclusive lock on it, just to be sure */ /* Obtain exclusive lock on it, just to be sure */
LockRelation(currentIndex, AccessExclusiveLock); LockRelation(currentIndex, AccessExclusiveLock);
/* Fetch info needed for index_build */
indexInfo = BuildIndexInfo(currentIndex);
/* /*
* Drop any buffers associated with this index. If they're dirty, * Drop any buffers associated with this index. If they're dirty,
* they're just dropped without bothering to flush to disk. * they're just dropped without bothering to flush to disk.
...@@ -1943,13 +1926,14 @@ RelationTruncateIndexes(Oid heapId) ...@@ -1943,13 +1926,14 @@ RelationTruncateIndexes(Oid heapId)
/* /*
* index_build will close both the heap and index relations (but * index_build will close both the heap and index relations (but
* not give up the locks we hold on them). * not give up the locks we hold on them). We're done with this
* index, but we must re-open the heap rel.
*/ */
heapRelation = heap_open(heapId, NoLock);
} }
/* Complete the scan and close pg_index */ /* Finish by closing the heap rel again */
systable_endscan(scan); heap_close(heapRelation, NoLock);
heap_close(indexRelation, AccessShareLock);
} }
/* /*
......
This diff is collapsed.
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/indexing.c,v 1.102 2002/09/04 20:31:14 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/catalog/indexing.c,v 1.103 2003/05/28 16:03:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -30,11 +30,9 @@ ...@@ -30,11 +30,9 @@
* In the current implementation, we share code for opening/closing the * In the current implementation, we share code for opening/closing the
* indexes with execUtils.c. But we do not use ExecInsertIndexTuples, * indexes with execUtils.c. But we do not use ExecInsertIndexTuples,
* because we don't want to create an EState. This implies that we * because we don't want to create an EState. This implies that we
* do not support partial indexes on system catalogs. Nor do we handle * do not support partial or expressional indexes on system catalogs.
* functional indexes very well (the code will work, but will leak memory * This could be fixed with localized changes here if we wanted to pay
* intraquery, because the index function is called in the per-query context * the extra overhead of building an EState.
* that we are invoked in). This could be fixed with localized changes here
* if we wanted to pay the extra overhead of building an EState.
*/ */
CatalogIndexState CatalogIndexState
CatalogOpenIndexes(Relation heapRel) CatalogOpenIndexes(Relation heapRel)
...@@ -99,7 +97,11 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple) ...@@ -99,7 +97,11 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple)
indexInfo = indexInfoArray[i]; indexInfo = indexInfoArray[i];
/* Partial indexes on system catalogs are not supported */ /*
* Expressional and partial indexes on system catalogs are not
* supported
*/
Assert(indexInfo->ii_Expressions == NIL);
Assert(indexInfo->ii_Predicate == NIL); Assert(indexInfo->ii_Predicate == NIL);
/* /*
...@@ -109,7 +111,7 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple) ...@@ -109,7 +111,7 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple)
FormIndexDatum(indexInfo, FormIndexDatum(indexInfo,
heapTuple, heapTuple,
heapDescriptor, heapDescriptor,
CurrentMemoryContext, NULL, /* no expression eval to do */
datum, datum,
nullv); nullv);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_constraint.c,v 1.12 2002/12/12 20:35:11 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/catalog/pg_constraint.c,v 1.13 2003/05/28 16:03:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -250,17 +250,10 @@ CreateConstraintEntry(const char *constraintName, ...@@ -250,17 +250,10 @@ CreateConstraintEntry(const char *constraintName,
{ {
/* /*
* Register dependencies from constraint to objects mentioned in * Register dependencies from constraint to objects mentioned in
* CHECK expression. We gin up a rather bogus rangetable list to * CHECK expression.
* handle any Vars in the constraint.
*/ */
RangeTblEntry rte; recordDependencyOnSingleRelExpr(&conobject, conExpr, relId,
DEPENDENCY_NORMAL,
MemSet(&rte, 0, sizeof(rte));
rte.type = T_RangeTblEntry;
rte.rtekind = RTE_RELATION;
rte.relid = relId;
recordDependencyOnExpr(&conobject, conExpr, makeList1(&rte),
DEPENDENCY_NORMAL); DEPENDENCY_NORMAL);
} }
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.109 2003/05/14 03:26:01 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.110 2003/05/28 16:03:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -214,7 +214,7 @@ cluster(ClusterStmt *stmt) ...@@ -214,7 +214,7 @@ cluster(ClusterStmt *stmt)
/* Start a new transaction for each relation. */ /* Start a new transaction for each relation. */
StartTransactionCommand(); StartTransactionCommand();
SetQuerySnapshot(); /* might be needed for functional index */ SetQuerySnapshot(); /* might be needed for functions in indexes */
cluster_rel(rvtc, true); cluster_rel(rvtc, true);
CommitTransactionCommand(); CommitTransactionCommand();
} }
...@@ -320,7 +320,7 @@ cluster_rel(RelToCluster *rvtc, bool recheck) ...@@ -320,7 +320,7 @@ cluster_rel(RelToCluster *rvtc, bool recheck)
* seqscan pass over the table to copy the missing rows, but that seems * seqscan pass over the table to copy the missing rows, but that seems
* expensive and tedious. * expensive and tedious.
*/ */
if (VARSIZE(&OldIndex->rd_index->indpred) > VARHDRSZ) /* partial? */ if (!heap_attisnull(OldIndex->rd_indextuple, Anum_pg_index_indpred))
elog(ERROR, "CLUSTER: cannot cluster on partial index"); elog(ERROR, "CLUSTER: cannot cluster on partial index");
if (!OldIndex->rd_am->amindexnulls) if (!OldIndex->rd_am->amindexnulls)
{ {
...@@ -332,15 +332,25 @@ cluster_rel(RelToCluster *rvtc, bool recheck) ...@@ -332,15 +332,25 @@ cluster_rel(RelToCluster *rvtc, bool recheck)
* at the first column; multicolumn-capable AMs are *required* to * at the first column; multicolumn-capable AMs are *required* to
* index nulls in columns after the first. * index nulls in columns after the first.
*/ */
if (OidIsValid(OldIndex->rd_index->indproc))
elog(ERROR, "CLUSTER: cannot cluster on functional index when index access method does not handle nulls");
colno = OldIndex->rd_index->indkey[0]; colno = OldIndex->rd_index->indkey[0];
if (colno > 0) /* system columns are non-null */ if (colno > 0)
{
/* ordinary user attribute */
if (!OldHeap->rd_att->attrs[colno - 1]->attnotnull) if (!OldHeap->rd_att->attrs[colno - 1]->attnotnull)
elog(ERROR, "CLUSTER: cannot cluster when index access method does not handle nulls" elog(ERROR, "CLUSTER: cannot cluster when index access method does not handle nulls"
"\n\tYou may be able to work around this by marking column \"%s\" NOT NULL", "\n\tYou may be able to work around this by marking column \"%s\" NOT NULL",
NameStr(OldHeap->rd_att->attrs[colno - 1]->attname)); NameStr(OldHeap->rd_att->attrs[colno - 1]->attname));
} }
else if (colno < 0)
{
/* system column --- okay, always non-null */
}
else
{
/* index expression, lose... */
elog(ERROR, "CLUSTER: cannot cluster on expressional index when index access method does not handle nulls");
}
}
/* /*
* Disallow clustering system relations. This will definitely NOT * Disallow clustering system relations. This will definitely NOT
...@@ -557,43 +567,24 @@ get_indexattr_list(Relation OldHeap, Oid OldIndex) ...@@ -557,43 +567,24 @@ get_indexattr_list(Relation OldHeap, Oid OldIndex)
foreach(indlist, RelationGetIndexList(OldHeap)) foreach(indlist, RelationGetIndexList(OldHeap))
{ {
Oid indexOID = lfirsto(indlist); Oid indexOID = lfirsto(indlist);
HeapTuple indexTuple; Relation oldIndex;
HeapTuple classTuple;
Form_pg_index indexForm;
Form_pg_class classForm;
IndexAttrs *attrs; IndexAttrs *attrs;
indexTuple = SearchSysCache(INDEXRELID, oldIndex = index_open(indexOID);
ObjectIdGetDatum(indexOID),
0, 0, 0);
if (!HeapTupleIsValid(indexTuple))
elog(ERROR, "Cache lookup failed for index %u", indexOID);
indexForm = (Form_pg_index) GETSTRUCT(indexTuple);
Assert(indexForm->indexrelid == indexOID);
attrs = (IndexAttrs *) palloc(sizeof(IndexAttrs)); attrs = (IndexAttrs *) palloc(sizeof(IndexAttrs));
attrs->indexOID = indexOID; attrs->indexOID = indexOID;
attrs->indexInfo = BuildIndexInfo(indexForm); attrs->indexName = pstrdup(NameStr(oldIndex->rd_rel->relname));
attrs->accessMethodOID = oldIndex->rd_rel->relam;
attrs->indexInfo = BuildIndexInfo(oldIndex);
attrs->classOID = (Oid *) attrs->classOID = (Oid *)
palloc(sizeof(Oid) * attrs->indexInfo->ii_NumIndexAttrs); palloc(sizeof(Oid) * attrs->indexInfo->ii_NumIndexAttrs);
memcpy(attrs->classOID, indexForm->indclass, memcpy(attrs->classOID, oldIndex->rd_index->indclass,
sizeof(Oid) * attrs->indexInfo->ii_NumIndexAttrs); sizeof(Oid) * attrs->indexInfo->ii_NumIndexAttrs);
/* We adjust the isclustered attribute to correct new state */ /* We adjust the isclustered attribute to correct new state */
attrs->isclustered = (indexOID == OldIndex); attrs->isclustered = (indexOID == OldIndex);
/* Name and access method of each index come from pg_class */ index_close(oldIndex);
classTuple = SearchSysCache(RELOID,
ObjectIdGetDatum(indexOID),
0, 0, 0);
if (!HeapTupleIsValid(classTuple))
elog(ERROR, "Cache lookup failed for index %u", indexOID);
classForm = (Form_pg_class) GETSTRUCT(classTuple);
attrs->indexName = pstrdup(NameStr(classForm->relname));
attrs->accessMethodOID = classForm->relam;
ReleaseSysCache(classTuple);
ReleaseSysCache(indexTuple);
/* /*
* Cons the gathered data into the list. We do not care about * Cons the gathered data into the list. We do not care about
......
This diff is collapsed.
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.72 2003/04/29 22:13:08 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.73 2003/05/28 16:03:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1103,6 +1103,7 @@ renameatt(Oid myrelid, ...@@ -1103,6 +1103,7 @@ renameatt(Oid myrelid,
Relation attrelation; Relation attrelation;
HeapTuple atttup; HeapTuple atttup;
Form_pg_attribute attform; Form_pg_attribute attform;
int attnum;
List *indexoidlist; List *indexoidlist;
List *indexoidscan; List *indexoidscan;
...@@ -1178,7 +1179,8 @@ renameatt(Oid myrelid, ...@@ -1178,7 +1179,8 @@ renameatt(Oid myrelid,
oldattname); oldattname);
attform = (Form_pg_attribute) GETSTRUCT(atttup); attform = (Form_pg_attribute) GETSTRUCT(atttup);
if (attform->attnum < 0) attnum = attform->attnum;
if (attnum < 0)
elog(ERROR, "renameatt: system attribute \"%s\" may not be renamed", elog(ERROR, "renameatt: system attribute \"%s\" may not be renamed",
oldattname); oldattname);
...@@ -1217,31 +1219,33 @@ renameatt(Oid myrelid, ...@@ -1217,31 +1219,33 @@ renameatt(Oid myrelid,
{ {
Oid indexoid = lfirsto(indexoidscan); Oid indexoid = lfirsto(indexoidscan);
HeapTuple indextup; HeapTuple indextup;
Form_pg_index indexform;
int i;
/* /*
* First check to see if index is a functional index. If so, its * Scan through index columns to see if there's any simple index
* column name is a function name and shouldn't be renamed here. * entries for this attribute. We ignore expressional entries.
*/ */
indextup = SearchSysCache(INDEXRELID, indextup = SearchSysCache(INDEXRELID,
ObjectIdGetDatum(indexoid), ObjectIdGetDatum(indexoid),
0, 0, 0); 0, 0, 0);
if (!HeapTupleIsValid(indextup)) if (!HeapTupleIsValid(indextup))
elog(ERROR, "renameatt: can't find index id %u", indexoid); elog(ERROR, "renameatt: can't find index id %u", indexoid);
if (OidIsValid(((Form_pg_index) GETSTRUCT(indextup))->indproc)) indexform = (Form_pg_index) GETSTRUCT(indextup);
for (i = 0; i < indexform->indnatts; i++)
{ {
ReleaseSysCache(indextup); if (attnum != indexform->indkey[i])
continue; continue;
}
ReleaseSysCache(indextup);
/* /*
* Okay, look to see if any column name of the index matches the * Found one, rename it.
* old attribute name.
*/ */
atttup = SearchSysCacheCopyAttName(indexoid, oldattname); atttup = SearchSysCacheCopy(ATTNUM,
ObjectIdGetDatum(indexoid),
Int16GetDatum(i + 1),
0, 0);
if (!HeapTupleIsValid(atttup)) if (!HeapTupleIsValid(atttup))
continue; /* Nope, so ignore it */ continue; /* should we raise an error? */
/* /*
* Update the (copied) attribute tuple. * Update the (copied) attribute tuple.
*/ */
...@@ -1256,6 +1260,9 @@ renameatt(Oid myrelid, ...@@ -1256,6 +1260,9 @@ renameatt(Oid myrelid,
heap_freetuple(atttup); heap_freetuple(atttup);
} }
ReleaseSysCache(indextup);
}
freeList(indexoidlist); freeList(indexoidlist);
heap_close(attrelation, RowExclusiveLock); heap_close(attrelation, RowExclusiveLock);
...@@ -1986,8 +1993,7 @@ AlterTableAlterColumnDropNotNull(Oid myrelid, bool recurse, ...@@ -1986,8 +1993,7 @@ AlterTableAlterColumnDropNotNull(Oid myrelid, bool recurse,
* Loop over each attribute in the primary key and see if it * Loop over each attribute in the primary key and see if it
* matches the to-be-altered attribute * matches the to-be-altered attribute
*/ */
for (i = 0; i < INDEX_MAX_KEYS && for (i = 0; i < indexStruct->indnatts; i++)
indexStruct->indkey[i] != InvalidAttrNumber; i++)
{ {
if (indexStruct->indkey[i] == attnum) if (indexStruct->indkey[i] == attnum)
elog(ERROR, "ALTER TABLE: Attribute \"%s\" is in a primary key", colName); elog(ERROR, "ALTER TABLE: Attribute \"%s\" is in a primary key", colName);
...@@ -3185,9 +3191,10 @@ transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid, ...@@ -3185,9 +3191,10 @@ transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
/* /*
* Now build the list of PK attributes from the indkey definition * Now build the list of PK attributes from the indkey definition
* (we assume a primary key cannot have expressional elements)
*/ */
*attnamelist = NIL; *attnamelist = NIL;
for (i = 0; i < INDEX_MAX_KEYS && indexStruct->indkey[i] != 0; i++) for (i = 0; i < indexStruct->indnatts; i++)
{ {
int pkattno = indexStruct->indkey[i]; int pkattno = indexStruct->indkey[i];
...@@ -3241,15 +3248,13 @@ transformFkeyCheckAttrs(Relation pkrel, ...@@ -3241,15 +3248,13 @@ transformFkeyCheckAttrs(Relation pkrel,
indexStruct = (Form_pg_index) GETSTRUCT(indexTuple); indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
/* /*
* Must be unique, not a functional index, and not a partial index * Must have the right number of columns; must be unique and not a
* partial index; forget it if there are any expressions, too
*/ */
if (indexStruct->indisunique && if (indexStruct->indnatts == numattrs &&
indexStruct->indproc == InvalidOid && indexStruct->indisunique &&
VARSIZE(&indexStruct->indpred) <= VARHDRSZ) heap_attisnull(indexTuple, Anum_pg_index_indpred) &&
{ heap_attisnull(indexTuple, Anum_pg_index_indexprs))
for (i = 0; i < INDEX_MAX_KEYS && indexStruct->indkey[i] != 0; i++)
;
if (i == numattrs)
{ {
/* /*
* The given attnum list may match the index columns in any * The given attnum list may match the index columns in any
...@@ -3287,7 +3292,6 @@ transformFkeyCheckAttrs(Relation pkrel, ...@@ -3287,7 +3292,6 @@ transformFkeyCheckAttrs(Relation pkrel,
} }
} }
} }
}
ReleaseSysCache(indexTuple); ReleaseSysCache(indexTuple);
if (found) if (found)
break; break;
...@@ -4020,12 +4024,12 @@ AlterTableCreateToastTable(Oid relOid, bool silent) ...@@ -4020,12 +4024,12 @@ AlterTableCreateToastTable(Oid relOid, bool silent)
indexInfo = makeNode(IndexInfo); indexInfo = makeNode(IndexInfo);
indexInfo->ii_NumIndexAttrs = 2; indexInfo->ii_NumIndexAttrs = 2;
indexInfo->ii_NumKeyAttrs = 2;
indexInfo->ii_KeyAttrNumbers[0] = 1; indexInfo->ii_KeyAttrNumbers[0] = 1;
indexInfo->ii_KeyAttrNumbers[1] = 2; indexInfo->ii_KeyAttrNumbers[1] = 2;
indexInfo->ii_Expressions = NIL;
indexInfo->ii_ExpressionsState = NIL;
indexInfo->ii_Predicate = NIL; indexInfo->ii_Predicate = NIL;
indexInfo->ii_PredicateState = NIL; indexInfo->ii_PredicateState = NIL;
indexInfo->ii_FuncOid = InvalidOid;
indexInfo->ii_Unique = true; indexInfo->ii_Unique = true;
classObjectId[0] = OID_BTREE_OPS_OID; classObjectId[0] = OID_BTREE_OPS_OID;
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.254 2003/05/27 17:49:45 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.255 2003/05/28 16:03:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -304,7 +304,7 @@ vacuum(VacuumStmt *vacstmt) ...@@ -304,7 +304,7 @@ vacuum(VacuumStmt *vacstmt)
if (vacstmt->vacuum) if (vacstmt->vacuum)
{ {
StartTransactionCommand(); StartTransactionCommand();
SetQuerySnapshot(); /* might be needed for functional index */ SetQuerySnapshot(); /* might be needed for functions in indexes */
} }
else else
old_context = MemoryContextSwitchTo(anl_context); old_context = MemoryContextSwitchTo(anl_context);
...@@ -728,7 +728,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind) ...@@ -728,7 +728,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind)
/* Begin a transaction for vacuuming this relation */ /* Begin a transaction for vacuuming this relation */
StartTransactionCommand(); StartTransactionCommand();
SetQuerySnapshot(); /* might be needed for functional index */ SetQuerySnapshot(); /* might be needed for functions in indexes */
/* /*
* Check for user-requested abort. Note we want this to be inside a * Check for user-requested abort. Note we want this to be inside a
...@@ -3028,7 +3028,10 @@ vac_is_partial_index(Relation indrel) ...@@ -3028,7 +3028,10 @@ vac_is_partial_index(Relation indrel)
return true; return true;
/* Otherwise, look to see if there's a partial-index predicate */ /* Otherwise, look to see if there's a partial-index predicate */
return (VARSIZE(&indrel->rd_index->indpred) > VARHDRSZ); if (!heap_attisnull(indrel->rd_indextuple, Anum_pg_index_indpred))
return true;
return false;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.99 2003/05/05 17:57:47 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.100 2003/05/28 16:03:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -706,10 +706,8 @@ ExecOpenIndices(ResultRelInfo *resultRelInfo) ...@@ -706,10 +706,8 @@ ExecOpenIndices(ResultRelInfo *resultRelInfo)
if (!indexDesc->rd_am->amconcurrent) if (!indexDesc->rd_am->amconcurrent)
LockRelation(indexDesc, AccessExclusiveLock); LockRelation(indexDesc, AccessExclusiveLock);
/* /* extract index key information from the index's pg_index info */
* extract index key information from the index's pg_index tuple ii = BuildIndexInfo(indexDesc);
*/
ii = BuildIndexInfo(indexDesc->rd_index);
relationDescs[i] = indexDesc; relationDescs[i] = indexDesc;
indexInfoArray[i] = ii; indexInfoArray[i] = ii;
...@@ -797,7 +795,7 @@ ExecInsertIndexTuples(TupleTableSlot *slot, ...@@ -797,7 +795,7 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
/* /*
* We will use the EState's per-tuple context for evaluating * We will use the EState's per-tuple context for evaluating
* predicates and functional-index functions (creating it if it's not * predicates and index expressions (creating it if it's not
* already there). * already there).
*/ */
econtext = GetPerTupleExprContext(estate); econtext = GetPerTupleExprContext(estate);
...@@ -844,11 +842,12 @@ ExecInsertIndexTuples(TupleTableSlot *slot, ...@@ -844,11 +842,12 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
/* /*
* FormIndexDatum fills in its datum and null parameters with * FormIndexDatum fills in its datum and null parameters with
* attribute information taken from the given heap tuple. * attribute information taken from the given heap tuple.
* It also computes any expressions needed.
*/ */
FormIndexDatum(indexInfo, FormIndexDatum(indexInfo,
heapTuple, heapTuple,
heapDescriptor, heapDescriptor,
econtext->ecxt_per_tuple_memory, estate,
datum, datum,
nullv); nullv);
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.250 2003/05/06 00:20:32 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.251 2003/05/28 16:03:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1421,8 +1421,7 @@ _copyIndexElem(IndexElem *from) ...@@ -1421,8 +1421,7 @@ _copyIndexElem(IndexElem *from)
IndexElem *newnode = makeNode(IndexElem); IndexElem *newnode = makeNode(IndexElem);
COPY_STRING_FIELD(name); COPY_STRING_FIELD(name);
COPY_NODE_FIELD(funcname); COPY_NODE_FIELD(expr);
COPY_NODE_FIELD(args);
COPY_NODE_FIELD(opclass); COPY_NODE_FIELD(opclass);
return newnode; return newnode;
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.193 2003/05/06 00:20:32 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.194 2003/05/28 16:03:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1466,8 +1466,7 @@ static bool ...@@ -1466,8 +1466,7 @@ static bool
_equalIndexElem(IndexElem *a, IndexElem *b) _equalIndexElem(IndexElem *a, IndexElem *b)
{ {
COMPARE_STRING_FIELD(name); COMPARE_STRING_FIELD(name);
COMPARE_NODE_FIELD(funcname); COMPARE_NODE_FIELD(expr);
COMPARE_NODE_FIELD(args);
COMPARE_NODE_FIELD(opclass); COMPARE_NODE_FIELD(opclass);
return true; return true;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.205 2003/05/06 00:20:32 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.206 2003/05/28 16:03:56 tgl Exp $
* *
* NOTES * NOTES
* Every node type that can appear in stored rules' parsetrees *must* * Every node type that can appear in stored rules' parsetrees *must*
...@@ -1181,8 +1181,7 @@ _outIndexElem(StringInfo str, IndexElem *node) ...@@ -1181,8 +1181,7 @@ _outIndexElem(StringInfo str, IndexElem *node)
WRITE_NODE_TYPE("INDEXELEM"); WRITE_NODE_TYPE("INDEXELEM");
WRITE_STRING_FIELD(name); WRITE_STRING_FIELD(name);
WRITE_NODE_FIELD(funcname); WRITE_NODE_FIELD(expr);
WRITE_NODE_FIELD(args);
WRITE_NODE_FIELD(opclass); WRITE_NODE_FIELD(opclass);
} }
......
This diff is collapsed.
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.48 2003/05/02 19:48:53 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.49 2003/05/28 16:03:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -636,62 +636,39 @@ build_index_pathkeys(Query *root, ...@@ -636,62 +636,39 @@ build_index_pathkeys(Query *root,
List *retval = NIL; List *retval = NIL;
int *indexkeys = index->indexkeys; int *indexkeys = index->indexkeys;
Oid *ordering = index->ordering; Oid *ordering = index->ordering;
PathKeyItem *item; List *indexprs = index->indexprs;
Oid sortop;
if (!indexkeys || indexkeys[0] == 0 ||
!ordering || ordering[0] == InvalidOid)
return NIL; /* unordered index? */
if (index->indproc) while (*ordering != InvalidOid)
{ {
/* Functional index: build a representation of the function call */ PathKeyItem *item;
Expr *funcnode; Oid sortop;
List *funcargs = NIL; Node *indexkey;
List *cpathkey;
sortop = *ordering; sortop = *ordering;
if (ScanDirectionIsBackward(scandir)) if (ScanDirectionIsBackward(scandir))
{ {
sortop = get_commutator(sortop); sortop = get_commutator(sortop);
if (sortop == InvalidOid) if (sortop == InvalidOid)
return NIL; /* oops, no reverse sort operator? */ break; /* oops, no reverse sort operator? */
} }
while (*indexkeys != 0) if (*indexkeys != 0)
{ {
funcargs = lappend(funcargs, /* simple index column */
find_indexkey_var(root, rel, *indexkeys)); indexkey = (Node *) find_indexkey_var(root, rel, *indexkeys);
indexkeys++;
}
funcnode = make_funcclause(index->indproc,
get_func_rettype(index->indproc),
false, /* cannot be a set */
COERCE_DONTCARE, /* to match any user expr */
funcargs);
/* Make a one-sublist pathkeys list for the function expression */
item = makePathKeyItem((Node *) funcnode, sortop);
retval = makeList1(make_canonical_pathkey(root, item));
} }
else else
{ {
/* Normal non-functional index */ /* expression --- assume we need not copy it */
while (*indexkeys != 0 && *ordering != InvalidOid) if (indexprs == NIL)
{ elog(ERROR, "wrong number of index expressions");
Var *relvar = find_indexkey_var(root, rel, *indexkeys); indexkey = (Node *) lfirst(indexprs);
List *cpathkey; indexprs = lnext(indexprs);
sortop = *ordering;
if (ScanDirectionIsBackward(scandir))
{
sortop = get_commutator(sortop);
if (sortop == InvalidOid)
break; /* oops, no reverse sort operator? */
} }
/* OK, make a sublist for this sort key */ /* OK, make a sublist for this sort key */
item = makePathKeyItem((Node *) relvar, sortop); item = makePathKeyItem(indexkey, sortop);
cpathkey = make_canonical_pathkey(root, item); cpathkey = make_canonical_pathkey(root, item);
/* /*
...@@ -700,10 +677,10 @@ build_index_pathkeys(Query *root, ...@@ -700,10 +677,10 @@ build_index_pathkeys(Query *root,
*/ */
if (!ptrMember(cpathkey, retval)) if (!ptrMember(cpathkey, retval))
retval = lappend(retval, cpathkey); retval = lappend(retval, cpathkey);
indexkeys++; indexkeys++;
ordering++; ordering++;
} }
}
return retval; return retval;
} }
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.142 2003/05/12 00:17:03 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.143 2003/05/28 16:03:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1197,12 +1197,6 @@ static Node * ...@@ -1197,12 +1197,6 @@ static Node *
fix_indxqual_operand(Node *node, int baserelid, IndexOptInfo *index, fix_indxqual_operand(Node *node, int baserelid, IndexOptInfo *index,
Oid *opclass) Oid *opclass)
{ {
/*
* Remove any binary-compatible relabeling of the indexkey
*/
if (IsA(node, RelabelType))
node = (Node *) ((RelabelType *) node)->arg;
/* /*
* We represent index keys by Var nodes having the varno of the base * We represent index keys by Var nodes having the varno of the base
* table but varattno equal to the index's attribute number (index * table but varattno equal to the index's attribute number (index
...@@ -1210,48 +1204,68 @@ fix_indxqual_operand(Node *node, int baserelid, IndexOptInfo *index, ...@@ -1210,48 +1204,68 @@ fix_indxqual_operand(Node *node, int baserelid, IndexOptInfo *index,
* a special-purpose node type that could not be mistaken for a * a special-purpose node type that could not be mistaken for a
* regular Var. But it will do for now. * regular Var. But it will do for now.
*/ */
if (IsA(node, Var)) Var *result;
{ int pos;
/* If it's a var, find which index key position it occupies */ List *indexprs;
Assert(index->indproc == InvalidOid);
if (((Var *) node)->varno == baserelid) /*
* Remove any binary-compatible relabeling of the indexkey
*/
if (IsA(node, RelabelType))
node = (Node *) ((RelabelType *) node)->arg;
if (IsA(node, Var) &&
((Var *) node)->varno == baserelid)
{ {
/* Try to match against simple index columns */
int varatt = ((Var *) node)->varattno; int varatt = ((Var *) node)->varattno;
int pos;
for (pos = 0; pos < index->nkeys; pos++) if (varatt != 0)
{
for (pos = 0; pos < index->ncolumns; pos++)
{ {
if (index->indexkeys[pos] == varatt) if (index->indexkeys[pos] == varatt)
{ {
Node *newnode = copyObject(node); result = (Var *) copyObject(node);
result->varattno = pos + 1;
((Var *) newnode)->varattno = pos + 1;
/* return the correct opclass, too */ /* return the correct opclass, too */
*opclass = index->classlist[pos]; *opclass = index->classlist[pos];
return newnode; return (Node *) result;
} }
} }
} }
/*
* Oops, this Var isn't an indexkey!
*/
elog(ERROR, "fix_indxqual_operand: var is not index attribute");
} }
/* /* Try to match against index expressions */
* Else, it must be a func expression matching a functional index. indexprs = index->indexprs;
* Since we currently only support single-column functional indexes, for (pos = 0; pos < index->ncolumns; pos++)
* the returned varattno must be 1. {
*/ if (index->indexkeys[pos] == 0)
Assert(index->indproc != InvalidOid); {
Assert(is_funcclause(node)); /* not a very thorough check, but easy */ Node *indexkey;
/* classlist[0] is the only class of a functional index */ if (indexprs == NIL)
*opclass = index->classlist[0]; elog(ERROR, "too few entries in indexprs list");
indexkey = (Node *) lfirst(indexprs);
if (indexkey && IsA(indexkey, RelabelType))
indexkey = (Node *) ((RelabelType *) indexkey)->arg;
if (equal(node, indexkey))
{
/* Found a match */
result = makeVar(baserelid, pos + 1,
exprType(lfirst(indexprs)), -1,
0);
/* return the correct opclass, too */
*opclass = index->classlist[pos];
return (Node *) result;
}
indexprs = lnext(indexprs);
}
}
return (Node *) makeVar(baserelid, 1, exprType(node), -1, 0); /* Ooops... */
elog(ERROR, "fix_indxqual_operand: node is not index attribute");
return NULL; /* keep compiler quiet */
} }
/* /*
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.136 2003/04/29 22:13:09 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.137 2003/05/28 16:03:56 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -132,28 +132,6 @@ get_rightop(Expr *clause) ...@@ -132,28 +132,6 @@ get_rightop(Expr *clause)
return NULL; return NULL;
} }
/*****************************************************************************
* FUNCTION clause functions
*****************************************************************************/
/*
* make_funcclause
* Creates a function clause given its function info and argument list.
*/
Expr *
make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
CoercionForm funcformat, List *funcargs)
{
FuncExpr *expr = makeNode(FuncExpr);
expr->funcid = funcid;
expr->funcresulttype = funcresulttype;
expr->funcretset = funcretset;
expr->funcformat = funcformat;
expr->args = funcargs;
return (Expr *) expr;
}
/***************************************************************************** /*****************************************************************************
* NOT clause functions * NOT clause functions
*****************************************************************************/ *****************************************************************************/
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.82 2003/05/12 00:17:03 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.83 2003/05/28 16:03:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "optimizer/clauses.h" #include "optimizer/clauses.h"
#include "optimizer/plancat.h" #include "optimizer/plancat.h"
#include "parser/parsetree.h" #include "parser/parsetree.h"
#include "rewrite/rewriteManip.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/fmgroids.h" #include "utils/fmgroids.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
...@@ -116,78 +117,67 @@ get_relation_info(Oid relationObjectId, RelOptInfo *rel) ...@@ -116,78 +117,67 @@ get_relation_info(Oid relationObjectId, RelOptInfo *rel)
Relation indexRelation; Relation indexRelation;
Form_pg_index index; Form_pg_index index;
IndexOptInfo *info; IndexOptInfo *info;
int ncolumns;
int i; int i;
int16 amorderstrategy; int16 amorderstrategy;
/* Extract info from the relation descriptor for the index */ /* Extract info from the relation descriptor for the index */
indexRelation = index_open(indexoid); indexRelation = index_open(indexoid);
index = indexRelation->rd_index;
info = makeNode(IndexOptInfo); info = makeNode(IndexOptInfo);
/*
* Need to make these arrays large enough to be sure there is room
* for a terminating 0 at the end of each one.
*/
info->classlist = (Oid *) palloc(sizeof(Oid) * (INDEX_MAX_KEYS + 1));
info->indexkeys = (int *) palloc(sizeof(int) * (INDEX_MAX_KEYS + 1));
info->ordering = (Oid *) palloc(sizeof(Oid) * (INDEX_MAX_KEYS + 1));
/* Extract info from the pg_index tuple */
index = indexRelation->rd_index;
info->indexoid = index->indexrelid; info->indexoid = index->indexrelid;
info->indproc = index->indproc; /* functional index ?? */ info->ncolumns = ncolumns = index->indnatts;
if (VARSIZE(&index->indpred) > VARHDRSZ) /* partial index ?? */
{
char *predString;
predString = DatumGetCString(DirectFunctionCall1(textout, /*
PointerGetDatum(&index->indpred))); * Need to make classlist and ordering arrays large enough to put
info->indpred = (List *) stringToNode(predString); * a terminating 0 at the end of each one.
pfree(predString); */
} info->indexkeys = (int *) palloc(sizeof(int) * ncolumns);
else info->classlist = (Oid *) palloc0(sizeof(Oid) * (ncolumns + 1));
info->indpred = NIL; info->ordering = (Oid *) palloc0(sizeof(Oid) * (ncolumns + 1));
info->unique = index->indisunique;
for (i = 0; i < INDEX_MAX_KEYS; i++) for (i = 0; i < ncolumns; i++)
{ {
if (index->indclass[i] == (Oid) 0)
break;
info->classlist[i] = index->indclass[i]; info->classlist[i] = index->indclass[i];
}
info->classlist[i] = (Oid) 0;
info->ncolumns = i;
for (i = 0; i < INDEX_MAX_KEYS; i++)
{
if (index->indkey[i] == 0)
break;
info->indexkeys[i] = index->indkey[i]; info->indexkeys[i] = index->indkey[i];
} }
info->indexkeys[i] = 0;
info->nkeys = i;
info->relam = indexRelation->rd_rel->relam; info->relam = indexRelation->rd_rel->relam;
info->pages = indexRelation->rd_rel->relpages; info->pages = indexRelation->rd_rel->relpages;
info->tuples = indexRelation->rd_rel->reltuples; info->tuples = indexRelation->rd_rel->reltuples;
info->amcostestimate = index_cost_estimator(indexRelation); info->amcostestimate = index_cost_estimator(indexRelation);
amorderstrategy = indexRelation->rd_am->amorderstrategy;
/* /*
* Fetch the ordering operators associated with the index, if any. * Fetch the ordering operators associated with the index, if any.
*/ */
MemSet(info->ordering, 0, sizeof(Oid) * (INDEX_MAX_KEYS + 1)); amorderstrategy = indexRelation->rd_am->amorderstrategy;
if (amorderstrategy != 0) if (amorderstrategy != 0)
{ {
int oprindex = amorderstrategy - 1; int oprindex = amorderstrategy - 1;
for (i = 0; i < info->ncolumns; i++) for (i = 0; i < ncolumns; i++)
{ {
info->ordering[i] = indexRelation->rd_operator[oprindex]; info->ordering[i] = indexRelation->rd_operator[oprindex];
oprindex += indexRelation->rd_am->amstrategies; oprindex += indexRelation->rd_am->amstrategies;
} }
} }
/*
* Fetch the index expressions and predicate, if any. We must
* modify the copies we obtain from the relcache to have the
* correct varno for the parent relation, so that they match up
* correctly against qual clauses.
*/
info->indexprs = RelationGetIndexExpressions(indexRelation);
info->indpred = RelationGetIndexPredicate(indexRelation);
if (info->indexprs && varno != 1)
ChangeVarNodes((Node *) info->indexprs, 1, varno, 0);
if (info->indpred && varno != 1)
ChangeVarNodes((Node *) info->indpred, 1, varno, 0);
info->unique = index->indisunique;
/* initialize cached join info to empty */ /* initialize cached join info to empty */
info->outer_relids = NULL; info->outer_relids = NULL;
info->inner_paths = NIL; info->inner_paths = NIL;
...@@ -372,15 +362,15 @@ has_unique_index(RelOptInfo *rel, AttrNumber attno) ...@@ -372,15 +362,15 @@ has_unique_index(RelOptInfo *rel, AttrNumber attno)
IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist); IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);
/* /*
* Note: ignore functional and partial indexes, since they don't * Note: ignore partial indexes, since they don't allow us to conclude
* allow us to conclude that all attr values are distinct. Also, a * that all attr values are distinct. We don't take any interest in
* multicolumn unique index doesn't allow us to conclude that just * expressional indexes either. Also, a multicolumn unique index
* the specified attr is unique. * doesn't allow us to conclude that just the specified attr is
* unique.
*/ */
if (index->unique && if (index->unique &&
index->nkeys == 1 && index->ncolumns == 1 &&
index->indexkeys[0] == attno && index->indexkeys[0] == attno &&
index->indproc == InvalidOid &&
index->indpred == NIL) index->indpred == NIL)
return true; return true;
} }
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.271 2003/05/06 00:20:32 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.272 2003/05/28 16:03:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1311,8 +1311,7 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt) ...@@ -1311,8 +1311,7 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
/* OK, add it to the index definition */ /* OK, add it to the index definition */
iparam = makeNode(IndexElem); iparam = makeNode(IndexElem);
iparam->name = pstrdup(key); iparam->name = pstrdup(key);
iparam->funcname = NIL; iparam->expr = NULL;
iparam->args = NIL;
iparam->opclass = NIL; iparam->opclass = NIL;
index->indexParams = lappend(index->indexParams, iparam); index->indexParams = lappend(index->indexParams, iparam);
} }
...@@ -1386,11 +1385,13 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt) ...@@ -1386,11 +1385,13 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
if (index->idxname == NULL && index->indexParams != NIL) if (index->idxname == NULL && index->indexParams != NIL)
{ {
iparam = lfirst(index->indexParams); iparam = (IndexElem *) lfirst(index->indexParams);
/* we should never see an expression item here */
Assert(iparam->expr == NULL);
index->idxname = CreateIndexName(cxt->relation->relname, index->idxname = CreateIndexName(cxt->relation->relname,
iparam->name ? iparam->name : iparam->name,
strVal(llast(iparam->funcname)), "key",
"key", cxt->alist); cxt->alist);
} }
if (index->idxname == NULL) /* should not happen */ if (index->idxname == NULL) /* should not happen */
elog(ERROR, "%s: failed to make implicit index name", elog(ERROR, "%s: failed to make implicit index name",
...@@ -1454,7 +1455,8 @@ static Query * ...@@ -1454,7 +1455,8 @@ static Query *
transformIndexStmt(ParseState *pstate, IndexStmt *stmt) transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
{ {
Query *qry; Query *qry;
RangeTblEntry *rte; RangeTblEntry *rte = NULL;
List *l;
qry = makeNode(Query); qry = makeNode(Query);
qry->commandType = CMD_UTILITY; qry->commandType = CMD_UTILITY;
...@@ -1477,6 +1479,32 @@ transformIndexStmt(ParseState *pstate, IndexStmt *stmt) ...@@ -1477,6 +1479,32 @@ transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
stmt->whereClause = transformWhereClause(pstate, stmt->whereClause); stmt->whereClause = transformWhereClause(pstate, stmt->whereClause);
} }
/* take care of any index expressions */
foreach(l, stmt->indexParams)
{
IndexElem *ielem = (IndexElem *) lfirst(l);
if (ielem->expr)
{
/* Set up rtable as for predicate, see notes above */
if (rte == NULL)
{
rte = addRangeTableEntry(pstate, stmt->relation, NULL,
false, true);
/* no to join list, yes to namespace */
addRTEtoQuery(pstate, rte, false, true);
}
ielem->expr = transformExpr(pstate, ielem->expr);
/*
* We check only that the result type is legitimate; this is
* for consistency with what transformWhereClause() checks for
* the predicate. DefineIndex() will make more checks.
*/
if (expression_returns_set(ielem->expr))
elog(ERROR, "index expression may not return a set");
}
}
qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasSubLinks = pstate->p_hasSubLinks;
stmt->rangetable = pstate->p_rtable; stmt->rangetable = pstate->p_rtable;
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.414 2003/05/15 16:35:28 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.415 2003/05/28 16:03:57 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -210,7 +210,7 @@ static void doNegateFloat(Value *v); ...@@ -210,7 +210,7 @@ static void doNegateFloat(Value *v);
oper_argtypes RuleActionList RuleActionMulti oper_argtypes RuleActionList RuleActionMulti
opt_column_list columnList opt_name_list opt_column_list columnList opt_name_list
sort_clause opt_sort_clause sortby_list index_params sort_clause opt_sort_clause sortby_list index_params
index_list name_list from_clause from_list opt_array_bounds name_list from_clause from_list opt_array_bounds
qualified_name_list any_name any_name_list qualified_name_list any_name any_name_list
any_operator expr_list dotted_name attrs any_operator expr_list dotted_name attrs
target_list update_target_list insert_column_list target_list update_target_list insert_column_list
...@@ -276,7 +276,7 @@ static void doNegateFloat(Value *v); ...@@ -276,7 +276,7 @@ static void doNegateFloat(Value *v);
%type <columnref> columnref %type <columnref> columnref
%type <alias> alias_clause %type <alias> alias_clause
%type <sortgroupby> sortby %type <sortgroupby> sortby
%type <ielem> index_elem func_index %type <ielem> index_elem
%type <node> table_ref %type <node> table_ref
%type <jexpr> joined_table %type <jexpr> joined_table
%type <range> relation_expr %type <range> relation_expr
...@@ -408,7 +408,7 @@ static void doNegateFloat(Value *v); ...@@ -408,7 +408,7 @@ static void doNegateFloat(Value *v);
%token UNIONJOIN %token UNIONJOIN
/* Special keywords, not in the query language - see the "lex" file */ /* Special keywords, not in the query language - see the "lex" file */
%token <str> IDENT FCONST SCONST NCONST BCONST XCONST Op %token <str> IDENT FCONST SCONST BCONST XCONST Op
%token <ival> ICONST PARAM %token <ival> ICONST PARAM
/* precedence: lowest to highest */ /* precedence: lowest to highest */
...@@ -2932,7 +2932,7 @@ function_with_argtypes: ...@@ -2932,7 +2932,7 @@ function_with_argtypes:
* *
* QUERY: * QUERY:
* create index <indexname> on <relname> * create index <indexname> on <relname>
* [ using <access> ] "(" (<col> with <op>)+ ")" * [ using <access> ] "(" ( <col> [ using <opclass> ] )+ ")"
* [ where <predicate> ] * [ where <predicate> ]
* *
*****************************************************************************/ *****************************************************************************/
...@@ -2958,70 +2958,48 @@ index_opt_unique: ...@@ -2958,70 +2958,48 @@ index_opt_unique:
access_method_clause: access_method_clause:
USING access_method { $$ = $2; } USING access_method { $$ = $2; }
/* If btree changes as our default, update pg_get_indexdef() */
| /*EMPTY*/ { $$ = DEFAULT_INDEX_TYPE; } | /*EMPTY*/ { $$ = DEFAULT_INDEX_TYPE; }
; ;
index_params: index_params: index_elem { $$ = makeList1($1); }
index_list { $$ = $1; } | index_params ',' index_elem { $$ = lappend($1, $3); }
| func_index { $$ = makeList1($1); }
;
index_list: index_elem { $$ = makeList1($1); }
| index_list ',' index_elem { $$ = lappend($1, $3); }
;
func_index: func_name '(' name_list ')' opt_class
{
$$ = makeNode(IndexElem);
$$->name = NULL;
$$->funcname = $1;
$$->args = $3;
$$->opclass = $5;
}
; ;
/*
* Index attributes can be either simple column references, or arbitrary
* expressions in parens. For backwards-compatibility reasons, we allow
* an expression that's just a function call to be written without parens.
*/
index_elem: attr_name opt_class index_elem: attr_name opt_class
{ {
$$ = makeNode(IndexElem); $$ = makeNode(IndexElem);
$$->name = $1; $$->name = $1;
$$->funcname = NIL; $$->expr = NULL;
$$->args = NIL;
$$->opclass = $2; $$->opclass = $2;
} }
; | func_name '(' expr_list ')' opt_class
opt_class: any_name
{
/*
* Release 7.0 removed network_ops, timespan_ops, and
* datetime_ops, so we suppress it from being passed to
* the parser so the default *_ops is used. This can be
* removed in some later release. bjm 2000/02/07
*
* Release 7.1 removes lztext_ops, so suppress that too
* for a while. tgl 2000/07/30
*
* Release 7.2 renames timestamp_ops to timestamptz_ops,
* so suppress that too for awhile. I'm starting to
* think we need a better approach. tgl 2000/10/01
*/
if (length($1) == 1)
{ {
char *claname = strVal(lfirst($1)); FuncCall *n = makeNode(FuncCall);
n->funcname = $1;
n->args = $3;
n->agg_star = FALSE;
n->agg_distinct = FALSE;
if (strcmp(claname, "network_ops") != 0 && $$ = makeNode(IndexElem);
strcmp(claname, "timespan_ops") != 0 && $$->name = NULL;
strcmp(claname, "datetime_ops") != 0 && $$->expr = (Node *)n;
strcmp(claname, "lztext_ops") != 0 && $$->opclass = $5;
strcmp(claname, "timestamp_ops") != 0)
$$ = $1;
else
$$ = NIL;
} }
else | '(' a_expr ')' opt_class
$$ = $1; {
$$ = makeNode(IndexElem);
$$->name = NULL;
$$->expr = $2;
$$->opclass = $4;
} }
;
opt_class: any_name { $$ = $1; }
| USING any_name { $$ = $2; } | USING any_name { $$ = $2; }
| /*EMPTY*/ { $$ = NIL; } | /*EMPTY*/ { $$ = NIL; }
; ;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.65 2003/05/27 17:49:46 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.66 2003/05/28 16:03:58 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -713,8 +713,9 @@ PortalRunUtility(Portal portal, Query *query, ...@@ -713,8 +713,9 @@ PortalRunUtility(Portal portal, Query *query,
* without freezing a snapshot. By extension we allow SHOW * without freezing a snapshot. By extension we allow SHOW
* not to set a snapshot. The other stmts listed are just * not to set a snapshot. The other stmts listed are just
* efficiency hacks. Beware of listing anything that can * efficiency hacks. Beware of listing anything that can
* modify the database --- if, say, it has to update a * modify the database --- if, say, it has to update an
* functional index, then it had better have a snapshot. * index with expressions that invoke user-defined functions,
* then it had better have a snapshot.
*/ */
if (! (IsA(utilityStmt, TransactionStmt) || if (! (IsA(utilityStmt, TransactionStmt) ||
IsA(utilityStmt, LockStmt) || IsA(utilityStmt, LockStmt) ||
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* back to source text * back to source text
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.140 2003/05/20 20:35:10 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.141 2003/05/28 16:03:59 tgl Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -535,12 +535,14 @@ pg_get_indexdef(PG_FUNCTION_ARGS) ...@@ -535,12 +535,14 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
Form_pg_index idxrec; Form_pg_index idxrec;
Form_pg_class idxrelrec; Form_pg_class idxrelrec;
Form_pg_am amrec; Form_pg_am amrec;
List *indexprs;
List *context;
Oid indrelid; Oid indrelid;
int len; int len;
int keyno; int keyno;
Oid keycoltypes[INDEX_MAX_KEYS]; Oid keycoltype;
StringInfoData buf; StringInfoData buf;
StringInfoData keybuf; char *str;
char *sep; char *sep;
/* /*
...@@ -576,6 +578,30 @@ pg_get_indexdef(PG_FUNCTION_ARGS) ...@@ -576,6 +578,30 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
elog(ERROR, "syscache lookup for AM %u failed", idxrelrec->relam); elog(ERROR, "syscache lookup for AM %u failed", idxrelrec->relam);
amrec = (Form_pg_am) GETSTRUCT(ht_am); amrec = (Form_pg_am) GETSTRUCT(ht_am);
/*
* Get the index expressions, if any. (NOTE: we do not use the relcache
* versions of the expressions and predicate, because we want to display
* non-const-folded expressions.)
*/
if (!heap_attisnull(ht_idx, Anum_pg_index_indexprs))
{
Datum exprsDatum;
bool isnull;
char *exprsString;
exprsDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
Anum_pg_index_indexprs, &isnull);
Assert(!isnull);
exprsString = DatumGetCString(DirectFunctionCall1(textout,
exprsDatum));
indexprs = (List *) stringToNode(exprsString);
pfree(exprsString);
}
else
indexprs = NIL;
context = deparse_context_for(get_rel_name(indrelid), indrelid);
/* /*
* Start the index definition. Note that the index's name should * Start the index definition. Note that the index's name should
* never be schema-qualified, but the indexed rel's name may be. * never be schema-qualified, but the indexed rel's name may be.
...@@ -588,57 +614,50 @@ pg_get_indexdef(PG_FUNCTION_ARGS) ...@@ -588,57 +614,50 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
quote_identifier(NameStr(amrec->amname))); quote_identifier(NameStr(amrec->amname)));
/* /*
* Collect the indexed attributes in keybuf * Report the indexed attributes
*/ */
initStringInfo(&keybuf);
sep = ""; sep = "";
for (keyno = 0; keyno < INDEX_MAX_KEYS; keyno++) for (keyno = 0; keyno < idxrec->indnatts; keyno++)
{ {
AttrNumber attnum = idxrec->indkey[keyno]; AttrNumber attnum = idxrec->indkey[keyno];
char *attname;
if (attnum == InvalidAttrNumber)
break;
attname = get_relid_attribute_name(indrelid, attnum);
keycoltypes[keyno] = get_atttype(indrelid, attnum);
appendStringInfo(&keybuf, sep); appendStringInfo(&buf, sep);
sep = ", "; sep = ", ";
/* if (attnum != 0)
* Add the indexed field name
*/
appendStringInfo(&keybuf, "%s", quote_identifier(attname));
/*
* If not a functional index, add the operator class name
*/
if (idxrec->indproc == InvalidOid)
get_opclass_name(idxrec->indclass[keyno],
keycoltypes[keyno],
&keybuf);
}
if (idxrec->indproc != InvalidOid)
{ {
/* /* Simple index column */
* For functional index say 'func (attrs) opclass' char *attname;
*/
appendStringInfo(&buf, "%s(%s)", attname = get_relid_attribute_name(indrelid, attnum);
generate_function_name(idxrec->indproc, appendStringInfo(&buf, "%s", quote_identifier(attname));
keyno, keycoltypes), keycoltype = get_atttype(indrelid, attnum);
keybuf.data);
get_opclass_name(idxrec->indclass[0],
get_func_rettype(idxrec->indproc),
&buf);
} }
else else
{ {
/* expressional index */
Node *indexkey;
if (indexprs == NIL)
elog(ERROR, "too few entries in indexprs list");
indexkey = (Node *) lfirst(indexprs);
indexprs = lnext(indexprs);
/* Deparse */
str = deparse_expression(indexkey, context, false, false);
/* Need parens if it's not a bare function call */
if (indexkey && IsA(indexkey, FuncExpr) &&
((FuncExpr *) indexkey)->funcformat == COERCE_EXPLICIT_CALL)
appendStringInfo(&buf, "%s", str);
else
appendStringInfo(&buf, "(%s)", str);
keycoltype = exprType(indexkey);
}
/* /*
* Otherwise say 'attr opclass [, ...]' * Add the operator class name
*/ */
appendStringInfo(&buf, "%s", keybuf.data); get_opclass_name(idxrec->indclass[keyno], keycoltype,
&buf);
} }
appendStringInfoChar(&buf, ')'); appendStringInfoChar(&buf, ')');
...@@ -646,18 +665,21 @@ pg_get_indexdef(PG_FUNCTION_ARGS) ...@@ -646,18 +665,21 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
/* /*
* If it's a partial index, decompile and append the predicate * If it's a partial index, decompile and append the predicate
*/ */
if (VARSIZE(&idxrec->indpred) > VARHDRSZ) if (!heap_attisnull(ht_idx, Anum_pg_index_indpred))
{ {
Node *node; Node *node;
List *context; Datum predDatum;
char *exprstr; bool isnull;
char *str; char *predString;
/* Convert TEXT object to C string */ /* Convert text string to node tree */
exprstr = DatumGetCString(DirectFunctionCall1(textout, predDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
PointerGetDatum(&idxrec->indpred))); Anum_pg_index_indpred, &isnull);
/* Convert expression to node tree */ Assert(!isnull);
node = (Node *) stringToNode(exprstr); predString = DatumGetCString(DirectFunctionCall1(textout,
predDatum));
node = (Node *) stringToNode(predString);
pfree(predString);
/* /*
* If top level is a List, assume it is an implicit-AND structure, * If top level is a List, assume it is an implicit-AND structure,
...@@ -667,7 +689,6 @@ pg_get_indexdef(PG_FUNCTION_ARGS) ...@@ -667,7 +689,6 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
if (node && IsA(node, List)) if (node && IsA(node, List))
node = (Node *) make_ands_explicit((List *) node); node = (Node *) make_ands_explicit((List *) node);
/* Deparse */ /* Deparse */
context = deparse_context_for(get_rel_name(indrelid), indrelid);
str = deparse_expression(node, context, false, false); str = deparse_expression(node, context, false, false);
appendStringInfo(&buf, " WHERE %s", str); appendStringInfo(&buf, " WHERE %s", str);
} }
...@@ -681,7 +702,6 @@ pg_get_indexdef(PG_FUNCTION_ARGS) ...@@ -681,7 +702,6 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
memcpy(VARDATA(indexdef), buf.data, buf.len); memcpy(VARDATA(indexdef), buf.data, buf.len);
pfree(buf.data); pfree(buf.data);
pfree(keybuf.data);
ReleaseSysCache(ht_idx); ReleaseSysCache(ht_idx);
ReleaseSysCache(ht_idxrel); ReleaseSysCache(ht_idxrel);
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.138 2003/05/26 00:11:27 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.139 2003/05/28 16:03:59 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -3905,14 +3905,13 @@ btcostestimate(PG_FUNCTION_ARGS) ...@@ -3905,14 +3905,13 @@ btcostestimate(PG_FUNCTION_ARGS)
indexSelectivity, indexCorrelation); indexSelectivity, indexCorrelation);
/* /*
* If it's a functional index, leave the default zero-correlation * If the first column is a simple variable, and we can get an estimate
* estimate in place. If not, and if we can get an estimate for the * for its ordering correlation C from pg_statistic, estimate
* first variable's ordering correlation C from pg_statistic, estimate
* the index correlation as C / number-of-columns. (The idea here is * the index correlation as C / number-of-columns. (The idea here is
* that multiple columns dilute the importance of the first column's * that multiple columns dilute the importance of the first column's
* ordering, but don't negate it entirely.) * ordering, but don't negate it entirely.)
*/ */
if (index->indproc == InvalidOid) if (index->indexkeys[0] != 0)
{ {
Oid relid; Oid relid;
HeapTuple tuple; HeapTuple tuple;
...@@ -3942,8 +3941,7 @@ btcostestimate(PG_FUNCTION_ARGS) ...@@ -3942,8 +3941,7 @@ btcostestimate(PG_FUNCTION_ARGS)
Assert(nnumbers == 1); Assert(nnumbers == 1);
varCorrelation = numbers[0]; varCorrelation = numbers[0];
for (nKeys = 1; index->indexkeys[nKeys] != 0; nKeys++) nKeys = index->ncolumns;
/* skip */ ;
*indexCorrelation = varCorrelation / nKeys; *indexCorrelation = varCorrelation / nKeys;
......
This diff is collapsed.
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* *
* Copyright 2000-2002 by PostgreSQL Global Development Group * Copyright 2000-2002 by PostgreSQL Global Development Group
* *
* $Header: /cvsroot/pgsql/src/bin/psql/describe.c,v 1.76 2003/03/27 16:57:39 momjian Exp $ * $Header: /cvsroot/pgsql/src/bin/psql/describe.c,v 1.77 2003/05/28 16:03:59 tgl Exp $
*/ */
#include "postgres_fe.h" #include "postgres_fe.h"
#include "describe.h" #include "describe.h"
...@@ -711,9 +711,6 @@ describeOneTableDetails(const char *schemaname, ...@@ -711,9 +711,6 @@ describeOneTableDetails(const char *schemaname,
headers[cols] = NULL; headers[cols] = NULL;
/* Get column info (index requires additional checks) */ /* Get column info (index requires additional checks) */
if (tableinfo.relkind == 'i')
printfPQExpBuffer(&buf, "SELECT\n CASE i.indproc WHEN ('-'::pg_catalog.regproc) THEN a.attname\n ELSE SUBSTR(pg_catalog.pg_get_indexdef(attrelid),\n POSITION('(' in pg_catalog.pg_get_indexdef(attrelid)))\n END,");
else
printfPQExpBuffer(&buf, "SELECT a.attname,"); printfPQExpBuffer(&buf, "SELECT a.attname,");
appendPQExpBuffer(&buf, "\n pg_catalog.format_type(a.atttypid, a.atttypmod)," appendPQExpBuffer(&buf, "\n pg_catalog.format_type(a.atttypid, a.atttypmod),"
"\n (SELECT substring(d.adsrc for 128) FROM pg_catalog.pg_attrdef d" "\n (SELECT substring(d.adsrc for 128) FROM pg_catalog.pg_attrdef d"
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: catversion.h,v 1.196 2003/05/26 00:11:27 tgl Exp $ * $Id: catversion.h,v 1.197 2003/05/28 16:03:59 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -53,6 +53,6 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 200305241 #define CATALOG_VERSION_NO 200305271
#endif #endif
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: dependency.h,v 1.7 2003/03/06 22:54:49 tgl Exp $ * $Id: dependency.h,v 1.8 2003/05/28 16:03:59 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -91,6 +91,11 @@ extern void recordDependencyOnExpr(const ObjectAddress *depender, ...@@ -91,6 +91,11 @@ extern void recordDependencyOnExpr(const ObjectAddress *depender,
Node *expr, List *rtable, Node *expr, List *rtable,
DependencyType behavior); DependencyType behavior);
extern void recordDependencyOnSingleRelExpr(const ObjectAddress *depender,
Node *expr, Oid relId,
DependencyType behavior,
DependencyType self_behavior);
/* in pg_depend.c */ /* in pg_depend.c */
extern void recordDependencyOn(const ObjectAddress *depender, extern void recordDependencyOn(const ObjectAddress *depender,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: index.h,v 1.50 2002/09/23 00:42:48 tgl Exp $ * $Id: index.h,v 1.51 2003/05/28 16:04:00 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "catalog/pg_index.h" #include "catalog/pg_index.h"
#include "nodes/execnodes.h" #include "nodes/execnodes.h"
#define DEFAULT_INDEX_TYPE "btree" #define DEFAULT_INDEX_TYPE "btree"
/* Typedef for callback function for IndexBuildHeapScan */ /* Typedef for callback function for IndexBuildHeapScan */
...@@ -40,12 +41,12 @@ extern Oid index_create(Oid heapRelationId, ...@@ -40,12 +41,12 @@ extern Oid index_create(Oid heapRelationId,
extern void index_drop(Oid indexId); extern void index_drop(Oid indexId);
extern IndexInfo *BuildIndexInfo(Form_pg_index indexStruct); extern IndexInfo *BuildIndexInfo(Relation index);
extern void FormIndexDatum(IndexInfo *indexInfo, extern void FormIndexDatum(IndexInfo *indexInfo,
HeapTuple heapTuple, HeapTuple heapTuple,
TupleDesc heapDescriptor, TupleDesc heapDescriptor,
MemoryContext resultCxt, EState *estate,
Datum *datum, Datum *datum,
char *nullv); char *nullv);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: pg_index.h,v 1.30 2003/03/10 22:28:19 tgl Exp $ * $Id: pg_index.h,v 1.31 2003/05/28 16:04:00 tgl Exp $
* *
* NOTES * NOTES
* the genbki.sh script reads this file and generates .bki * the genbki.sh script reads this file and generates .bki
...@@ -35,18 +35,19 @@ CATALOG(pg_index) BKI_WITHOUT_OIDS ...@@ -35,18 +35,19 @@ CATALOG(pg_index) BKI_WITHOUT_OIDS
{ {
Oid indexrelid; /* OID of the index */ Oid indexrelid; /* OID of the index */
Oid indrelid; /* OID of the relation it indexes */ Oid indrelid; /* OID of the relation it indexes */
regproc indproc; /* OID of function for functional index */ int2vector indkey; /* column numbers of indexed cols, or 0 */
int2vector indkey; /* column numbers of indexed attributes */
oidvector indclass; /* opclass identifiers */ oidvector indclass; /* opclass identifiers */
bool indisclustered; /* is this the index last clustered by? */ int2 indnatts; /* number of columns in index */
bool indisunique; /* is this a unique index? */ bool indisunique; /* is this a unique index? */
bool indisprimary; /* is this index for primary key? */ bool indisprimary; /* is this index for primary key? */
Oid indreference; /* oid of index of referenced relation (ie bool indisclustered; /* is this the index last clustered by? */
* - this index for foreign key) */
/* VARIABLE LENGTH FIELD: */ /* VARIABLE LENGTH FIELDS: */
text indexprs; /* expression trees for index attributes
* that are not simple column references;
* one for each zero entry in indkey[] */
text indpred; /* expression tree for predicate, if a text indpred; /* expression tree for predicate, if a
* partial index */ * partial index; else NULL */
} FormData_pg_index; } FormData_pg_index;
/* ---------------- /* ----------------
...@@ -63,13 +64,13 @@ typedef FormData_pg_index *Form_pg_index; ...@@ -63,13 +64,13 @@ typedef FormData_pg_index *Form_pg_index;
#define Natts_pg_index 10 #define Natts_pg_index 10
#define Anum_pg_index_indexrelid 1 #define Anum_pg_index_indexrelid 1
#define Anum_pg_index_indrelid 2 #define Anum_pg_index_indrelid 2
#define Anum_pg_index_indproc 3 #define Anum_pg_index_indkey 3
#define Anum_pg_index_indkey 4 #define Anum_pg_index_indclass 4
#define Anum_pg_index_indclass 5 #define Anum_pg_index_indnatts 5
#define Anum_pg_index_indisclustered 6 #define Anum_pg_index_indisunique 6
#define Anum_pg_index_indisunique 7 #define Anum_pg_index_indisprimary 7
#define Anum_pg_index_indisprimary 8 #define Anum_pg_index_indisclustered 8
#define Anum_pg_index_indreference 9 #define Anum_pg_index_indexprs 9
#define Anum_pg_index_indpred 10 #define Anum_pg_index_indpred 10
#endif /* PG_INDEX_H */ #endif /* PG_INDEX_H */
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* <opcamid, opcname> --- that is, there is a row for each valid combination * <opcamid, opcname> --- that is, there is a row for each valid combination
* of opclass name and index access method type. This row specifies the * of opclass name and index access method type. This row specifies the
* expected input data type for the opclass (the type of the heap column, * expected input data type for the opclass (the type of the heap column,
* or the function output type in the case of a functional index). Note * or the expression output type in the case of an index expression). Note
* that types binary-coercible to the specified type will be accepted too. * that types binary-coercible to the specified type will be accepted too.
* *
* For a given <opcamid, opcintype> pair, there can be at most one row that * For a given <opcamid, opcintype> pair, there can be at most one row that
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: pg_opclass.h,v 1.49 2003/05/26 00:11:27 tgl Exp $ * $Id: pg_opclass.h,v 1.50 2003/05/28 16:04:00 tgl Exp $
* *
* NOTES * NOTES
* the genbki.sh script reads this file and generates .bki * the genbki.sh script reads this file and generates .bki
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: execnodes.h,v 1.97 2003/04/08 23:20:04 tgl Exp $ * $Id: execnodes.h,v 1.98 2003/05/28 16:04:00 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -27,19 +27,17 @@ ...@@ -27,19 +27,17 @@
/* ---------------- /* ----------------
* IndexInfo information * IndexInfo information
* *
* this class holds the information needed to construct new index * this struct holds the information needed to construct new index
* entries for a particular index. Used for both index_build and * entries for a particular index. Used for both index_build and
* retail creation of index entries. * retail creation of index entries.
* *
* NumIndexAttrs number of columns in this index * NumIndexAttrs number of columns in this index
* (1 if a func. index, else same as NumKeyAttrs)
* NumKeyAttrs number of key attributes for this index
* (ie, number of attrs from underlying relation)
* KeyAttrNumbers underlying-rel attribute numbers used as keys * KeyAttrNumbers underlying-rel attribute numbers used as keys
* (zeroes indicate expressions)
* Expressions expr trees for expression entries, or NIL if none
* ExpressionsState exec state for expressions, or NIL if none
* Predicate partial-index predicate, or NIL if none * Predicate partial-index predicate, or NIL if none
* PredicateState exec state for predicate, or NIL if none * PredicateState exec state for 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? * Unique is it a unique index?
* ---------------- * ----------------
*/ */
...@@ -47,12 +45,11 @@ typedef struct IndexInfo ...@@ -47,12 +45,11 @@ typedef struct IndexInfo
{ {
NodeTag type; NodeTag type;
int ii_NumIndexAttrs; int ii_NumIndexAttrs;
int ii_NumKeyAttrs;
AttrNumber ii_KeyAttrNumbers[INDEX_MAX_KEYS]; AttrNumber ii_KeyAttrNumbers[INDEX_MAX_KEYS];
List *ii_Expressions; /* list of Expr */
List *ii_ExpressionsState; /* list of ExprState */
List *ii_Predicate; /* list of Expr */ List *ii_Predicate; /* list of Expr */
List *ii_PredicateState; /* list of ExprState */ List *ii_PredicateState; /* list of ExprState */
Oid ii_FuncOid;
FmgrInfo ii_FuncInfo;
bool ii_Unique; bool ii_Unique;
} IndexInfo; } IndexInfo;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: parsenodes.h,v 1.237 2003/05/02 20:54:36 tgl Exp $ * $Id: parsenodes.h,v 1.238 2003/05/28 16:04:02 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -353,17 +353,15 @@ typedef struct ColumnDef ...@@ -353,17 +353,15 @@ typedef struct ColumnDef
/* /*
* IndexElem - index parameters (used in CREATE INDEX) * IndexElem - index parameters (used in CREATE INDEX)
* *
* For a plain index, each 'name' is an attribute name in the heap relation; * For a plain index attribute, 'name' is the name of the table column to
* 'funcname' and 'args' are NIL. For a functional index, only one IndexElem * index, and 'expr' is NULL. For an index expression, 'name' is NULL and
* is allowed. It has name = NULL, funcname = name of function and args = * 'expr' is the expression tree.
* list of attribute names that are the function's arguments.
*/ */
typedef struct IndexElem typedef struct IndexElem
{ {
NodeTag type; NodeTag type;
char *name; /* name of attribute to index, or NULL */ char *name; /* name of attribute to index, or NULL */
List *funcname; /* qualified name of function */ Node *expr; /* expression to index, or NULL */
List *args; /* list of names of function arguments */
List *opclass; /* name of desired opclass; NIL = default */ List *opclass; /* name of desired opclass; NIL = default */
} IndexElem; } IndexElem;
...@@ -1271,8 +1269,8 @@ typedef struct IndexStmt ...@@ -1271,8 +1269,8 @@ typedef struct IndexStmt
char *accessMethod; /* name of access method (eg. btree) */ char *accessMethod; /* name of access method (eg. btree) */
List *indexParams; /* a list of IndexElem */ List *indexParams; /* a list of IndexElem */
Node *whereClause; /* qualification (partial-index predicate) */ Node *whereClause; /* qualification (partial-index predicate) */
List *rangetable; /* range table for qual, filled in by List *rangetable; /* range table for qual and/or expressions,
* transformStmt() */ * filled in by transformStmt() */
bool unique; /* is index unique? */ bool unique; /* is index unique? */
bool primary; /* is index on primary key? */ bool primary; /* is index on primary key? */
bool isconstraint; /* is it from a CONSTRAINT clause? */ bool isconstraint; /* is it from a CONSTRAINT clause? */
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: relation.h,v 1.79 2003/02/08 20:20:55 tgl Exp $ * $Id: relation.h,v 1.80 2003/05/28 16:04:02 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -227,15 +227,16 @@ typedef struct RelOptInfo ...@@ -227,15 +227,16 @@ typedef struct RelOptInfo
* and indexes, but that created confusion without actually doing anything * and indexes, but that created confusion without actually doing anything
* useful. So now we have a separate IndexOptInfo struct for indexes. * useful. So now we have a separate IndexOptInfo struct for indexes.
* *
* ncolumns and nkeys are the same except for a functional index, * classlist[], indexkeys[], and ordering[] have ncolumns entries.
* wherein ncolumns is 1 (the single function output) while nkeys * Zeroes in the indexkeys[] array indicate index columns that are
* is the number of table columns passed to the function. classlist[] * expressions; there is one element in indexprs for each such column.
* and ordering[] have ncolumns entries, while indexkeys[] has nkeys
* entries.
* *
* Note: for historical reasons, the arrays classlist, indexkeys and * Note: for historical reasons, the classlist and ordering arrays have
* ordering have an extra entry that is always zero. Some code scans * an extra entry that is always zero. Some code scans until it sees a
* until it sees a zero rather than looking at ncolumns or nkeys. * zero entry, rather than looking at ncolumns.
*
* The indexprs and indpred expressions have been run through
* eval_const_expressions() for ease of matching to WHERE clauses.
*/ */
typedef struct IndexOptInfo typedef struct IndexOptInfo
...@@ -250,15 +251,14 @@ typedef struct IndexOptInfo ...@@ -250,15 +251,14 @@ typedef struct IndexOptInfo
/* index descriptor information */ /* index descriptor information */
int ncolumns; /* number of columns in index */ int ncolumns; /* number of columns in index */
int nkeys; /* number of keys used by index */
Oid *classlist; /* OIDs of operator classes for columns */ Oid *classlist; /* OIDs of operator classes for columns */
int *indexkeys; /* column numbers of index's keys */ int *indexkeys; /* column numbers of index's keys, or 0 */
Oid *ordering; /* OIDs of sort operators for each column */ Oid *ordering; /* OIDs of sort operators for each column */
Oid relam; /* OID of the access method (in pg_am) */ Oid relam; /* OID of the access method (in pg_am) */
RegProcedure amcostestimate; /* OID of the access method's cost fcn */ RegProcedure amcostestimate; /* OID of the access method's cost fcn */
Oid indproc; /* OID of func if functional index, else 0 */ List *indexprs; /* expressions for non-simple index columns */
List *indpred; /* predicate if a partial index, else NIL */ List *indpred; /* predicate if a partial index, else NIL */
bool unique; /* true if a unique index */ bool unique; /* true if a unique index */
...@@ -289,9 +289,8 @@ typedef struct PathKeyItem ...@@ -289,9 +289,8 @@ typedef struct PathKeyItem
/* /*
* key typically points to a Var node, ie a relation attribute, but it * key typically points to a Var node, ie a relation attribute, but it
* can also point to a FuncExpr clause representing the value indexed by a * can also point to an arbitrary expression representing the value
* functional index. Someday we might allow arbitrary expressions as * indexed by an index expression.
* path keys, so don't assume more than you must.
*/ */
} PathKeyItem; } PathKeyItem;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: clauses.h,v 1.62 2003/02/04 00:50:01 tgl Exp $ * $Id: clauses.h,v 1.63 2003/05/28 16:04:02 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -28,9 +28,6 @@ extern Expr *make_opclause(Oid opno, Oid opresulttype, bool opretset, ...@@ -28,9 +28,6 @@ extern Expr *make_opclause(Oid opno, Oid opresulttype, bool opretset,
extern Node *get_leftop(Expr *clause); extern Node *get_leftop(Expr *clause);
extern Node *get_rightop(Expr *clause); extern Node *get_rightop(Expr *clause);
extern Expr *make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
CoercionForm funcformat, List *funcargs);
extern bool not_clause(Node *clause); extern bool not_clause(Node *clause);
extern Expr *make_notclause(Expr *notclause); extern Expr *make_notclause(Expr *notclause);
extern Expr *get_notclausearg(Expr *notclause); extern Expr *get_notclausearg(Expr *notclause);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: rel.h,v 1.64 2002/11/23 03:59:09 momjian Exp $ * $Id: rel.h,v 1.65 2003/05/28 16:04:02 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -132,6 +132,8 @@ typedef struct RelationData ...@@ -132,6 +132,8 @@ typedef struct RelationData
/* These are non-NULL only for an index relation: */ /* These are non-NULL only for an index relation: */
Form_pg_index rd_index; /* pg_index tuple describing this index */ Form_pg_index rd_index; /* pg_index tuple describing this index */
struct HeapTupleData *rd_indextuple; /* all of pg_index tuple */
/* "struct HeapTupleData *" avoids need to include htup.h here */
Form_pg_am rd_am; /* pg_am tuple for index's AM */ Form_pg_am rd_am; /* pg_am tuple for index's AM */
/* index access support info (used only for an index relation) */ /* index access support info (used only for an index relation) */
...@@ -142,6 +144,8 @@ typedef struct RelationData ...@@ -142,6 +144,8 @@ typedef struct RelationData
struct FmgrInfo *rd_supportinfo; /* lookup info for support struct FmgrInfo *rd_supportinfo; /* lookup info for support
* procedures */ * procedures */
/* "struct FmgrInfo" avoids need to include fmgr.h here */ /* "struct FmgrInfo" avoids need to include fmgr.h here */
List *rd_indexprs; /* index expression trees, if any */
List *rd_indpred; /* index predicate tree, if any */
/* statistics collection area */ /* statistics collection area */
PgStat_Info pgstat_info; PgStat_Info pgstat_info;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: relcache.h,v 1.34 2002/08/06 02:36:35 tgl Exp $ * $Id: relcache.h,v 1.35 2003/05/28 16:04:02 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -32,6 +32,8 @@ extern void RelationClose(Relation relation); ...@@ -32,6 +32,8 @@ extern void RelationClose(Relation relation);
* Routines to compute/retrieve additional cached information * Routines to compute/retrieve additional cached information
*/ */
extern List *RelationGetIndexList(Relation relation); extern List *RelationGetIndexList(Relation relation);
extern List *RelationGetIndexExpressions(Relation relation);
extern List *RelationGetIndexPredicate(Relation relation);
extern void RelationInitIndexAccessInfo(Relation relation); extern void RelationInitIndexAccessInfo(Relation relation);
......
...@@ -36,8 +36,8 @@ def list_simple_ind(pgcnx): ...@@ -36,8 +36,8 @@ def list_simple_ind(pgcnx):
ic.relname AS index_name, a.attname ic.relname AS index_name, a.attname
FROM pg_class bc, pg_class ic, pg_index i, pg_attribute a FROM pg_class bc, pg_class ic, pg_index i, pg_attribute a
WHERE i.indrelid = bc.oid AND i.indexrelid = bc.oid WHERE i.indrelid = bc.oid AND i.indexrelid = bc.oid
AND i.indkey[0] = a.attnum AND a.attrelid = bc.oid AND i.indkey[0] = a.attnum AND i.indnatts = 1
AND i.indproc = '0'::oid AND a.attisdropped = 'f' AND a.attrelid = bc.oid AND a.attisdropped = 'f'
ORDER BY class_name, index_name, attname""") ORDER BY class_name, index_name, attname""")
return result return result
......
...@@ -81,3 +81,17 @@ INSERT INTO func_index_heap VALUES('ABCD', 'EF'); ...@@ -81,3 +81,17 @@ INSERT INTO func_index_heap VALUES('ABCD', 'EF');
ERROR: Cannot insert a duplicate key into unique index func_index_index ERROR: Cannot insert a duplicate key into unique index func_index_index
-- but this shouldn't: -- but this shouldn't:
INSERT INTO func_index_heap VALUES('QWERTY'); INSERT INTO func_index_heap VALUES('QWERTY');
--
-- Same test, expressional index
--
DROP TABLE func_index_heap;
CREATE TABLE func_index_heap (f1 text, f2 text);
CREATE UNIQUE INDEX func_index_index on func_index_heap ((f1 || f2) text_ops);
INSERT INTO func_index_heap VALUES('ABC','DEF');
INSERT INTO func_index_heap VALUES('AB','CDEFG');
INSERT INTO func_index_heap VALUES('QWE','RTY');
-- this should fail because of unique index:
INSERT INTO func_index_heap VALUES('ABCD', 'EF');
ERROR: Cannot insert a duplicate key into unique index func_index_index
-- but this shouldn't:
INSERT INTO func_index_heap VALUES('QWERTY');
...@@ -75,7 +75,7 @@ FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = relnamespace ...@@ -75,7 +75,7 @@ FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = relnamespace
WHERE relhasoids WHERE relhasoids
AND ((nspname ~ '^pg_') IS NOT FALSE) AND ((nspname ~ '^pg_') IS NOT FALSE)
AND NOT EXISTS (SELECT 1 FROM pg_index i WHERE indrelid = c.oid AND NOT EXISTS (SELECT 1 FROM pg_index i WHERE indrelid = c.oid
AND indkey[0] = -2 AND indkey[1] = 0 AND indisunique); AND indkey[0] = -2 AND indnatts = 1 AND indisunique);
relname | nspname relname | nspname
---------+--------- ---------+---------
(0 rows) (0 rows)
......
...@@ -104,3 +104,19 @@ INSERT INTO func_index_heap VALUES('QWE','RTY'); ...@@ -104,3 +104,19 @@ INSERT INTO func_index_heap VALUES('QWE','RTY');
INSERT INTO func_index_heap VALUES('ABCD', 'EF'); INSERT INTO func_index_heap VALUES('ABCD', 'EF');
-- but this shouldn't: -- but this shouldn't:
INSERT INTO func_index_heap VALUES('QWERTY'); INSERT INTO func_index_heap VALUES('QWERTY');
--
-- Same test, expressional index
--
DROP TABLE func_index_heap;
CREATE TABLE func_index_heap (f1 text, f2 text);
CREATE UNIQUE INDEX func_index_index on func_index_heap ((f1 || f2) text_ops);
INSERT INTO func_index_heap VALUES('ABC','DEF');
INSERT INTO func_index_heap VALUES('AB','CDEFG');
INSERT INTO func_index_heap VALUES('QWE','RTY');
-- this should fail because of unique index:
INSERT INTO func_index_heap VALUES('ABCD', 'EF');
-- but this shouldn't:
INSERT INTO func_index_heap VALUES('QWERTY');
...@@ -21,4 +21,4 @@ FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = relnamespace ...@@ -21,4 +21,4 @@ FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = relnamespace
WHERE relhasoids WHERE relhasoids
AND ((nspname ~ '^pg_') IS NOT FALSE) AND ((nspname ~ '^pg_') IS NOT FALSE)
AND NOT EXISTS (SELECT 1 FROM pg_index i WHERE indrelid = c.oid AND NOT EXISTS (SELECT 1 FROM pg_index i WHERE indrelid = c.oid
AND indkey[0] = -2 AND indkey[1] = 0 AND indisunique); AND indkey[0] = -2 AND indnatts = 1 AND indisunique);
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
-- Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group -- Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
-- Portions Copyright (c) 1994, Regents of the University of California -- Portions Copyright (c) 1994, Regents of the University of California
-- --
-- $Id: syscat.source,v 1.7 2002/06/20 20:29:54 momjian Exp $ -- $Id: syscat.source,v 1.8 2003/05/28 16:04:02 tgl Exp $
-- --
--------------------------------------------------------------------------- ---------------------------------------------------------------------------
...@@ -31,8 +31,8 @@ SELECT relname ...@@ -31,8 +31,8 @@ SELECT relname
-- --
-- lists all simple indices (ie. those that are not defined over a function -- lists all simple indices (ie. those that are defined over one simple
-- of several attributes) -- column reference)
-- --
SELECT bc.relname AS class_name, SELECT bc.relname AS class_name,
ic.relname AS index_name, ic.relname AS index_name,
...@@ -44,8 +44,8 @@ SELECT bc.relname AS class_name, ...@@ -44,8 +44,8 @@ SELECT bc.relname AS class_name,
WHERE i.indrelid = bc.oid WHERE i.indrelid = bc.oid
and i.indexrelid = ic.oid and i.indexrelid = ic.oid
and i.indkey[0] = a.attnum and i.indkey[0] = a.attnum
and i.indnatts = 1
and a.attrelid = bc.oid and a.attrelid = bc.oid
and i.indproc = '0'::oid -- no functional indices
ORDER BY class_name, index_name, attname; ORDER BY class_name, index_name, attname;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment