Replace functional-index facility with expressional indexes. Any column
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 28 May 2003 16:04:02 +0000 (16:04 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 28 May 2003 16:04:02 +0000 (16:04 +0000)
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.

50 files changed:
contrib/dblink/dblink.c
doc/src/sgml/catalogs.sgml
doc/src/sgml/indices.sgml
doc/src/sgml/plpgsql.sgml
doc/src/sgml/ref/create_index.sgml
doc/src/sgml/release.sgml
src/backend/bootstrap/bootparse.y
src/backend/bootstrap/bootstrap.c
src/backend/catalog/dependency.c
src/backend/catalog/heap.c
src/backend/catalog/index.c
src/backend/catalog/indexing.c
src/backend/catalog/pg_constraint.c
src/backend/commands/cluster.c
src/backend/commands/indexcmds.c
src/backend/commands/tablecmds.c
src/backend/commands/vacuum.c
src/backend/executor/execUtils.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/optimizer/path/indxpath.c
src/backend/optimizer/path/pathkeys.c
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/util/clauses.c
src/backend/optimizer/util/plancat.c
src/backend/parser/analyze.c
src/backend/parser/gram.y
src/backend/tcop/pquery.c
src/backend/utils/adt/ruleutils.c
src/backend/utils/adt/selfuncs.c
src/backend/utils/cache/relcache.c
src/bin/psql/describe.c
src/include/catalog/catversion.h
src/include/catalog/dependency.h
src/include/catalog/index.h
src/include/catalog/pg_index.h
src/include/catalog/pg_opclass.h
src/include/nodes/execnodes.h
src/include/nodes/parsenodes.h
src/include/nodes/relation.h
src/include/optimizer/clauses.h
src/include/utils/rel.h
src/include/utils/relcache.h
src/interfaces/python/tutorial/syscat.py
src/test/regress/expected/create_index.out
src/test/regress/expected/sanity_check.out
src/test/regress/sql/create_index.sql
src/test/regress/sql/sanity_check.sql
src/tutorial/syscat.source

index accfd97e9f8e3d3a43d3583230a436e9f2febc72..20e7662e28029fb7627001fcb5a0344b1ec52a47 100644 (file)
@@ -1492,10 +1492,7 @@ get_pkey_attnames(Oid relid, int16 *numatts)
        /* we're only interested if it is the primary key */
        if (index->indisprimary == TRUE)
        {
-           i = 0;
-           while (index->indkey[i++] != 0)
-               (*numatts)++;
-
+           *numatts = index->indnatts;
            if (*numatts > 0)
            {
                result = (char **) palloc(*numatts * sizeof(char *));
index 0cc355330dd511b01b1007895941267cb7f8df86..a8f7190856c75fd46e208929f0d45c43a8940f46 100644 (file)
@@ -1,6 +1,6 @@
 <!--
  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">
       <entry>The OID of the <structname>pg_class</> entry for the table this index is for</entry>
      </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>
       <entry><structfield>indkey</structfield></entry>
       <entry><type>int2vector</type></entry>
       <entry>pg_attribute.attnum</entry>
       <entry>
-       This is an array of up to
-       <symbol>INDEX_MAX_KEYS</symbol> values that indicate which
-       table columns this index pertains to.  For example a value of
-       <literal>1 3</literal> would mean that the first and the third
-       column make up the index key.  For a functional index, these
-       columns are the inputs to the function, and the function's return
-       value is the index key.
+       This is an array of <structfield>indnatts</structfield> (up to
+       <symbol>INDEX_MAX_KEYS</symbol>) values that indicate which
+       table columns this index indexes.  For example a value of
+       <literal>1 3</literal> would mean that the first and the third table
+       columns make up the index key.  A zero in this array indicates that the
+       corresponding index attribute is an expression over the table columns,
+       rather than a simple column reference.
       </entry>
      </row>
 
       <entry><type>oidvector</type></entry>
       <entry>pg_opclass.oid</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
        <structname>pg_opclass</structname> for details.
       </entry>
      </row>
 
      <row>
-      <entry><structfield>indisclustered</structfield></entry>
-      <entry><type>bool</type></entry>
+      <entry><structfield>indnatts</structfield></entry>
+      <entry><type>int2</type></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>
-      <entry><structfield>indreference</structfield></entry>
-      <entry><type>oid</type></entry>
+      <entry><structfield>indisclustered</structfield></entry>
+      <entry><type>bool</type></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>
       <entry><structfield>indpred</structfield></entry>
       <entry><type>text</type></entry>
       <entry></entry>
-      <entry>Expression tree (in the form of a <function>nodeToString()</function> representation)
-      for partial index predicate.  Empty string if not a partial
-      index.</entry>
+      <entry>Expression tree (in <function>nodeToString()</function> representation)
+      for partial index predicate.  Null if not a partial index.</entry>
      </row>
     </tbody>
    </tgroup>
index fcd7108a14cd8ec236340bb90c8210e2f6b22c48..4e79084e7ec4b57f3ee7a7fb124695e678adf4bf 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $Header: /cvsroot/pgsql/doc/src/sgml/indices.sgml,v 1.41 2003/05/15 15:50:18 petere Exp $ -->
+<!-- $Header: /cvsroot/pgsql/doc/src/sgml/indices.sgml,v 1.42 2003/05/28 16:03:55 tgl Exp $ -->
 
 <chapter id="indexes">
  <title id="indexes-title">Indexes</title>
@@ -20,8 +20,7 @@
   <title>Introduction</title>
 
   <para>
-   The classical example for the need of an index is if there is a
-   table similar to this:
+   Suppose we have a table similar to this:
 <programlisting>
 CREATE TABLE test1 (
     id integer,
@@ -32,24 +31,24 @@ CREATE TABLE test1 (
 <programlisting>
 SELECT content FROM test1 WHERE id = <replaceable>constant</replaceable>;
 </programlisting>
-   Ordinarily, the system would have to scan the entire
-   <structname>test1</structname> table row by row to find all
+   With no advance preparation, the system would have to scan the entire
+   <structname>test1</structname> table, row by row, to find all
    matching entries.  If there are a lot of rows in
-   <structname>test1</structname> and only a few rows (possibly zero
-   or one) returned by the query, then this is clearly an inefficient
-   method.  If the system were instructed to maintain an index on the
-   <structfield>id</structfield> column, then it could use a more
+   <structname>test1</structname> and only a few rows (perhaps only zero
+   or one) that would be returned by such a query, then this is clearly an
+   inefficient method.  But if the system has been instructed to maintain an
+   index on the <structfield>id</structfield> column, then it can use a more
    efficient method for locating matching rows.  For instance, it
    might only have to walk a few levels deep into a search tree.
   </para>
 
   <para>
-   A similar approach is used in most books of non-fiction:  Terms and
+   A similar approach is used in most books of non-fiction:  terms and
    concepts that are frequently looked up by readers are collected in
    an alphabetic index at the end of the book.  The interested reader
    can scan the index relatively quickly and flip to the appropriate
-   page, and would not have to read the entire book to find the
-   interesting location.  As it is the task of the author to
+   page(s), rather than having to read the entire book to find the
+   material of interest.  Just as it is the task of the author to
    anticipate the items that the readers are most likely to look up,
    it is the task of the database programmer to foresee which indexes
    would be of advantage.
@@ -73,13 +72,14 @@ CREATE INDEX test1_id_index ON test1 (id);
 
   <para>
    Once the index is created, no further intervention is required: the
-   system will use the index when it thinks it would be more efficient
+   system will update the index when the table is modified, and it will
+   use the index in queries when it thinks this would be more efficient
    than a sequential table scan.  But you may have to run the
    <command>ANALYZE</command> command regularly to update
    statistics to allow the query planner to make educated decisions.
    Also read <xref linkend="performance-tips"> for information about
    how to find out whether an index is used and when and why the
-   planner may choose to <emphasis>not</emphasis> use an index.
+   planner may choose <emphasis>not</emphasis> to use an index.
   </para>
 
   <para>
@@ -198,7 +198,7 @@ CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable>
      than B-tree indexes, and the index size and build time for hash
      indexes is much worse. Hash indexes also suffer poor performance
      under high concurrency. For these reasons, hash index use is
-     discouraged.
+     presently discouraged.
     </para>
    </note>  
   </para>
@@ -250,14 +250,13 @@ CREATE INDEX test2_mm_idx ON test2 (major, minor);
    Currently, only the B-tree and GiST implementations support multicolumn
    indexes.  Up to 32 columns may be specified.  (This limit can be
    altered when building <productname>PostgreSQL</productname>; see the
-   file <filename>pg_config.h</filename>.)
+   file <filename>pg_config_manual.h</filename>.)
   </para>
 
   <para>
    The query planner can use a multicolumn index for queries that
-   involve the leftmost column in the index definition and any number
-   of columns listed to the right of it without a gap (when
-   used with appropriate operators).  For example,
+   involve the leftmost column in the index definition plus any number
+   of columns listed to the right of it, without a gap.  For example,
    an index on <literal>(a, b, c)</literal> can be used in queries
    involving all of <literal>a</literal>, <literal>b</literal>, and
    <literal>c</literal>, or in queries involving both
@@ -266,7 +265,9 @@ CREATE INDEX test2_mm_idx ON test2 (major, minor);
    (In a query involving <literal>a</literal> and <literal>c</literal>
    the planner might choose to use the index for
    <literal>a</literal> only and treat <literal>c</literal> like an
-   ordinary unindexed column.)
+   ordinary unindexed column.)  Of course, each column must be used with
+   operators appropriate to the index type; clauses that involve other
+   operators will not be considered.
   </para>
 
   <para>
@@ -283,8 +284,8 @@ SELECT name FROM test2 WHERE major = <replaceable>constant</replaceable> OR mino
   <para>
    Multicolumn indexes should be used sparingly.  Most of the time,
    an index on a single column is sufficient and saves space and time.
-   Indexes with more than three columns are almost certainly
-   inappropriate.
+   Indexes with more than three columns are unlikely to be helpful
+   unless the usage of the table is extremely stylized.
   </para>
  </sect1>
 
@@ -332,19 +333,19 @@ CREATE UNIQUE INDEX <replaceable>name</replaceable> ON <replaceable>table</repla
  </sect1>
 
 
- <sect1 id="indexes-functional">
-  <title>Functional Indexes</title>
+ <sect1 id="indexes-expressional">
+  <title>Indexes on Expressions</title>
 
-  <indexterm zone="indexes-functional">
+  <indexterm zone="indexes-expressional">
    <primary>indexes</primary>
-   <secondary>on functions</secondary>
+   <secondary>on expressions</secondary>
   </indexterm>
 
   <para>
-   For a <firstterm>functional index</firstterm>, an index is defined
-   on the result of a function applied to one or more columns of a
-   single table.  Functional indexes can be used to obtain fast access
-   to data based on the result of function calls.
+   An index column need not be just a column of the underlying table,
+   but can be a function or scalar expression computed from one or
+   more columns of the table.  This feature is useful to obtain fast
+   access to tables based on the results of computations.
   </para>
 
   <para>
@@ -362,20 +363,29 @@ CREATE INDEX test1_lower_col1_idx ON test1 (lower(col1));
   </para>
 
   <para>
-   The function in the index definition can take more than one
-   argument, but they must be table columns, not constants.
-   Functional indexes are always single-column (namely, the function
-   result) even if the function uses more than one input column; there
-   cannot be multicolumn indexes that contain function calls.
+   As another example, if one often does queries like this:
+<programlisting>
+SELECT * FROM people WHERE (first_name || ' ' || last_name) = 'John Smith';
+</programlisting>
+   then it might be worth creating an index like this:
+<programlisting>
+CREATE INDEX people_names ON people ((first_name || ' ' || last_name));
+</programlisting>
   </para>
 
-  <tip>
-   <para>
-    The restrictions mentioned in the previous paragraph can easily be
-    worked around by defining a custom function to use in the index
-    definition that computes any desired result internally.
-   </para>
-  </tip>
+  <para>
+   The syntax of the <command>CREATE INDEX</> command normally requires
+   writing parentheses around index expressions, as shown in the second
+   example.  The parentheses may be omitted when the expression is just
+   a function call, as in the first example.
+  </para>
+
+  <para>
+   Index expressions are relatively expensive to maintain, since the
+   derived expression(s) must be computed for each row upon insertion
+   or whenever it is updated.  Therefore they should be used only when
+   queries that can use the index are very frequent.
+  </para>
  </sect1>
 
 
@@ -391,8 +401,8 @@ CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable>
    The operator class identifies the operators to be used by the index
    for that column.  For example, a B-tree index on the type <type>int4</type>
    would use the <literal>int4_ops</literal> class; this operator
-   class includes comparison functions for values of type <type>int4</type>.  In
-   practice the default operator class for the column's data type is
+   class includes comparison functions for values of type <type>int4</type>.
+   In practice the default operator class for the column's data type is
    usually sufficient.  The main point of having operator classes is
    that for some data types, there could be more than one meaningful
    ordering.  For example, we might want to sort a complex-number data
@@ -427,24 +437,25 @@ CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable>
       <literal>name_pattern_ops</literal> support B-tree indexes on
       the types <type>text</type>, <type>varchar</type>,
       <type>char</type>, and <type>name</type>, respectively.  The
-      difference to the ordinary operator classes is that the values
+      difference from the ordinary operator classes is that the values
       are compared strictly character by character rather than
       according to the locale-specific collation rules.  This makes
       these operator classes suitable for use by queries involving
       pattern matching expressions (<literal>LIKE</literal> or POSIX
       regular expressions) if the server does not use the standard
-      <quote>C</quote> locale.  As an example, to index a
+      <quote>C</quote> locale.  As an example, you might index a
       <type>varchar</type> column like this:
 <programlisting>
 CREATE INDEX test_index ON test_table (col varchar_pattern_ops);
 </programlisting>
-      If you do use the C locale, you should instead create an index
-      with the default operator class.  Also note that you should
+      If you do use the C locale, you may instead create an index
+      with the default operator class, and it will still be useful
+      for pattern-matching queries.  Also note that you should
       create an index with the default operator class if you want
       queries involving ordinary comparisons to use an index.  Such
       queries cannot use the
       <literal><replaceable>xxx</replaceable>_pattern_ops</literal>
-      operator classes.  It is possible, however, to create multiple
+      operator classes.  It is allowed to create multiple
       indexes on the same column with different operator classes.
      </para>
     </listitem>
index 2aa0c07ed1bc15a762a1fb07f45fd3ec68cb9541..2f51d50cf3e49e41bddabcaf0327cbba3e17875e 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$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"> 
@@ -136,9 +136,10 @@ END;
    <para>
     Except for input/output conversion and calculation functions
     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
-    them to define operators or use them in functional indexes.
+    them to define operators or use them in index expressions.
    </para>
 
   <sect2 id="plpgsql-advantages">
index 2c6a10ce3dfb8383fba1347341077f942c0044bb..ff60aaa3b0102f61d71044864348fc5dbd012175 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$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
 -->
 
@@ -16,12 +16,8 @@ PostgreSQL documentation
 
  <refsynopsisdiv>
 <synopsis>
-CREATE [ UNIQUE ] INDEX <replaceable class="parameter">index_name</replaceable> ON <replaceable class="parameter">table</replaceable>
-    [ USING <replaceable class="parameter">method</replaceable> ] ( <replaceable class="parameter">column</replaceable> [ <replaceable class="parameter">ops_name</replaceable> ] [, ...] )
-    [ WHERE <replaceable class="parameter">predicate</replaceable> ]
-
-CREATE [ UNIQUE ] INDEX <replaceable class="parameter">index_name</replaceable> ON <replaceable class="parameter">table</replaceable>
-    [ USING <replaceable class="parameter">method</replaceable> ] ( <replaceable class="parameter">func_name</replaceable>( <replaceable class="parameter">column</replaceable> [, ... ]) [ <replaceable class="parameter">ops_name</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">column</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ <replaceable class="parameter">opclass</replaceable> ] [, ...] )
     [ WHERE <replaceable class="parameter">predicate</replaceable> ]
 </synopsis>
  </refsynopsisdiv>
@@ -32,25 +28,22 @@ CREATE [ UNIQUE ] INDEX <replaceable class="parameter">index_name</replaceable>
   <para>
    <command>CREATE INDEX</command> constructs an index <replaceable
    class="parameter">index_name</replaceable> on the specified table.
-   Indexes are primarily used to enhance database performance.  But
-   inappropriate use will result in slower performance.
+   Indexes are primarily used to enhance database performance (though
+   inappropriate use will result in slower performance).
   </para>
 
   <para>
-   In the first syntax shown above, the key field(s) for the
-   index are specified as column names.
+   The key field(s) for the index are specified as column names,
+   or alternatively as expressions written in parentheses.
    Multiple fields can be specified if the index method supports
    multicolumn indexes.
   </para>
 
   <para>
-   In the second syntax shown above, an index is defined on the result
-   of a user-specified function <replaceable
-   class="parameter">func_name</replaceable> applied to one or more
-   columns of a single table. These <firstterm>functional
-   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
+   An index field can be an expression computed from the values of
+   one or more columns of the table row.  This feature can be used
+   to obtain fast access to data based on some transformation of
+   the basic data. For example, an index computed on
    <literal>upper(col)</> would allow the clause
    <literal>WHERE upper(col) = 'JIM'</> to use an index.
   </para>
@@ -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,
     not only the one(s) being indexed).  Presently, subqueries and
     aggregate expressions are also forbidden in <literal>WHERE</literal>.
+    The same restrictions apply to index fields that are expressions.
   </para>
 
   <para>
@@ -92,8 +86,8 @@ CREATE [ UNIQUE ] INDEX <replaceable class="parameter">index_name</replaceable>
    their arguments and never on any outside influence (such as
    the contents of another table or the current time).  This restriction
    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
-   when you create it.
+   user-defined function in an index expression or <literal>WHERE</literal>
+   clause, remember to mark the function immutable when you create it.
   </para>
  </refsect1>
 
@@ -156,19 +150,22 @@ CREATE [ UNIQUE ] INDEX <replaceable class="parameter">index_name</replaceable>
      </varlistentry>
 
      <varlistentry>
-      <term><replaceable class="parameter">ops_name</replaceable></term>
+      <term><replaceable class="parameter">expression</replaceable></term>
       <listitem>
        <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>
       </listitem>
      </varlistentry>
 
      <varlistentry>
-      <term><replaceable class="parameter">func_name</replaceable></term>
+      <term><replaceable class="parameter">opclass</replaceable></term>
       <listitem>
        <para>
-   A function, which returns a value that can be indexed.
+   The name of an operator class. See below for details.
        </para>
       </listitem>
      </varlistentry>
@@ -177,7 +174,7 @@ CREATE [ UNIQUE ] INDEX <replaceable class="parameter">index_name</replaceable>
       <term><replaceable class="parameter">predicate</replaceable></term>
       <listitem>
        <para>
-   Defines the constraint expression for a partial index.
+   The constraint expression for a partial index.
        </para>
       </listitem>
      </varlistentry>
index 45ba598bd6798579a3caeb3982cfe0161f2fc074..db59de76f6475a4726afcffe2258051ac4aa5226 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$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">
@@ -24,6 +24,7 @@ CDATA means the content is "SGML-free", so you can write without
 worries about funny characters.
 -->
 <literallayout><![CDATA[
+Functional indexes have been generalized into expressional indexes
 CHAR(n) to TEXT conversion automatically strips trailing blanks
 Pattern matching operations can use indexes regardless of locale
 New frontend/backend protocol supports many long-requested features
index ee6246977763255aeea8ea24dcdb84e2bf2fcd59..2f64414aee2489a918479a6ad2d8891bce98ad70 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * 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:
                {
                    IndexElem *n = makeNode(IndexElem);
                    n->name = LexIDStr($1);
-                   n->funcname = n->args = NIL; /* no func indexes */
+                   n->expr = NULL;
                    n->opclass = makeList1(makeString(LexIDStr($2)));
                    $$ = n;
                }
index c903afaa003941a6fa99f7b672327e627d54558c..8539537f5195da4975b7e4f3fd96eefd3ba890d0 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.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,
    newind->il_info = (IndexInfo *) palloc(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 */
    newind->il_info->ii_Predicate = (List *)
        copyObject(indexInfo->ii_Predicate);
index 37282aab7f3f77cab75f2200a48fd99fc2247ca1..d57f645b281a85dfb32e18e6c6a1d3b72c0a3c64 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * 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,
    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.
  *
index 4c9c98e38562cc757b38449d626a85411974f368..fe57ab7bad3d343ca9d4c53247c3b95f6ac9426f 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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
@@ -1885,41 +1885,21 @@ RemoveStatistics(Relation rel, AttrNumber attnum)
 static void
 RelationTruncateIndexes(Oid heapId)
 {
-   Relation    indexRelation;
-   ScanKeyData entry;
-   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;
+   Relation    heapRelation;
+   List       *indlist;
 
-       /*
-        * For each index, fetch info needed for index_build
-        */
-       indexId = indexform->indexrelid;
-       indexInfo = BuildIndexInfo(indexform);
+   /*
+    * Open the heap rel.  We need grab no lock because we assume
+    * heap_truncate is holding an exclusive lock on the heap rel.
+    */
+   heapRelation = heap_open(heapId, NoLock);
 
-       /*
-        * We have to re-open the heap rel each time through this loop
-        * because index_build will close it again.  We need grab no lock,
-        * however, because we assume heap_truncate is holding an
-        * exclusive lock on the heap rel.
-        */
-       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 */
        currentIndex = index_open(indexId);
@@ -1927,6 +1907,9 @@ RelationTruncateIndexes(Oid heapId)
        /* Obtain exclusive lock on it, just to be sure */
        LockRelation(currentIndex, AccessExclusiveLock);
 
+       /* Fetch info needed for index_build */
+       indexInfo = BuildIndexInfo(currentIndex);
+
        /*
         * Drop any buffers associated with this index. If they're dirty,
         * they're just dropped without bothering to flush to disk.
@@ -1943,13 +1926,14 @@ RelationTruncateIndexes(Oid heapId)
 
        /*
         * 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 */
-   systable_endscan(scan);
-   heap_close(indexRelation, AccessShareLock);
+   /* Finish by closing the heap rel again */
+   heap_close(heapRelation, NoLock);
 }
 
 /*
index 3533b24ca781ad4ff4d5dd345cbfd8c8fa09ebd2..1399f960f71d0411e8b42a84ffb9787c9a2f5779 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.209 2003/02/19 04:06:28 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.210 2003/05/28 16:03:56 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -42,6 +42,7 @@
 #include "miscadmin.h"
 #include "optimizer/clauses.h"
 #include "optimizer/prep.h"
+#include "parser/parse_expr.h"
 #include "parser/parse_func.h"
 #include "storage/sinval.h"
 #include "storage/smgr.h"
    ((natts) * AVG_ATTR_SIZE + MAXALIGN(sizeof(HeapTupleHeaderData))))
 
 /* non-export function prototypes */
-static TupleDesc BuildFuncTupleDesc(Oid funcOid,
-                  Oid *classObjectId);
 static TupleDesc ConstructTupleDescriptor(Relation heapRelation,
-                        int numatts, AttrNumber *attNums,
-                        Oid *classObjectId);
+                                         IndexInfo *indexInfo,
+                                         Oid *classObjectId);
 static void UpdateRelationRelation(Relation indexRelation);
 static void InitializeAttributeOids(Relation indexRelation,
                        int numatts, Oid indexoid);
@@ -98,95 +97,18 @@ IsReindexProcessing(void)
    return reindexing;
 }
 
-static TupleDesc
-BuildFuncTupleDesc(Oid funcOid,
-                  Oid *classObjectId)
-{
-   TupleDesc   funcTupDesc;
-   HeapTuple   tuple;
-   Oid         keyType;
-   Oid         retType;
-   Form_pg_type typeTup;
-
-   /*
-    * Allocate and zero a tuple descriptor for a one-column tuple.
-    */
-   funcTupDesc = CreateTemplateTupleDesc(1, false);
-   funcTupDesc->attrs[0] = (Form_pg_attribute) palloc0(ATTRIBUTE_TUPLE_SIZE);
-
-   /*
-    * Lookup the function to get its name and return type.
-    */
-   tuple = SearchSysCache(PROCOID,
-                          ObjectIdGetDatum(funcOid),
-                          0, 0, 0);
-   if (!HeapTupleIsValid(tuple))
-       elog(ERROR, "Function %u does not exist", funcOid);
-   retType = ((Form_pg_proc) GETSTRUCT(tuple))->prorettype;
-
-   /*
-    * make the attributes name the same as the functions
-    */
-   namestrcpy(&funcTupDesc->attrs[0]->attname,
-              NameStr(((Form_pg_proc) GETSTRUCT(tuple))->proname));
-
-   ReleaseSysCache(tuple);
-
-   /*
-    * Check the opclass to see if it provides a keytype (overriding the
-    * function result type).
-    */
-   tuple = SearchSysCache(CLAOID,
-                          ObjectIdGetDatum(classObjectId[0]),
-                          0, 0, 0);
-   if (!HeapTupleIsValid(tuple))
-       elog(ERROR, "Opclass %u does not exist", classObjectId[0]);
-   keyType = ((Form_pg_opclass) GETSTRUCT(tuple))->opckeytype;
-   ReleaseSysCache(tuple);
-
-   if (!OidIsValid(keyType))
-       keyType = retType;
-
-   /*
-    * Lookup the key type in pg_type for the type length etc.
-    */
-   tuple = SearchSysCache(TYPEOID,
-                          ObjectIdGetDatum(keyType),
-                          0, 0, 0);
-   if (!HeapTupleIsValid(tuple))
-       elog(ERROR, "Type %u does not exist", keyType);
-   typeTup = (Form_pg_type) GETSTRUCT(tuple);
-
-   /*
-    * Assign some of the attributes values. Leave the rest as 0.
-    */
-   funcTupDesc->attrs[0]->attnum = 1;
-   funcTupDesc->attrs[0]->atttypid = keyType;
-   funcTupDesc->attrs[0]->attlen = typeTup->typlen;
-   funcTupDesc->attrs[0]->attbyval = typeTup->typbyval;
-   funcTupDesc->attrs[0]->attstorage = typeTup->typstorage;
-   funcTupDesc->attrs[0]->attalign = typeTup->typalign;
-   funcTupDesc->attrs[0]->attcacheoff = -1;
-   funcTupDesc->attrs[0]->atttypmod = -1;
-   funcTupDesc->attrs[0]->attislocal = true;
-
-   ReleaseSysCache(tuple);
-
-   return funcTupDesc;
-}
-
-/* ----------------------------------------------------------------
+/*
  *     ConstructTupleDescriptor
  *
- * Build an index tuple descriptor for a new index (plain not functional)
- * ----------------------------------------------------------------
+ * Build an index tuple descriptor for a new index
  */
 static TupleDesc
 ConstructTupleDescriptor(Relation heapRelation,
-                        int numatts,
-                        AttrNumber *attNums,
+                        IndexInfo *indexInfo,
                         Oid *classObjectId)
 {
+   int         numatts = indexInfo->ii_NumIndexAttrs;
+   List       *indexprs = indexInfo->ii_Expressions;
    TupleDesc   heapTupDesc;
    TupleDesc   indexTupDesc;
    int         natts;          /* #atts in heap rel --- for error checks */
@@ -198,69 +120,109 @@ ConstructTupleDescriptor(Relation heapRelation,
    /*
     * allocate the new tuple descriptor
     */
-
    indexTupDesc = CreateTemplateTupleDesc(numatts, false);
 
-   /* ----------------
-    *    for each attribute we are indexing, obtain its attribute
-    *    tuple form from either the static table of system attribute
-    *    tuple forms or the relation tuple descriptor
-    * ----------------
+   /*
+    * For simple index columns, we copy the pg_attribute row from the
+    * parent relation and modify it as necessary.  For expressions we
+    * have to cons up a pg_attribute row the hard way.
     */
    for (i = 0; i < numatts; i++)
    {
-       AttrNumber  atnum;      /* attributeNumber[attributeOffset] */
-       Form_pg_attribute from;
+       AttrNumber  atnum = indexInfo->ii_KeyAttrNumbers[i];
        Form_pg_attribute to;
        HeapTuple   tuple;
+       Form_pg_type typeTup;
        Oid         keyType;
 
-       /*
-        * get the attribute number and make sure it's valid; determine
-        * which attribute descriptor to copy
-        */
-       atnum = attNums[i];
+       indexTupDesc->attrs[i] = to =
+           (Form_pg_attribute) palloc0(ATTRIBUTE_TUPLE_SIZE);
 
-       if (!AttrNumberIsForUserDefinedAttr(atnum))
+       if (atnum != 0)
        {
+           /* Simple index column */
+           Form_pg_attribute from;
+
+           if (atnum < 0)
+           {
+               /*
+                * here we are indexing on a system attribute (-1...-n)
+                */
+               from = SystemAttributeDefinition(atnum,
+                                                heapRelation->rd_rel->relhasoids);
+           }
+           else
+           {
+               /*
+                * here we are indexing on a normal attribute (1...n)
+                */
+               if (atnum > natts)
+                   elog(ERROR, "cannot create index: column %d does not exist",
+                        atnum);
+               from = heapTupDesc->attrs[AttrNumberGetAttrOffset(atnum)];
+           }
+
+           /*
+            * now that we've determined the "from", let's copy the tuple desc
+            * data...
+            */
+           memcpy(to, from, ATTRIBUTE_TUPLE_SIZE);
+
            /*
-            * here we are indexing on a system attribute (-1...-n)
+            * Fix the stuff that should not be the same as the underlying
+            * attr
             */
-           from = SystemAttributeDefinition(atnum,
-                                      heapRelation->rd_rel->relhasoids);
+           to->attnum = i + 1;
+
+           to->attstattarget = 0;
+           to->attcacheoff = -1;
+           to->attnotnull = false;
+           to->atthasdef = false;
+           to->attislocal = true;
+           to->attinhcount = 0;
        }
        else
        {
+           /* Expressional index */
+           Node       *indexkey;
+
+           if (indexprs == NIL)
+               elog(ERROR, "too few entries in indexprs list");
+           indexkey = (Node *) lfirst(indexprs);
+           indexprs = lnext(indexprs);
+
            /*
-            * here we are indexing on a normal attribute (1...n)
+            * Make the attribute's name "pg_expresssion_nnn" (maybe think
+            * of something better later)
             */
-           if (atnum > natts)
-               elog(ERROR, "cannot create index: column %d does not exist",
-                    atnum);
+           sprintf(NameStr(to->attname), "pg_expression_%d", i + 1);
 
-           from = heapTupDesc->attrs[AttrNumberGetAttrOffset(atnum)];
-       }
-
-       /*
-        * now that we've determined the "from", let's copy the tuple desc
-        * data...
-        */
-       indexTupDesc->attrs[i] = to =
-           (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
-       memcpy(to, from, ATTRIBUTE_TUPLE_SIZE);
+           /*
+            * Lookup the expression type in pg_type for the type length etc.
+            */
+           keyType = exprType(indexkey);
+           tuple = SearchSysCache(TYPEOID,
+                                  ObjectIdGetDatum(keyType),
+                                  0, 0, 0);
+           if (!HeapTupleIsValid(tuple))
+               elog(ERROR, "Type %u does not exist", keyType);
+           typeTup = (Form_pg_type) GETSTRUCT(tuple);
 
-       /*
-        * Fix the stuff that should not be the same as the underlying
-        * attr
-        */
-       to->attnum = i + 1;
+           /*
+            * Assign some of the attributes values. Leave the rest as 0.
+            */
+           to->attnum = i + 1;
+           to->atttypid = keyType;
+           to->attlen = typeTup->typlen;
+           to->attbyval = typeTup->typbyval;
+           to->attstorage = typeTup->typstorage;
+           to->attalign = typeTup->typalign;
+           to->attcacheoff = -1;
+           to->atttypmod = -1;
+           to->attislocal = true;
 
-       to->attstattarget = 0;
-       to->attcacheoff = -1;
-       to->attnotnull = false;
-       to->atthasdef = false;
-       to->attislocal = true;
-       to->attinhcount = 0;
+           ReleaseSysCache(tuple);
+       }
 
        /*
         * We do not yet have the correct relation OID for the index, so
@@ -284,8 +246,6 @@ ConstructTupleDescriptor(Relation heapRelation,
        if (OidIsValid(keyType) && keyType != to->atttypid)
        {
            /* index value and heap value have different types */
-           Form_pg_type typeTup;
-
            tuple = SearchSysCache(TYPEOID,
                                   ObjectIdGetDatum(keyType),
                                   0, 0, 0);
@@ -421,6 +381,7 @@ UpdateIndexRelation(Oid indexoid,
 {
    int16       indkey[INDEX_MAX_KEYS];
    Oid         indclass[INDEX_MAX_KEYS];
+   Datum       exprsDatum;
    Datum       predDatum;
    Datum       values[Natts_pg_index];
    char        nulls[Natts_pg_index];
@@ -430,20 +391,32 @@ UpdateIndexRelation(Oid indexoid,
 
    /*
     * Copy the index key and opclass info into zero-filled vectors
-    *
-    * (zero filling is essential 'cause we don't store the number of
-    * index columns explicitly in pg_index, which is pretty grotty...)
     */
    MemSet(indkey, 0, sizeof(indkey));
-   for (i = 0; i < indexInfo->ii_NumKeyAttrs; i++)
-       indkey[i] = indexInfo->ii_KeyAttrNumbers[i];
-
    MemSet(indclass, 0, sizeof(indclass));
    for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
+   {
+       indkey[i] = indexInfo->ii_KeyAttrNumbers[i];
        indclass[i] = classOids[i];
+   }
+
+   /*
+    * Convert the index expressions (if any) to a text datum
+    */
+   if (indexInfo->ii_Expressions != NIL)
+   {
+       char       *exprsString;
+
+       exprsString = nodeToString(indexInfo->ii_Expressions);
+       exprsDatum = DirectFunctionCall1(textin,
+                                        CStringGetDatum(exprsString));
+       pfree(exprsString);
+   }
+   else
+       exprsDatum = (Datum) 0;
 
    /*
-    * Convert the index-predicate (if any) to a text datum
+    * Convert the index predicate (if any) to a text datum
     */
    if (indexInfo->ii_Predicate != NIL)
    {
@@ -455,8 +428,7 @@ UpdateIndexRelation(Oid indexoid,
        pfree(predString);
    }
    else
-       predDatum = DirectFunctionCall1(textin,
-                                       CStringGetDatum(""));
+       predDatum = (Datum) 0;
 
    /*
     * open the system catalog index relation
@@ -470,14 +442,18 @@ UpdateIndexRelation(Oid indexoid,
 
    values[Anum_pg_index_indexrelid - 1] = ObjectIdGetDatum(indexoid);
    values[Anum_pg_index_indrelid - 1] = ObjectIdGetDatum(heapoid);
-   values[Anum_pg_index_indproc - 1] = ObjectIdGetDatum(indexInfo->ii_FuncOid);
    values[Anum_pg_index_indkey - 1] = PointerGetDatum(indkey);
    values[Anum_pg_index_indclass - 1] = PointerGetDatum(indclass);
-   values[Anum_pg_index_indisclustered - 1] = BoolGetDatum(false);
+   values[Anum_pg_index_indnatts - 1] = Int16GetDatum(indexInfo->ii_NumIndexAttrs);
    values[Anum_pg_index_indisunique - 1] = BoolGetDatum(indexInfo->ii_Unique);
    values[Anum_pg_index_indisprimary - 1] = BoolGetDatum(primary);
-   values[Anum_pg_index_indreference - 1] = ObjectIdGetDatum(InvalidOid);
+   values[Anum_pg_index_indisclustered - 1] = BoolGetDatum(false);
+   values[Anum_pg_index_indexprs - 1] = exprsDatum;
+   if (exprsDatum == (Datum) 0)
+       nulls[Anum_pg_index_indexprs - 1] = 'n';
    values[Anum_pg_index_indpred - 1] = predDatum;
+   if (predDatum == (Datum) 0)
+       nulls[Anum_pg_index_indpred - 1] = 'n';
 
    tuple = heap_formtuple(RelationGetDescr(pg_index), values, nulls);
 
@@ -538,8 +514,7 @@ index_create(Oid heapRelationId,
    /*
     * check parameters
     */
-   if (indexInfo->ii_NumIndexAttrs < 1 ||
-       indexInfo->ii_NumKeyAttrs < 1)
+   if (indexInfo->ii_NumIndexAttrs < 1)
        elog(ERROR, "must index at least one column");
 
    if (!allow_system_table_mods &&
@@ -564,16 +539,9 @@ index_create(Oid heapRelationId,
    /*
     * construct tuple descriptor for index tuples
     */
-   if (OidIsValid(indexInfo->ii_FuncOid))
-       indexTupDesc = BuildFuncTupleDesc(indexInfo->ii_FuncOid,
-                                         classObjectId);
-   else
-       indexTupDesc = ConstructTupleDescriptor(heapRelation,
-                                               indexInfo->ii_NumKeyAttrs,
-                                           indexInfo->ii_KeyAttrNumbers,
-                                               classObjectId);
-
-   indexTupDesc->tdhasoid = false;
+   indexTupDesc = ConstructTupleDescriptor(heapRelation,
+                                           indexInfo,
+                                           classObjectId);
 
    /*
     * create the index relation's relcache entry and physical disk file.
@@ -675,6 +643,10 @@ index_create(Oid heapRelationId,
                constraintType = 0;     /* keep compiler quiet */
            }
 
+           /* Shouldn't have any expressions */
+           if (indexInfo->ii_Expressions)
+               elog(ERROR, "constraints can't have index expressions");
+
            conOid = CreateConstraintEntry(indexRelationName,
                                           namespaceId,
                                           constraintType,
@@ -682,7 +654,7 @@ index_create(Oid heapRelationId,
                                           false,       /* isDeferred */
                                           heapRelationId,
                                           indexInfo->ii_KeyAttrNumbers,
-                                          indexInfo->ii_NumKeyAttrs,
+                                          indexInfo->ii_NumIndexAttrs,
                                           InvalidOid,  /* no domain */
                                           InvalidOid,  /* no foreign key */
                                           NULL,
@@ -703,13 +675,17 @@ index_create(Oid heapRelationId,
        }
        else
        {
-           for (i = 0; i < indexInfo->ii_NumKeyAttrs; i++)
+           /* Create auto dependencies on simply-referenced columns */
+           for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
            {
-               referenced.classId = RelOid_pg_class;
-               referenced.objectId = heapRelationId;
-               referenced.objectSubId = indexInfo->ii_KeyAttrNumbers[i];
-
-               recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
+               if (indexInfo->ii_KeyAttrNumbers[i] != 0)
+               {
+                   referenced.classId = RelOid_pg_class;
+                   referenced.objectId = heapRelationId;
+                   referenced.objectSubId = indexInfo->ii_KeyAttrNumbers[i];
+
+                   recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
+               }
            }
        }
 
@@ -723,14 +699,24 @@ index_create(Oid heapRelationId,
            recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
        }
 
-       /* Store the dependency on the function (if appropriate) */
-       if (OidIsValid(indexInfo->ii_FuncOid))
+       /* Store dependencies on anything mentioned in index expressions */
+       if (indexInfo->ii_Expressions)
        {
-           referenced.classId = RelOid_pg_proc;
-           referenced.objectId = indexInfo->ii_FuncOid;
-           referenced.objectSubId = 0;
+           recordDependencyOnSingleRelExpr(&myself,
+                                           (Node *) indexInfo->ii_Expressions,
+                                           heapRelationId,
+                                           DEPENDENCY_NORMAL,
+                                           DEPENDENCY_AUTO);
+       }
 
-           recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+       /* Store dependencies on anything mentioned in predicate */
+       if (indexInfo->ii_Predicate)
+       {
+           recordDependencyOnSingleRelExpr(&myself,
+                                           (Node *) indexInfo->ii_Predicate,
+                                           heapRelationId,
+                                           DEPENDENCY_NORMAL,
+                                           DEPENDENCY_AUTO);
        }
    }
 
@@ -864,7 +850,7 @@ index_drop(Oid indexId)
 
 /* ----------------
  *     BuildIndexInfo
- *         Construct an IndexInfo record given the index's pg_index tuple
+ *         Construct an IndexInfo record for an open index
  *
  * IndexInfo stores the information about the index that's needed by
  * FormIndexDatum, which is used for both index_build() and later insertion
@@ -873,62 +859,31 @@ index_drop(Oid indexId)
  * ----------------
  */
 IndexInfo *
-BuildIndexInfo(Form_pg_index indexStruct)
+BuildIndexInfo(Relation index)
 {
    IndexInfo  *ii = makeNode(IndexInfo);
+   Form_pg_index indexStruct = index->rd_index;
    int         i;
    int         numKeys;
 
-   /*
-    * count the number of keys, and copy them into the IndexInfo
-    */
-   numKeys = 0;
-   for (i = 0; i < INDEX_MAX_KEYS &&
-        indexStruct->indkey[i] != InvalidAttrNumber; i++)
-   {
+   /* check the number of keys, and copy attr numbers into the IndexInfo */
+   numKeys = indexStruct->indnatts;
+   if (numKeys < 1 || numKeys > INDEX_MAX_KEYS)
+       elog(ERROR, "invalid indnatts %d for index %u",
+            numKeys, RelationGetRelid(index));
+   ii->ii_NumIndexAttrs = numKeys;
+   for (i = 0; i < numKeys; i++)
        ii->ii_KeyAttrNumbers[i] = indexStruct->indkey[i];
-       numKeys++;
-   }
-   ii->ii_NumKeyAttrs = numKeys;
 
-   /*
-    * Handle functional index.
-    *
-    * If we have a functional index then the number of attributes defined in
-    * the index must be 1 (the function's single return value). Otherwise
-    * it's same as number of keys.
-    */
-   ii->ii_FuncOid = indexStruct->indproc;
+   /* fetch any expressions needed for expressional indexes */
+   ii->ii_Expressions = RelationGetIndexExpressions(index);
+   ii->ii_ExpressionsState = NIL;
 
-   if (OidIsValid(indexStruct->indproc))
-   {
-       ii->ii_NumIndexAttrs = 1;
-       /* Do a lookup on the function, too */
-       fmgr_info(indexStruct->indproc, &ii->ii_FuncInfo);
-   }
-   else
-       ii->ii_NumIndexAttrs = numKeys;
-
-   /*
-    * If partial index, convert predicate into expression nodetree
-    */
-   if (VARSIZE(&indexStruct->indpred) > VARHDRSZ)
-   {
-       char       *predString;
-
-       predString = DatumGetCString(DirectFunctionCall1(textout,
-                               PointerGetDatum(&indexStruct->indpred)));
-       ii->ii_Predicate = stringToNode(predString);
-       ii->ii_PredicateState = NIL;
-       pfree(predString);
-   }
-   else
-   {
-       ii->ii_Predicate = NIL;
-       ii->ii_PredicateState = NIL;
-   }
+   /* fetch index predicate if any */
+   ii->ii_Predicate = RelationGetIndexPredicate(index);
+   ii->ii_PredicateState = NIL;
 
-   /* Other info */
+   /* other info */
    ii->ii_Unique = indexStruct->indisunique;
 
    return ii;
@@ -941,10 +896,14 @@ BuildIndexInfo(Form_pg_index indexStruct)
  * indexInfo       Info about the index
  * heapTuple       Heap tuple for which we must prepare an index entry
  * heapDescriptor  tupledesc for heap tuple
- * resultCxt       Temporary memory context for any palloc'd datums created
+ * estate          executor state for evaluating any index expressions
  * datum           Array of index Datums (output area)
  * nullv           Array of is-null indicators (output area)
  *
+ * When there are no index expressions, estate may be NULL.  Otherwise it
+ * must be supplied, *and* the ecxt_scantuple slot of its per-tuple expr
+ * context must point to the heap tuple passed in.
+ *
  * For largely historical reasons, we don't actually call index_formtuple()
  * here, we just prepare its input arrays datum[] and nullv[].
  * ----------------
@@ -953,69 +912,58 @@ void
 FormIndexDatum(IndexInfo *indexInfo,
               HeapTuple heapTuple,
               TupleDesc heapDescriptor,
-              MemoryContext resultCxt,
+              EState *estate,
               Datum *datum,
               char *nullv)
 {
-   MemoryContext oldContext;
+   List       *indexprs;
    int         i;
-   Datum       iDatum;
-   bool        isNull;
-
-   oldContext = MemoryContextSwitchTo(resultCxt);
 
-   if (OidIsValid(indexInfo->ii_FuncOid))
+   if (indexInfo->ii_Expressions != NIL &&
+       indexInfo->ii_ExpressionsState == NIL)
    {
-       /*
-        * Functional index --- compute the single index attribute
-        */
-       FunctionCallInfoData fcinfo;
-       bool        anynull = false;
+       /* First time through, set up expression evaluation state */
+       indexInfo->ii_ExpressionsState = (List *)
+           ExecPrepareExpr((Expr *) indexInfo->ii_Expressions,
+                           estate);
+       /* Check caller has set up context correctly */
+       Assert(GetPerTupleExprContext(estate)->ecxt_scantuple->val == heapTuple);
+   }
+   indexprs = indexInfo->ii_ExpressionsState;
 
-       MemSet(&fcinfo, 0, sizeof(fcinfo));
-       fcinfo.flinfo = &indexInfo->ii_FuncInfo;
-       fcinfo.nargs = indexInfo->ii_NumKeyAttrs;
+   for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
+   {
+       int         keycol = indexInfo->ii_KeyAttrNumbers[i];
+       Datum       iDatum;
+       bool        isNull;
 
-       for (i = 0; i < indexInfo->ii_NumKeyAttrs; i++)
-       {
-           fcinfo.arg[i] = heap_getattr(heapTuple,
-                                        indexInfo->ii_KeyAttrNumbers[i],
-                                        heapDescriptor,
-                                        &fcinfo.argnull[i]);
-           anynull |= fcinfo.argnull[i];
-       }
-       if (indexInfo->ii_FuncInfo.fn_strict && anynull)
+       if (keycol != 0)
        {
-           /* force a null result for strict function */
-           iDatum = (Datum) 0;
-           isNull = true;
+           /*
+            * Plain index column; get the value we need directly from the
+            * heap tuple.
+            */
+           iDatum = heap_getattr(heapTuple, keycol, heapDescriptor, &isNull);
        }
        else
        {
-           iDatum = FunctionCallInvoke(&fcinfo);
-           isNull = fcinfo.isnull;
-       }
-       datum[0] = iDatum;
-       nullv[0] = (isNull) ? 'n' : ' ';
-   }
-   else
-   {
-       /*
-        * Plain index --- for each attribute we need from the heap tuple,
-        * get the attribute and stick it into the datum and nullv arrays.
-        */
-       for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
-       {
-           iDatum = heap_getattr(heapTuple,
-                                 indexInfo->ii_KeyAttrNumbers[i],
-                                 heapDescriptor,
-                                 &isNull);
-           datum[i] = iDatum;
-           nullv[i] = (isNull) ? 'n' : ' ';
+           /*
+            * Index expression --- need to evaluate it.
+            */
+           if (indexprs == NIL)
+               elog(ERROR, "wrong number of index expressions");
+           iDatum = ExecEvalExprSwitchContext((ExprState *) lfirst(indexprs),
+                                              GetPerTupleExprContext(estate),
+                                              &isNull,
+                                              NULL);
+           indexprs = lnext(indexprs);
        }
+       datum[i] = iDatum;
+       nullv[i] = (isNull) ? 'n' : ' ';
    }
 
-   MemoryContextSwitchTo(oldContext);
+   if (indexprs != NIL)
+       elog(ERROR, "wrong number of index expressions");
 }
 
 
@@ -1501,7 +1449,7 @@ IndexBuildHeapScan(Relation heapRelation,
    heapDescriptor = RelationGetDescr(heapRelation);
 
    /*
-    * Need an EState for evaluation of functional-index functions
+    * Need an EState for evaluation of index expressions
     * and partial-index predicates.
     */
    estate = CreateExecutorState();
@@ -1510,9 +1458,9 @@ IndexBuildHeapScan(Relation heapRelation,
    /*
     * If this is a predicate (partial) index, we will need to evaluate
     * the predicate using ExecQual, which requires the current tuple to
-    * be in a slot of a TupleTable.
+    * be in a slot of a TupleTable.  Likewise if there are any expressions.
     */
-   if (indexInfo->ii_Predicate != NIL)
+   if (indexInfo->ii_Predicate != NIL || indexInfo->ii_Expressions != NIL)
    {
        tupleTable = ExecCreateTupleTable(1);
        slot = ExecAllocTableSlot(tupleTable);
@@ -1521,11 +1469,7 @@ IndexBuildHeapScan(Relation heapRelation,
        /* Arrange for econtext's scan tuple to be the tuple under test */
        econtext->ecxt_scantuple = slot;
 
-       /*
-        * Set up execution state for predicate.  Note: we mustn't attempt to
-        * cache this in the indexInfo, since we're building it in a transient
-        * EState.
-        */
+       /* Set up execution state for predicate. */
        predicate = (List *)
            ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
                            estate);
@@ -1676,12 +1620,12 @@ IndexBuildHeapScan(Relation heapRelation,
        /*
         * For the current heap tuple, extract all the attributes we use
         * in this index, and note which are null.  This also performs
-        * evaluation of the function, if this is a functional index.
+        * evaluation of any expressions needed.
         */
        FormIndexDatum(indexInfo,
                       heapTuple,
                       heapDescriptor,
-                      econtext->ecxt_per_tuple_memory,
+                      estate,
                       attdata,
                       nulls);
 
@@ -1703,6 +1647,10 @@ IndexBuildHeapScan(Relation heapRelation,
 
    FreeExecutorState(estate);
 
+   /* These may have been pointing to the now-gone estate */
+   indexInfo->ii_ExpressionsState = NIL;
+   indexInfo->ii_PredicateState = NIL;
+
    return reltuples;
 }
 
@@ -1807,7 +1755,7 @@ reindex_index(Oid indexId, bool force, bool inplace)
    }
 
    /* Fetch info needed for index_build */
-   indexInfo = BuildIndexInfo(iRel->rd_index);
+   indexInfo = BuildIndexInfo(iRel);
 
    if (inplace)
    {
index 27d66b4b2e8064a3b38730898ddd79a54f75dc29..45a60b0e8565e7f98e63ec9d4faad1e17899e19f 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * 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 $
  *
  *-------------------------------------------------------------------------
  */
  * In the current implementation, we share code for opening/closing the
  * indexes with execUtils.c.  But we do not use ExecInsertIndexTuples,
  * 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
- * functional indexes very well (the code will work, but will leak memory
- * intraquery, because the index function is called in the per-query context
- * 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.
+ * do not support partial or expressional indexes on system catalogs.
+ * This could be fixed with localized changes here if we wanted to pay
+ * the extra overhead of building an EState.
  */
 CatalogIndexState
 CatalogOpenIndexes(Relation heapRel)
@@ -99,7 +97,11 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple)
 
        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);
 
        /*
@@ -109,7 +111,7 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple)
        FormIndexDatum(indexInfo,
                       heapTuple,
                       heapDescriptor,
-                      CurrentMemoryContext,
+                      NULL,    /* no expression eval to do */
                       datum,
                       nullv);
 
index e724034969cde3a5eedfd123c055bb44296855f5..3822059dd35f4aa42e81f4faeca520bef1da64c3 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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,18 +250,11 @@ CreateConstraintEntry(const char *constraintName,
    {
        /*
         * Register dependencies from constraint to objects mentioned in
-        * CHECK expression.  We gin up a rather bogus rangetable list to
-        * handle any Vars in the constraint.
+        * CHECK expression.
         */
-       RangeTblEntry rte;
-
-       MemSet(&rte, 0, sizeof(rte));
-       rte.type = T_RangeTblEntry;
-       rte.rtekind = RTE_RELATION;
-       rte.relid = relId;
-
-       recordDependencyOnExpr(&conobject, conExpr, makeList1(&rte),
-                              DEPENDENCY_NORMAL);
+       recordDependencyOnSingleRelExpr(&conobject, conExpr, relId,
+                                       DEPENDENCY_NORMAL,
+                                       DEPENDENCY_NORMAL);
    }
 
    return conOid;
index 09ef0ac598e1ad41cba24b70b94fe0f772dc3271..87062782456854b3cdb6c3207fe80dde9f3156ab 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * 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)
 
            /* Start a new transaction for each relation. */
            StartTransactionCommand();
-           SetQuerySnapshot(); /* might be needed for functional index */
+           SetQuerySnapshot(); /* might be needed for functions in indexes */
            cluster_rel(rvtc, true);
            CommitTransactionCommand();
        }
@@ -320,7 +320,7 @@ cluster_rel(RelToCluster *rvtc, bool recheck)
     * seqscan pass over the table to copy the missing rows, but that seems
     * 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");
    if (!OldIndex->rd_am->amindexnulls)
    {
@@ -332,14 +332,24 @@ cluster_rel(RelToCluster *rvtc, bool recheck)
         * at the first column; multicolumn-capable AMs are *required* to
         * 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];
-       if (colno > 0)          /* system columns are non-null */
+       if (colno > 0)
+       {
+           /* ordinary user attribute */
            if (!OldHeap->rd_att->attrs[colno - 1]->attnotnull)
                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",
                     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");
+       }
    }
 
    /*
@@ -557,43 +567,24 @@ get_indexattr_list(Relation OldHeap, Oid OldIndex)
    foreach(indlist, RelationGetIndexList(OldHeap))
    {
        Oid         indexOID = lfirsto(indlist);
-       HeapTuple   indexTuple;
-       HeapTuple   classTuple;
-       Form_pg_index indexForm;
-       Form_pg_class classForm;
+       Relation    oldIndex;
        IndexAttrs *attrs;
 
-       indexTuple = SearchSysCache(INDEXRELID,
-                                   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);
+       oldIndex = index_open(indexOID);
 
        attrs = (IndexAttrs *) palloc(sizeof(IndexAttrs));
        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 *)
            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);
        /* We adjust the isclustered attribute to correct new state */
        attrs->isclustered = (indexOID == OldIndex);
 
-       /* Name and access method of each index come from pg_class */
-       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);
+       index_close(oldIndex);
 
        /*
         * Cons the gathered data into the list.  We do not care about
index 6a371587368785016ba7d7a59457336bfa5a0eb4..4186a1457951285c3db1c00922557c927be4c3dc 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.99 2003/05/14 03:26:01 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.100 2003/05/28 16:03:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -32,6 +32,7 @@
 #include "optimizer/prep.h"
 #include "parser/parsetree.h"
 #include "parser/parse_coerce.h"
+#include "parser/parse_expr.h"
 #include "parser/parse_func.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/syscache.h"
 
 
-#define IsFuncIndex(ATTR_LIST) (((IndexElem*)lfirst(ATTR_LIST))->funcname != NIL)
-
 /* non-export function prototypes */
-static void CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid);
-static void FuncIndexArgs(IndexInfo *indexInfo, Oid *classOidP,
-             IndexElem *funcIndex,
-             Oid relId,
-             char *accessMethodName, Oid accessMethodId);
-static void NormIndexAttrs(IndexInfo *indexInfo, Oid *classOidP,
+static void CheckPredicate(List *predList);
+static void ComputeIndexAttrs(IndexInfo *indexInfo, Oid *classOidP,
               List *attList,
               Oid relId,
               char *accessMethodName, Oid accessMethodId);
-static Oid GetAttrOpClass(IndexElem *attribute, Oid attrType,
+static Oid GetIndexOpClass(List *opclass, Oid attrType,
               char *accessMethodName, Oid accessMethodId);
 static Oid GetDefaultOpClass(Oid attrType, Oid accessMethodId);
 
@@ -59,8 +54,8 @@ static Oid    GetDefaultOpClass(Oid attrType, Oid accessMethodId);
  * DefineIndex
  *     Creates a new index.
  *
- * 'attributeList' is a list of IndexElem specifying either a functional
- *     index or a list of attributes to index on.
+ * 'attributeList' is a list of IndexElem specifying columns and expressions
+ *     to index on.
  * 'predicate' is the qual specified in the where clause.
  * 'rangetable' is needed to interpret the predicate.
  */
@@ -155,6 +150,16 @@ DefineIndex(RangeVar *heapRelation,
 
    ReleaseSysCache(tuple);
 
+   /*
+    * If a range table was created then check that only the base rel is
+    * mentioned.
+    */
+   if (rangetable != NIL)
+   {
+       if (length(rangetable) != 1 || getrelid(1, rangetable) != relationId)
+           elog(ERROR, "index expressions and predicates may refer only to the base relation");
+   }
+
    /*
     * Convert the partial-index predicate from parsetree form to an
     * implicit-AND qual expression, for easier evaluation at runtime.
@@ -164,14 +169,14 @@ DefineIndex(RangeVar *heapRelation,
    if (predicate)
    {
        cnfPred = canonicalize_qual((Expr *) copyObject(predicate), true);
-       CheckPredicate(cnfPred, rangetable, relationId);
+       CheckPredicate(cnfPred);
    }
 
    /*
     * Check that all of the attributes in a primary key are marked
     * as not null, otherwise attempt to ALTER TABLE .. SET NOT NULL
     */
-   if (primary && !IsFuncIndex(attributeList))
+   if (primary)
    {
        List   *keys;
 
@@ -180,6 +185,9 @@ DefineIndex(RangeVar *heapRelation,
            IndexElem   *key = (IndexElem *) lfirst(keys);
            HeapTuple   atttuple;
 
+           if (!key->name)
+               elog(ERROR, "primary keys cannot be expressions");
+
            /* System attributes are never null, so no problem */
            if (SystemAttributeByName(key->name, rel->rd_rel->relhasoids))
                continue;
@@ -216,43 +224,16 @@ DefineIndex(RangeVar *heapRelation,
     * structure
     */
    indexInfo = makeNode(IndexInfo);
+   indexInfo->ii_NumIndexAttrs = numberOfAttributes;
+   indexInfo->ii_Expressions = NIL; /* for now */
+   indexInfo->ii_ExpressionsState = NIL;
    indexInfo->ii_Predicate = cnfPred;
    indexInfo->ii_PredicateState = NIL;
-   indexInfo->ii_FuncOid = InvalidOid;
    indexInfo->ii_Unique = unique;
 
-   if (IsFuncIndex(attributeList))
-   {
-       IndexElem  *funcIndex = (IndexElem *) lfirst(attributeList);
-       int         nargs;
-
-       /* Parser should have given us only one list item, but check */
-       if (numberOfAttributes != 1)
-           elog(ERROR, "Functional index can only have one attribute");
-
-       nargs = length(funcIndex->args);
-       if (nargs > INDEX_MAX_KEYS)
-           elog(ERROR, "Index function can take at most %d arguments",
-                INDEX_MAX_KEYS);
-
-       indexInfo->ii_NumIndexAttrs = 1;
-       indexInfo->ii_NumKeyAttrs = nargs;
-
-       classObjectId = (Oid *) palloc(sizeof(Oid));
-
-       FuncIndexArgs(indexInfo, classObjectId, funcIndex,
+   classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
+   ComputeIndexAttrs(indexInfo, classObjectId, attributeList,
                      relationId, accessMethodName, accessMethodId);
-   }
-   else
-   {
-       indexInfo->ii_NumIndexAttrs = numberOfAttributes;
-       indexInfo->ii_NumKeyAttrs = numberOfAttributes;
-
-       classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
-
-       NormIndexAttrs(indexInfo, classObjectId, attributeList,
-                      relationId, accessMethodName, accessMethodId);
-   }
 
    index_create(relationId, indexRelationName,
                 indexInfo, accessMethodId, classObjectId,
@@ -271,8 +252,7 @@ DefineIndex(RangeVar *heapRelation,
 
 /*
  * CheckPredicate
- *     Checks that the given list of partial-index predicates refer
- *     (via the given range table) only to the given base relation oid.
+ *     Checks that the given list of partial-index predicates is valid.
  *
  * This used to also constrain the form of the predicate to forms that
  * indxpath.c could do something with. However, that seems overly
@@ -281,14 +261,9 @@ DefineIndex(RangeVar *heapRelation,
  * any evaluatable predicate will work.  So accept any predicate here
  * (except ones requiring a plan), and let indxpath.c fend for itself.
  */
-
 static void
-CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid)
+CheckPredicate(List *predList)
 {
-   if (length(rangeTable) != 1 || getrelid(1, rangeTable) != baseRelOid)
-       elog(ERROR,
-        "Partial-index predicates may refer only to the base relation");
-
    /*
     * We don't currently support generation of an actual query plan for a
     * predicate, only simple scalar expressions; hence these
@@ -301,119 +276,19 @@ CheckPredicate(List *predList, List *rangeTable, Oid baseRelOid)
 
    /*
     * A predicate using mutable functions is probably wrong, for the same
-    * reasons that we don't allow a functional index to use one.
+    * reasons that we don't allow an index expression to use one.
     */
    if (contain_mutable_functions((Node *) predList))
        elog(ERROR, "Functions in index predicate must be marked IMMUTABLE");
 }
 
-
 static void
-FuncIndexArgs(IndexInfo *indexInfo,
-             Oid *classOidP,
-             IndexElem *funcIndex,
-             Oid relId,
-             char *accessMethodName,
-             Oid accessMethodId)
-{
-   Oid         argTypes[FUNC_MAX_ARGS];
-   List       *arglist;
-   int         nargs = 0;
-   int         i;
-   FuncDetailCode fdresult;
-   Oid         funcid;
-   Oid         rettype;
-   bool        retset;
-   Oid        *true_typeids;
-
-   /*
-    * process the function arguments, which are a list of T_String
-    * (someday ought to allow more general expressions?)
-    *
-    * Note caller already checked that list is not too long.
-    */
-   MemSet(argTypes, 0, sizeof(argTypes));
-
-   foreach(arglist, funcIndex->args)
-   {
-       char       *arg = strVal(lfirst(arglist));
-       HeapTuple   tuple;
-       Form_pg_attribute att;
-
-       tuple = SearchSysCacheAttName(relId, arg);
-       if (!HeapTupleIsValid(tuple))
-           elog(ERROR, "DefineIndex: column \"%s\" named in key does not exist", arg);
-       att = (Form_pg_attribute) GETSTRUCT(tuple);
-       indexInfo->ii_KeyAttrNumbers[nargs] = att->attnum;
-       argTypes[nargs] = att->atttypid;
-       ReleaseSysCache(tuple);
-       nargs++;
-   }
-
-   /*
-    * Lookup the function procedure to get its OID and result type.
-    *
-    * We rely on parse_func.c to find the correct function in the possible
-    * presence of binary-compatible types.  However, parse_func may do
-    * too much: it will accept a function that requires run-time coercion
-    * of input types, and the executor is not currently set up to support
-    * that.  So, check to make sure that the selected function has
-    * exact-match or binary-compatible input types.
-    */
-   fdresult = func_get_detail(funcIndex->funcname, funcIndex->args,
-                              nargs, argTypes,
-                              &funcid, &rettype, &retset,
-                              &true_typeids);
-   if (fdresult != FUNCDETAIL_NORMAL)
-   {
-       if (fdresult == FUNCDETAIL_AGGREGATE)
-           elog(ERROR, "DefineIndex: functional index may not use an aggregate function");
-       else if (fdresult == FUNCDETAIL_COERCION)
-           elog(ERROR, "DefineIndex: functional index must use a real function, not a type coercion"
-                "\n\tTry specifying the index opclass you want to use, instead");
-       else
-           func_error("DefineIndex", funcIndex->funcname, nargs, argTypes,
-                      NULL);
-   }
-
-   if (retset)
-       elog(ERROR, "DefineIndex: cannot index on a function returning a set");
-
-   for (i = 0; i < nargs; i++)
-   {
-       if (!IsBinaryCoercible(argTypes[i], true_typeids[i]))
-           func_error("DefineIndex", funcIndex->funcname, nargs, true_typeids,
-                      "Index function must be binary-compatible with table datatype");
-   }
-
-   /*
-    * Require that the function be marked immutable.  Using a mutable
-    * function for a functional index is highly questionable, since if
-    * you aren't going to get the same result for the same data every
-    * time, it's not clear what the index entries mean at all.
-    */
-   if (func_volatile(funcid) != PROVOLATILE_IMMUTABLE)
-       elog(ERROR, "DefineIndex: index function must be marked IMMUTABLE");
-
-   /* Process opclass, using func return type as default type */
-
-   classOidP[0] = GetAttrOpClass(funcIndex, rettype,
-                                 accessMethodName, accessMethodId);
-
-   /* OK, return results */
-
-   indexInfo->ii_FuncOid = funcid;
-   /* Need to do the fmgr function lookup now, too */
-   fmgr_info(funcid, &indexInfo->ii_FuncInfo);
-}
-
-static void
-NormIndexAttrs(IndexInfo *indexInfo,
-              Oid *classOidP,
-              List *attList,   /* list of IndexElem's */
-              Oid relId,
-              char *accessMethodName,
-              Oid accessMethodId)
+ComputeIndexAttrs(IndexInfo *indexInfo,
+                 Oid *classOidP,
+                 List *attList,    /* list of IndexElem's */
+                 Oid relId,
+                 char *accessMethodName,
+                 Oid accessMethodId)
 {
    List       *rest;
    int         attn = 0;
@@ -424,31 +299,75 @@ NormIndexAttrs(IndexInfo *indexInfo,
    foreach(rest, attList)
    {
        IndexElem  *attribute = (IndexElem *) lfirst(rest);
-       HeapTuple   atttuple;
-       Form_pg_attribute attform;
-
-       if (attribute->name == NULL)
-           elog(ERROR, "missing attribute for define index");
-
-       atttuple = SearchSysCacheAttName(relId, attribute->name);
-       if (!HeapTupleIsValid(atttuple))
-           elog(ERROR, "DefineIndex: attribute \"%s\" not found",
-                attribute->name);
-       attform = (Form_pg_attribute) GETSTRUCT(atttuple);
+       Oid         atttype;
 
-       indexInfo->ii_KeyAttrNumbers[attn] = attform->attnum;
+       if (attribute->name != NULL)
+       {
+           /* Simple index attribute */
+           HeapTuple   atttuple;
+           Form_pg_attribute attform;
+
+           Assert(attribute->expr == NULL);
+           atttuple = SearchSysCacheAttName(relId, attribute->name);
+           if (!HeapTupleIsValid(atttuple))
+               elog(ERROR, "DefineIndex: attribute \"%s\" not found",
+                    attribute->name);
+           attform = (Form_pg_attribute) GETSTRUCT(atttuple);
+           indexInfo->ii_KeyAttrNumbers[attn] = attform->attnum;
+           atttype = attform->atttypid;
+           ReleaseSysCache(atttuple);
+       }
+       else if (attribute->expr && IsA(attribute->expr, Var))
+       {
+           /* Tricky tricky, he wrote (column) ... treat as simple attr */
+           Var    *var = (Var *) attribute->expr;
 
-       classOidP[attn] = GetAttrOpClass(attribute, attform->atttypid,
-                                      accessMethodName, accessMethodId);
+           indexInfo->ii_KeyAttrNumbers[attn] = var->varattno;
+           atttype = get_atttype(relId, var->varattno);
+       }
+       else
+       {
+           /* Index expression */
+           Assert(attribute->expr != NULL);
+           indexInfo->ii_KeyAttrNumbers[attn] = 0; /* marks expression */
+           indexInfo->ii_Expressions = lappend(indexInfo->ii_Expressions,
+                                               attribute->expr);
+           atttype = exprType(attribute->expr);
+
+           /*
+            * We don't currently support generation of an actual query plan
+            * for an index expression, only simple scalar expressions;
+            * hence these restrictions.
+            */
+           if (contain_subplans(attribute->expr))
+               elog(ERROR, "Cannot use subselect in index expression");
+           if (contain_agg_clause(attribute->expr))
+               elog(ERROR, "Cannot use aggregate in index expression");
+
+           /*
+            * A expression using mutable functions is probably wrong,
+            * since if you aren't going to get the same result for the same
+            * data every time, it's not clear what the index entries mean at
+            * all.
+            */
+           if (contain_mutable_functions(attribute->expr))
+               elog(ERROR, "Functions in index expression must be marked IMMUTABLE");
+       }
 
-       ReleaseSysCache(atttuple);
+       classOidP[attn] = GetIndexOpClass(attribute->opclass,
+                                         atttype,
+                                         accessMethodName,
+                                         accessMethodId);
        attn++;
    }
 }
 
+/*
+ * Resolve possibly-defaulted operator class specification
+ */
 static Oid
-GetAttrOpClass(IndexElem *attribute, Oid attrType,
-              char *accessMethodName, Oid accessMethodId)
+GetIndexOpClass(List *opclass, Oid attrType,
+               char *accessMethodName, Oid accessMethodId)
 {
    char       *schemaname;
    char       *opcname;
@@ -456,7 +375,32 @@ GetAttrOpClass(IndexElem *attribute, Oid attrType,
    Oid         opClassId,
                opInputType;
 
-   if (attribute->opclass == NIL)
+   /*
+    * Release 7.0 removed network_ops, timespan_ops, and
+    * datetime_ops, so we ignore those opclass names
+    * 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(opclass) == 1)
+   {
+       char   *claname = strVal(lfirst(opclass));
+
+       if (strcmp(claname, "network_ops") == 0 ||
+           strcmp(claname, "timespan_ops") == 0 ||
+           strcmp(claname, "datetime_ops") == 0 ||
+           strcmp(claname, "lztext_ops") == 0 ||
+           strcmp(claname, "timestamp_ops") == 0)
+           opclass = NIL;
+   }
+
+   if (opclass == NIL)
    {
        /* no operator class specified, so find the default */
        opClassId = GetDefaultOpClass(attrType, accessMethodId);
@@ -473,7 +417,7 @@ GetAttrOpClass(IndexElem *attribute, Oid attrType,
     */
 
    /* deconstruct the name list */
-   DeconstructQualifiedName(attribute->opclass, &schemaname, &opcname);
+   DeconstructQualifiedName(opclass, &schemaname, &opcname);
 
    if (schemaname)
    {
@@ -501,7 +445,7 @@ GetAttrOpClass(IndexElem *attribute, Oid attrType,
 
    if (!HeapTupleIsValid(tuple))
        elog(ERROR, "DefineIndex: operator class \"%s\" not supported by access method \"%s\"",
-            NameListToString(attribute->opclass), accessMethodName);
+            NameListToString(opclass), accessMethodName);
 
    /*
     * Verify that the index operator class accepts this datatype.  Note
@@ -512,7 +456,7 @@ GetAttrOpClass(IndexElem *attribute, Oid attrType,
 
    if (!IsBinaryCoercible(attrType, opInputType))
        elog(ERROR, "operator class \"%s\" does not accept data type %s",
-        NameListToString(attribute->opclass), format_type_be(attrType));
+        NameListToString(opclass), format_type_be(attrType));
 
    ReleaseSysCache(tuple);
 
@@ -773,7 +717,7 @@ ReindexDatabase(const char *dbname, bool force, bool all)
    for (i = 0; i < relcnt; i++)
    {
        StartTransactionCommand();
-       SetQuerySnapshot();     /* might be needed for functional index */
+       SetQuerySnapshot();     /* might be needed for functions in indexes */
        if (reindex_relation(relids[i], force))
            elog(NOTICE, "relation %u was reindexed", relids[i]);
        CommitTransactionCommand();
index 5f60ab9cf016d9daefa63b412d6eab0773eb3d88..349fb8f391759c62aaefa1f421bd87adf7b6f71e 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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,
    Relation    attrelation;
    HeapTuple   atttup;
    Form_pg_attribute attform;
+   int         attnum;
    List       *indexoidlist;
    List       *indexoidscan;
 
@@ -1178,7 +1179,8 @@ renameatt(Oid myrelid,
             oldattname);
    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",
             oldattname);
 
@@ -1217,43 +1219,48 @@ renameatt(Oid myrelid,
    {
        Oid         indexoid = lfirsto(indexoidscan);
        HeapTuple   indextup;
+       Form_pg_index indexform;
+       int         i;
 
        /*
-        * First check to see if index is a functional index. If so, its
-        * column name is a function name and shouldn't be renamed here.
+        * Scan through index columns to see if there's any simple index
+        * entries for this attribute.  We ignore expressional entries.
         */
        indextup = SearchSysCache(INDEXRELID,
                                  ObjectIdGetDatum(indexoid),
                                  0, 0, 0);
        if (!HeapTupleIsValid(indextup))
            elog(ERROR, "renameatt: can't find index id %u", indexoid);
-       if (OidIsValid(((Form_pg_index) GETSTRUCT(indextup))->indproc))
-       {
-           ReleaseSysCache(indextup);
-           continue;
-       }
-       ReleaseSysCache(indextup);
+       indexform = (Form_pg_index) GETSTRUCT(indextup);
 
-       /*
-        * Okay, look to see if any column name of the index matches the
-        * old attribute name.
-        */
-       atttup = SearchSysCacheCopyAttName(indexoid, oldattname);
-       if (!HeapTupleIsValid(atttup))
-           continue;           /* Nope, so ignore it */
+       for (i = 0; i < indexform->indnatts; i++)
+       {
+           if (attnum != indexform->indkey[i])
+               continue;
+           /*
+            * Found one, rename it.
+            */
+           atttup = SearchSysCacheCopy(ATTNUM,
+                                       ObjectIdGetDatum(indexoid),
+                                       Int16GetDatum(i + 1),
+                                       0, 0);
+           if (!HeapTupleIsValid(atttup))
+               continue;       /* should we raise an error? */
+           /*
+            * Update the (copied) attribute tuple.
+            */
+           namestrcpy(&(((Form_pg_attribute) GETSTRUCT(atttup))->attname),
+                      newattname);
 
-       /*
-        * Update the (copied) attribute tuple.
-        */
-       namestrcpy(&(((Form_pg_attribute) GETSTRUCT(atttup))->attname),
-                  newattname);
+           simple_heap_update(attrelation, &atttup->t_self, atttup);
 
-       simple_heap_update(attrelation, &atttup->t_self, atttup);
+           /* keep system catalog indexes current */
+           CatalogUpdateIndexes(attrelation, atttup);
 
-       /* keep system catalog indexes current */
-       CatalogUpdateIndexes(attrelation, atttup);
+           heap_freetuple(atttup);
+       }
 
-       heap_freetuple(atttup);
+       ReleaseSysCache(indextup);
    }
 
    freeList(indexoidlist);
@@ -1986,8 +1993,7 @@ AlterTableAlterColumnDropNotNull(Oid myrelid, bool recurse,
             * Loop over each attribute in the primary key and see if it
             * matches the to-be-altered attribute
             */
-           for (i = 0; i < INDEX_MAX_KEYS &&
-                indexStruct->indkey[i] != InvalidAttrNumber; i++)
+           for (i = 0; i < indexStruct->indnatts; i++)
            {
                if (indexStruct->indkey[i] == attnum)
                    elog(ERROR, "ALTER TABLE: Attribute \"%s\" is in a primary key", colName);
@@ -3185,9 +3191,10 @@ transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
 
    /*
     * Now build the list of PK attributes from the indkey definition
+    * (we assume a primary key cannot have expressional elements)
     */
    *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];
 
@@ -3241,26 +3248,40 @@ transformFkeyCheckAttrs(Relation pkrel,
        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 &&
-           indexStruct->indproc == InvalidOid &&
-           VARSIZE(&indexStruct->indpred) <= VARHDRSZ)
+       if (indexStruct->indnatts == numattrs &&
+           indexStruct->indisunique &&
+           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
+            * order.  Check that each list is a subset of the other.
+            */
+           for (i = 0; i < numattrs; i++)
+           {
+               found = false;
+               for (j = 0; j < numattrs; j++)
+               {
+                   if (attnums[i] == indexStruct->indkey[j])
+                   {
+                       found = true;
+                       break;
+                   }
+               }
+               if (!found)
+                   break;
+           }
+           if (found)
            {
-               /*
-                * The given attnum list may match the index columns in any
-                * order.  Check that each list is a subset of the other.
-                */
                for (i = 0; i < numattrs; i++)
                {
                    found = false;
                    for (j = 0; j < numattrs; j++)
                    {
-                       if (attnums[i] == indexStruct->indkey[j])
+                       if (attnums[j] == indexStruct->indkey[i])
                        {
                            found = true;
                            break;
@@ -3269,23 +3290,6 @@ transformFkeyCheckAttrs(Relation pkrel,
                    if (!found)
                        break;
                }
-               if (found)
-               {
-                   for (i = 0; i < numattrs; i++)
-                   {
-                       found = false;
-                       for (j = 0; j < numattrs; j++)
-                       {
-                           if (attnums[j] == indexStruct->indkey[i])
-                           {
-                               found = true;
-                               break;
-                           }
-                       }
-                       if (!found)
-                           break;
-                   }
-               }
            }
        }
        ReleaseSysCache(indexTuple);
@@ -4020,12 +4024,12 @@ AlterTableCreateToastTable(Oid relOid, bool silent)
 
    indexInfo = makeNode(IndexInfo);
    indexInfo->ii_NumIndexAttrs = 2;
-   indexInfo->ii_NumKeyAttrs = 2;
    indexInfo->ii_KeyAttrNumbers[0] = 1;
    indexInfo->ii_KeyAttrNumbers[1] = 2;
+   indexInfo->ii_Expressions = NIL;
+   indexInfo->ii_ExpressionsState = NIL;
    indexInfo->ii_Predicate = NIL;
    indexInfo->ii_PredicateState = NIL;
-   indexInfo->ii_FuncOid = InvalidOid;
    indexInfo->ii_Unique = true;
 
    classObjectId[0] = OID_BTREE_OPS_OID;
index b5a8f7f3a175ffd7067971a51f9a72f84ac2d57a..93701b611eda99bd342efa6ce250c0d5e5864e3a 100644 (file)
@@ -13,7 +13,7 @@
  *
  *
  * 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)
            if (vacstmt->vacuum)
            {
                StartTransactionCommand();
-               SetQuerySnapshot(); /* might be needed for functional index */
+               SetQuerySnapshot(); /* might be needed for functions in indexes */
            }
            else
                old_context = MemoryContextSwitchTo(anl_context);
@@ -728,7 +728,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, char expected_relkind)
 
    /* Begin a transaction for vacuuming this relation */
    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
@@ -3028,7 +3028,10 @@ vac_is_partial_index(Relation indrel)
        return true;
 
    /* 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;
 }
 
 
index c06d951f91e6c11116069b264cb88929eca6f0d3..ca4ff192f442d07581b81445dce5553780a5f542 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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)
        if (!indexDesc->rd_am->amconcurrent)
            LockRelation(indexDesc, AccessExclusiveLock);
 
-       /*
-        * extract index key information from the index's pg_index tuple
-        */
-       ii = BuildIndexInfo(indexDesc->rd_index);
+       /* extract index key information from the index's pg_index info */
+       ii = BuildIndexInfo(indexDesc);
 
        relationDescs[i] = indexDesc;
        indexInfoArray[i] = ii;
@@ -797,7 +795,7 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 
    /*
     * 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).
     */
    econtext = GetPerTupleExprContext(estate);
@@ -844,11 +842,12 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
        /*
         * FormIndexDatum fills in its datum and null parameters with
         * attribute information taken from the given heap tuple.
+        * It also computes any expressions needed.
         */
        FormIndexDatum(indexInfo,
                       heapTuple,
                       heapDescriptor,
-                      econtext->ecxt_per_tuple_memory,
+                      estate,
                       datum,
                       nullv);
 
index 1dc79a4dc40834121169a4231b34f788097d1287..4a5f858d7b08e20d6b525f982f2688cde638d618 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * 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)
    IndexElem  *newnode = makeNode(IndexElem);
 
    COPY_STRING_FIELD(name);
-   COPY_NODE_FIELD(funcname);
-   COPY_NODE_FIELD(args);
+   COPY_NODE_FIELD(expr);
    COPY_NODE_FIELD(opclass);
 
    return newnode;
index 6b9cac028b990d87b128231ca10adf7f579c7188..ff5400f6ee8fb62ae759a43ee96a921691bf6e2c 100644 (file)
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * 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
 _equalIndexElem(IndexElem *a, IndexElem *b)
 {
    COMPARE_STRING_FIELD(name);
-   COMPARE_NODE_FIELD(funcname);
-   COMPARE_NODE_FIELD(args);
+   COMPARE_NODE_FIELD(expr);
    COMPARE_NODE_FIELD(opclass);
 
    return true;
index 377c6bd2297b54fbb4490ed8f9af6d35e4999ace..a1b238c93cf8bcbd098c47295bbeaea3db04a926 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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
  *   Every node type that can appear in stored rules' parsetrees *must*
@@ -1181,8 +1181,7 @@ _outIndexElem(StringInfo str, IndexElem *node)
    WRITE_NODE_TYPE("INDEXELEM");
 
    WRITE_STRING_FIELD(name);
-   WRITE_NODE_FIELD(funcname);
-   WRITE_NODE_FIELD(args);
+   WRITE_NODE_FIELD(expr);
    WRITE_NODE_FIELD(opclass);
 }
 
index 454e9c9260598355470e7648d2de863b12c47c3c..9319f8c147414b88376c22b6df2593cd28894e33 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.141 2003/05/27 17:49:46 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.142 2003/05/28 16:03:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 /*
  * DoneMatchingIndexKeys() - MACRO
- *
- * Formerly this looked at indexkeys, but that's the wrong thing for a
- * functional index.
  */
-#define DoneMatchingIndexKeys(indexkeys, classes) \
-   (classes[0] == InvalidOid)
+#define DoneMatchingIndexKeys(classes) (classes[0] == InvalidOid)
 
 #define is_indexable_operator(clause,opclass,indexkey_on_left) \
    (indexable_operator(clause,opclass,indexkey_on_left) != InvalidOid)
@@ -67,14 +63,14 @@ static List *group_clauses_by_indexkey_for_join(RelOptInfo *rel,
                                IndexOptInfo *index,
                                Relids outer_relids,
                                bool isouterjoin);
-static bool match_clause_to_indexkey(RelOptInfo *rel, IndexOptInfo *index,
-                                    int indexkey, Oid opclass, Expr *clause);
-static bool match_join_clause_to_indexkey(RelOptInfo *rel, IndexOptInfo *index,
-                        int indexkey, Oid opclass, Expr *clause);
+static bool match_clause_to_indexcol(RelOptInfo *rel, IndexOptInfo *index,
+                                    int indexcol, Oid opclass, Expr *clause);
+static bool match_join_clause_to_indexcol(RelOptInfo *rel, IndexOptInfo *index,
+                        int indexcol, Oid opclass, Expr *clause);
 static Oid indexable_operator(Expr *clause, Oid opclass,
                   bool indexkey_on_left);
 static bool pred_test(List *predicate_list, List *restrictinfo_list,
-         List *joininfo_list, int relvarno);
+         List *joininfo_list);
 static bool pred_test_restrict_list(Expr *predicate, List *restrictinfo_list);
 static bool pred_test_recurse_clause(Expr *predicate, Node *clause);
 static bool pred_test_recurse_pred(Expr *predicate, Node *clause);
@@ -83,10 +79,8 @@ static Relids indexable_outerrelids(RelOptInfo *rel, IndexOptInfo *index);
 static Path *make_innerjoin_index_path(Query *root,
                                       RelOptInfo *rel, IndexOptInfo *index,
                                       List *clausegroups);
-static bool match_index_to_operand(int indexkey, Node *operand,
+static bool match_index_to_operand(Node *operand, int indexcol,
                       RelOptInfo *rel, IndexOptInfo *index);
-static bool function_index_operand(Expr *funcOpnd, RelOptInfo *rel,
-                      IndexOptInfo *index);
 static bool match_special_index_operator(Expr *clause, Oid opclass,
                             bool indexkey_on_left);
 static List *expand_indexqual_condition(Expr *clause, Oid opclass);
@@ -149,8 +143,7 @@ create_index_paths(Query *root, RelOptInfo *rel)
         * predicate test.
         */
        if (index->indpred != NIL)
-           if (!pred_test(index->indpred, restrictinfo_list, joininfo_list,
-                          rel->relid))
+           if (!pred_test(index->indpred, restrictinfo_list, joininfo_list))
                continue;
 
        /*
@@ -371,7 +364,6 @@ match_or_subclause_to_indexkey(RelOptInfo *rel,
                               IndexOptInfo *index,
                               Expr *clause)
 {
-   int         indexkey = index->indexkeys[0];
    Oid         opclass = index->classlist[0];
 
    if (and_clause((Node *) clause))
@@ -380,14 +372,14 @@ match_or_subclause_to_indexkey(RelOptInfo *rel,
 
        foreach(item, ((BoolExpr *) clause)->args)
        {
-           if (match_clause_to_indexkey(rel, index, indexkey, opclass,
+           if (match_clause_to_indexcol(rel, index, 0, opclass,
                                         lfirst(item)))
                return true;
        }
        return false;
    }
    else
-       return match_clause_to_indexkey(rel, index, indexkey, opclass,
+       return match_clause_to_indexcol(rel, index, 0, opclass,
                                        clause);
 }
 
@@ -426,7 +418,7 @@ extract_or_indexqual_conditions(RelOptInfo *rel,
                                Expr *orsubclause)
 {
    List       *quals = NIL;
-   int        *indexkeys = index->indexkeys;
+   int         indexcol = 0;
    Oid        *classes = index->classlist;
 
    /*
@@ -437,7 +429,6 @@ extract_or_indexqual_conditions(RelOptInfo *rel,
     */
    do
    {
-       int         curIndxKey = indexkeys[0];
        Oid         curClass = classes[0];
        List       *clausegroup = NIL;
        List       *item;
@@ -448,16 +439,16 @@ extract_or_indexqual_conditions(RelOptInfo *rel,
            {
                Expr       *subsubclause = (Expr *) lfirst(item);
 
-               if (match_clause_to_indexkey(rel, index,
-                                            curIndxKey, curClass,
+               if (match_clause_to_indexcol(rel, index,
+                                            indexcol, curClass,
                                             subsubclause))
                    clausegroup = nconc(clausegroup,
                                        expand_indexqual_condition(subsubclause,
                                                                   curClass));
            }
        }
-       else if (match_clause_to_indexkey(rel, index,
-                                         curIndxKey, curClass,
+       else if (match_clause_to_indexcol(rel, index,
+                                         indexcol, curClass,
                                          orsubclause))
            clausegroup = expand_indexqual_condition(orsubclause, curClass);
 
@@ -471,8 +462,8 @@ extract_or_indexqual_conditions(RelOptInfo *rel,
            {
                RestrictInfo *rinfo = (RestrictInfo *) lfirst(item);
 
-               if (match_clause_to_indexkey(rel, index,
-                                            curIndxKey, curClass,
+               if (match_clause_to_indexcol(rel, index,
+                                            indexcol, curClass,
                                             rinfo->clause))
                    clausegroup = nconc(clausegroup,
                                        expand_indexqual_condition(rinfo->clause,
@@ -489,10 +480,10 @@ extract_or_indexqual_conditions(RelOptInfo *rel,
 
        quals = nconc(quals, clausegroup);
 
-       indexkeys++;
+       indexcol++;
        classes++;
 
-   } while (!DoneMatchingIndexKeys(indexkeys, classes));
+   } while (!DoneMatchingIndexKeys(classes));
 
    if (quals == NIL)
        elog(ERROR, "extract_or_indexqual_conditions: no matching clause");
@@ -531,7 +522,7 @@ group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index)
 {
    List       *clausegroup_list = NIL;
    List       *restrictinfo_list = rel->baserestrictinfo;
-   int        *indexkeys = index->indexkeys;
+   int         indexcol = 0;
    Oid        *classes = index->classlist;
 
    if (restrictinfo_list == NIL)
@@ -539,7 +530,6 @@ group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index)
 
    do
    {
-       int         curIndxKey = indexkeys[0];
        Oid         curClass = classes[0];
        List       *clausegroup = NIL;
        List       *i;
@@ -548,9 +538,9 @@ group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index)
        {
            RestrictInfo *rinfo = (RestrictInfo *) lfirst(i);
 
-           if (match_clause_to_indexkey(rel,
+           if (match_clause_to_indexcol(rel,
                                         index,
-                                        curIndxKey,
+                                        indexcol,
                                         curClass,
                                         rinfo->clause))
                clausegroup = lappend(clausegroup, rinfo);
@@ -565,10 +555,10 @@ group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index)
 
        clausegroup_list = lappend(clausegroup_list, clausegroup);
 
-       indexkeys++;
+       indexcol++;
        classes++;
 
-   } while (!DoneMatchingIndexKeys(indexkeys, classes));
+   } while (!DoneMatchingIndexKeys(classes));
 
    return clausegroup_list;
 }
@@ -592,12 +582,11 @@ group_clauses_by_indexkey_for_join(RelOptInfo *rel, IndexOptInfo *index,
 {
    List       *clausegroup_list = NIL;
    bool        jfound = false;
-   int        *indexkeys = index->indexkeys;
+   int         indexcol = 0;
    Oid        *classes = index->classlist;
 
    do
    {
-       int         curIndxKey = indexkeys[0];
        Oid         curClass = classes[0];
        List       *clausegroup = NIL;
        List       *i;
@@ -619,9 +608,9 @@ group_clauses_by_indexkey_for_join(RelOptInfo *rel, IndexOptInfo *index,
                if (isouterjoin && rinfo->ispusheddown)
                    continue;
 
-               if (match_join_clause_to_indexkey(rel,
+               if (match_join_clause_to_indexcol(rel,
                                                  index,
-                                                 curIndxKey,
+                                                 indexcol,
                                                  curClass,
                                                  rinfo->clause))
                {
@@ -640,9 +629,9 @@ group_clauses_by_indexkey_for_join(RelOptInfo *rel, IndexOptInfo *index,
            if (isouterjoin && rinfo->ispusheddown)
                continue;
 
-           if (match_clause_to_indexkey(rel,
+           if (match_clause_to_indexcol(rel,
                                         index,
-                                        curIndxKey,
+                                        indexcol,
                                         curClass,
                                         rinfo->clause))
                clausegroup = lappend(clausegroup, rinfo);
@@ -657,10 +646,10 @@ group_clauses_by_indexkey_for_join(RelOptInfo *rel, IndexOptInfo *index,
 
        clausegroup_list = lappend(clausegroup_list, clausegroup);
 
-       indexkeys++;
+       indexcol++;
        classes++;
 
-   } while (!DoneMatchingIndexKeys(indexkeys, classes));
+   } while (!DoneMatchingIndexKeys(classes));
 
    /* if no join clause was matched then forget it, per comments above */
    if (!jfound)
@@ -674,15 +663,15 @@ group_clauses_by_indexkey_for_join(RelOptInfo *rel, IndexOptInfo *index,
 
 
 /*
- * match_clause_to_indexkey()
- *   Determines whether a restriction clause matches a key of an index.
+ * match_clause_to_indexcol()
+ *   Determines whether a restriction clause matches a column of an index.
  *
  *   To match, the clause:
  *
  *   (1)  must be in the form (indexkey op const) or (const op indexkey);
  *        and
  *   (2)  must contain an operator which is in the same class as the index
- *        operator for this key, or is a "special" operator as recognized
+ *        operator for this column, or is a "special" operator as recognized
  *        by match_special_index_operator().
  *
  *   Presently, the executor can only deal with indexquals that have the
@@ -693,7 +682,7 @@ group_clauses_by_indexkey_for_join(RelOptInfo *rel, IndexOptInfo *index,
  *
  * 'rel' is the relation of interest.
  * 'index' is an index on 'rel'.
- * 'indexkey' is a key of 'index'.
+ * 'indexcol' is a column number of 'index' (counting from 0).
  * 'opclass' is the corresponding operator class.
  * 'clause' is the clause to be tested.
  *
@@ -703,9 +692,9 @@ group_clauses_by_indexkey_for_join(RelOptInfo *rel, IndexOptInfo *index,
  * responsibility of higher-level routines to cope with those.
  */
 static bool
-match_clause_to_indexkey(RelOptInfo *rel,
+match_clause_to_indexcol(RelOptInfo *rel,
                         IndexOptInfo *index,
-                        int indexkey,
+                        int indexcol,
                         Oid opclass,
                         Expr *clause)
 {
@@ -725,7 +714,7 @@ match_clause_to_indexkey(RelOptInfo *rel,
     *      (indexkey operator constant) or (constant operator indexkey).
     * Anything that is a "pseudo constant" expression will do.
     */
-   if (match_index_to_operand(indexkey, leftop, rel, index) &&
+   if (match_index_to_operand(leftop, indexcol, rel, index) &&
        is_pseudo_constant_clause(rightop))
    {
        if (is_indexable_operator(clause, opclass, true))
@@ -740,7 +729,7 @@ match_clause_to_indexkey(RelOptInfo *rel,
        return false;
    }
 
-   if (match_index_to_operand(indexkey, rightop, rel, index) &&
+   if (match_index_to_operand(rightop, indexcol, rel, index) &&
        is_pseudo_constant_clause(leftop))
    {
        if (is_indexable_operator(clause, opclass, false))
@@ -759,8 +748,8 @@ match_clause_to_indexkey(RelOptInfo *rel,
 }
 
 /*
- * match_join_clause_to_indexkey()
- *   Determines whether a join clause matches a key of an index.
+ * match_join_clause_to_indexcol()
+ *   Determines whether a join clause matches a column of an index.
  *
  *   To match, the clause:
  *
@@ -768,7 +757,7 @@ match_clause_to_indexkey(RelOptInfo *rel,
  *        where others is an expression involving only vars of the other
  *        relation(s); and
  *   (2)  must contain an operator which is in the same class as the index
- *        operator for this key, or is a "special" operator as recognized
+ *        operator for this column, or is a "special" operator as recognized
  *        by match_special_index_operator().
  *
  *   As above, we must be able to commute the clause to put the indexkey
@@ -781,7 +770,7 @@ match_clause_to_indexkey(RelOptInfo *rel,
  *
  * 'rel' is the relation of interest.
  * 'index' is an index on 'rel'.
- * 'indexkey' is a key of 'index'.
+ * 'indexcol' is a column number of 'index' (counting from 0).
  * 'opclass' is the corresponding operator class.
  * 'clause' is the clause to be tested.
  *
@@ -791,9 +780,9 @@ match_clause_to_indexkey(RelOptInfo *rel,
  * responsibility of higher-level routines to cope with those.
  */
 static bool
-match_join_clause_to_indexkey(RelOptInfo *rel,
+match_join_clause_to_indexcol(RelOptInfo *rel,
                              IndexOptInfo *index,
-                             int indexkey,
+                             int indexcol,
                              Oid opclass,
                              Expr *clause)
 {
@@ -814,7 +803,7 @@ match_join_clause_to_indexkey(RelOptInfo *rel,
     * expression that uses none of the indexed relation's vars and
     * contains no volatile functions.
     */
-   if (match_index_to_operand(indexkey, leftop, rel, index))
+   if (match_index_to_operand(leftop, indexcol, rel, index))
    {
        Relids      othervarnos = pull_varnos(rightop);
        bool        isIndexable;
@@ -827,7 +816,7 @@ match_join_clause_to_indexkey(RelOptInfo *rel,
        return isIndexable;
    }
 
-   if (match_index_to_operand(indexkey, rightop, rel, index))
+   if (match_index_to_operand(rightop, indexcol, rel, index))
    {
        Relids      othervarnos = pull_varnos(leftop);
        bool        isIndexable;
@@ -895,8 +884,7 @@ indexable_operator(Expr *clause, Oid opclass, bool indexkey_on_left)
  *   to CNF format). --Nels, Jan '93
  */
 static bool
-pred_test(List *predicate_list, List *restrictinfo_list, List *joininfo_list,
-         int relvarno)
+pred_test(List *predicate_list, List *restrictinfo_list, List *joininfo_list)
 {
    List       *pred;
 
@@ -919,18 +907,6 @@ pred_test(List *predicate_list, List *restrictinfo_list, List *joininfo_list,
        return false;           /* no restriction clauses: the test must
                                 * fail */
 
-   /*
-    * The predicate as stored in the index definition will use varno 1
-    * for its Vars referencing the indexed relation.  If the indexed
-    * relation isn't varno 1 in the query, we must adjust the predicate
-    * to make the Vars match, else equal() won't work.
-    */
-   if (relvarno != 1)
-   {
-       predicate_list = copyObject(predicate_list);
-       ChangeVarNodes((Node *) predicate_list, 1, relvarno, 0);
-   }
-
    foreach(pred, predicate_list)
    {
        /*
@@ -1320,17 +1296,16 @@ indexable_outerrelids(RelOptInfo *rel, IndexOptInfo *index)
        {
            RestrictInfo *rinfo = (RestrictInfo *) lfirst(j);
            Expr   *clause = rinfo->clause;
-           int    *indexkeys = index->indexkeys;
+           int     indexcol = 0;
            Oid    *classes = index->classlist;
 
            do
            {
-               int         curIndxKey = indexkeys[0];
                Oid         curClass = classes[0];
 
-               if (match_join_clause_to_indexkey(rel,
+               if (match_join_clause_to_indexcol(rel,
                                                  index,
-                                                 curIndxKey,
+                                                 indexcol,
                                                  curClass,
                                                  clause))
                {
@@ -1338,10 +1313,10 @@ indexable_outerrelids(RelOptInfo *rel, IndexOptInfo *index)
                    break;
                }
 
-               indexkeys++;
+               indexcol++;
                classes++;
 
-           } while (!DoneMatchingIndexKeys(indexkeys, classes));
+           } while (!DoneMatchingIndexKeys(classes));
 
            if (match_found)
                break;
@@ -1611,16 +1586,22 @@ make_innerjoin_index_path(Query *root,
  * match_index_to_operand()
  *   Generalized test for a match between an index's key
  *   and the operand on one side of a restriction or join clause.
- *   Now check for functional indices as well.
+ *
+ * operand: the nodetree to be compared to the index
+ * indexcol: the column number of the index (counting from 0)
+ * rel: the parent relation
+ * index: the index of interest
  */
 static bool
-match_index_to_operand(int indexkey,
-                      Node *operand,
+match_index_to_operand(Node *operand,
+                      int indexcol,
                       RelOptInfo *rel,
                       IndexOptInfo *index)
 {
+   int         indkey;
+
    /*
-    * Ignore any RelabelType node above the indexkey.  This is needed to
+    * Ignore any RelabelType node above the operand.   This is needed to
     * be able to apply indexscanning in binary-compatible-operator cases.
     * Note: we can assume there is at most one RelabelType node;
     * eval_const_expressions() will have simplified if more than one.
@@ -1628,77 +1609,52 @@ match_index_to_operand(int indexkey,
    if (operand && IsA(operand, RelabelType))
        operand = (Node *) ((RelabelType *) operand)->arg;
 
-   if (index->indproc == InvalidOid)
+   indkey = index->indexkeys[indexcol];
+   if (indkey != 0)
    {
        /*
-        * Simple index.
+        * Simple index column; operand must be a matching Var.
         */
        if (operand && IsA(operand, Var) &&
            rel->relid == ((Var *) operand)->varno &&
-           indexkey == ((Var *) operand)->varattno)
+           indkey == ((Var *) operand)->varattno)
            return true;
-       else
-           return false;
    }
-
-   /*
-    * Functional index.
-    */
-   return function_index_operand((Expr *) operand, rel, index);
-}
-
-static bool
-function_index_operand(Expr *funcOpnd, RelOptInfo *rel, IndexOptInfo *index)
-{
-   FuncExpr   *function;
-   List       *funcargs;
-   int        *indexKeys = index->indexkeys;
-   List       *arg;
-   int         i;
-
-   /*
-    * sanity check, make sure we know what we're dealing with here.
-    */
-   if (funcOpnd == NULL || !IsA(funcOpnd, FuncExpr) ||
-       indexKeys == NULL)
-       return false;
-
-   function = (FuncExpr *) funcOpnd;
-   funcargs = function->args;
-
-   if (function->funcid != index->indproc)
-       return false;
-
-   /*----------
-    * Check that the arguments correspond to the same arguments used to
-    * create the functional index.  To do this we must check that
-    *  1. they refer to the right relation.
-    *  2. the args have the right attr. numbers in the right order.
-    * We must ignore RelabelType nodes above the argument Vars in order
-    * to recognize binary-compatible-function cases correctly.
-    *----------
-    */
-   i = 0;
-   foreach(arg, funcargs)
+   else
    {
-       Var        *var = (Var *) lfirst(arg);
+       /*
+        * Index expression; find the correct expression.  (This search could
+        * be avoided, at the cost of complicating all the callers of this
+        * routine; doesn't seem worth it.)
+        */
+       List       *indexprs;
+       int         i;
+       Node       *indexkey;
 
-       if (var && IsA(var, RelabelType))
-           var = (Var *) ((RelabelType *) var)->arg;
-       if (var == NULL || !IsA(var, Var))
-           return false;
-       if (indexKeys[i] == 0)
-           return false;
-       if (var->varno != rel->relid || var->varattno != indexKeys[i])
-           return false;
+       indexprs = index->indexprs;
+       for (i = 0; i < indexcol; i++)
+       {
+           if (index->indexkeys[i] == 0)
+           {
+               if (indexprs == NIL)
+                   elog(ERROR, "wrong number of index expressions");
+               indexprs = lnext(indexprs);
+           }
+       }
+       if (indexprs == NIL)
+           elog(ERROR, "wrong number of index expressions");
+       indexkey = (Node *) lfirst(indexprs);
+       /*
+        * Does it match the operand?  Again, strip any relabeling.
+        */
+       if (indexkey && IsA(indexkey, RelabelType))
+           indexkey = (Node *) ((RelabelType *) indexkey)->arg;
 
-       i++;
+       if (equal(indexkey, operand))
+           return true;
    }
 
-   if (indexKeys[i] != 0)
-       return false;           /* not enough arguments */
-
-   return true;
+   return false;
 }
 
 /****************************************************************************
@@ -1728,7 +1684,7 @@ function_index_operand(Expr *funcOpnd, RelOptInfo *rel, IndexOptInfo *index)
  *
  * Two routines are provided here, match_special_index_operator() and
  * expand_indexqual_conditions().  match_special_index_operator() is
- * just an auxiliary function for match_clause_to_indexkey(); after
+ * just an auxiliary function for match_clause_to_indexcol(); after
  * the latter fails to recognize a restriction opclause's operator
  * as a member of an index's opclass, it asks match_special_index_operator()
  * whether the clause should be considered an indexqual anyway.
@@ -1915,7 +1871,6 @@ List *
 expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
 {
    List       *resultquals = NIL;
-   int        *indexkeys = index->indexkeys;
    Oid        *classes = index->classlist;
 
    if (clausegroups == NIL)
@@ -1937,11 +1892,9 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
 
        clausegroups = lnext(clausegroups);
 
-       indexkeys++;
        classes++;
 
-   } while (clausegroups != NIL &&
-            !DoneMatchingIndexKeys(indexkeys, classes));
+   } while (clausegroups != NIL && !DoneMatchingIndexKeys(classes));
 
    Assert(clausegroups == NIL); /* else more groups than indexkeys... */
 
index cf48b7c47bd39d8a5213a8744271dc5f333dc42d..f8e4906c13bda67fb883585827b910f8b45874fc 100644 (file)
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * 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,73 +636,50 @@ build_index_pathkeys(Query *root,
    List       *retval = NIL;
    int        *indexkeys = index->indexkeys;
    Oid        *ordering = index->ordering;
-   PathKeyItem *item;
-   Oid         sortop;
-
-   if (!indexkeys || indexkeys[0] == 0 ||
-       !ordering || ordering[0] == InvalidOid)
-       return NIL;             /* unordered index? */
+   List       *indexprs = index->indexprs;
 
-   if (index->indproc)
+   while (*ordering != InvalidOid)
    {
-       /* Functional index: build a representation of the function call */
-       Expr       *funcnode;
-       List       *funcargs = NIL;
+       PathKeyItem *item;
+       Oid         sortop;
+       Node       *indexkey;
+       List       *cpathkey;
 
        sortop = *ordering;
        if (ScanDirectionIsBackward(scandir))
        {
            sortop = get_commutator(sortop);
            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,
-                              find_indexkey_var(root, rel, *indexkeys));
-           indexkeys++;
+           /* simple index column */
+           indexkey = (Node *) find_indexkey_var(root, rel, *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
-   {
-       /* Normal non-functional index */
-       while (*indexkeys != 0 && *ordering != InvalidOid)
+       else
        {
-           Var        *relvar = find_indexkey_var(root, rel, *indexkeys);
-           List       *cpathkey;
+           /* expression --- assume we need not copy it */
+           if (indexprs == NIL)
+               elog(ERROR, "wrong number of index expressions");
+           indexkey = (Node *) lfirst(indexprs);
+           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 */
+       item = makePathKeyItem(indexkey, sortop);
+       cpathkey = make_canonical_pathkey(root, item);
 
-           /* OK, make a sublist for this sort key */
-           item = makePathKeyItem((Node *) relvar, sortop);
-           cpathkey = make_canonical_pathkey(root, item);
+       /*
+        * Eliminate redundant ordering info; could happen if query is
+        * such that index keys are equijoined...
+        */
+       if (!ptrMember(cpathkey, retval))
+           retval = lappend(retval, cpathkey);
 
-           /*
-            * Eliminate redundant ordering info; could happen if query is
-            * such that index keys are equijoined...
-            */
-           if (!ptrMember(cpathkey, retval))
-               retval = lappend(retval, cpathkey);
-           indexkeys++;
-           ordering++;
-       }
+       indexkeys++;
+       ordering++;
    }
 
    return retval;
index f50eb792811f745661d8a9b111a000579731026d..0d55dd7f8c301604a103004e913dec9cb46c55b8 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * 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 *
 fix_indxqual_operand(Node *node, int baserelid, IndexOptInfo *index,
                     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
     * table but varattno equal to the index's attribute number (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
     * regular Var.  But it will do for now.
     */
-   if (IsA(node, Var))
+   Var        *result;
+   int         pos;
+   List       *indexprs;
+
+   /*
+    * 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)
    {
-       /* If it's a var, find which index key position it occupies */
-       Assert(index->indproc == InvalidOid);
+       /* Try to match against simple index columns */
+       int         varatt = ((Var *) node)->varattno;
 
-       if (((Var *) node)->varno == baserelid)
+       if (varatt != 0)
        {
-           int         varatt = ((Var *) node)->varattno;
-           int         pos;
-
-           for (pos = 0; pos < index->nkeys; pos++)
+           for (pos = 0; pos < index->ncolumns; pos++)
            {
                if (index->indexkeys[pos] == varatt)
                {
-                   Node       *newnode = copyObject(node);
-
-                   ((Var *) newnode)->varattno = pos + 1;
+                   result = (Var *) copyObject(node);
+                   result->varattno = pos + 1;
                    /* return the correct opclass, too */
                    *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");
    }
 
-   /*
-    * Else, it must be a func expression matching a functional index.
-    * Since we currently only support single-column functional indexes,
-    * the returned varattno must be 1.
-    */
-   Assert(index->indproc != InvalidOid);
-   Assert(is_funcclause(node));    /* not a very thorough check, but easy */
-
-   /* classlist[0] is the only class of a functional index */
-   *opclass = index->classlist[0];
+   /* Try to match against index expressions */
+   indexprs = index->indexprs;
+   for (pos = 0; pos < index->ncolumns; pos++)
+   {
+       if (index->indexkeys[pos] == 0)
+       {
+           Node       *indexkey;
+
+           if (indexprs == NIL)
+               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 */
 }
 
 /*
index c0ffd939cbe520f9a50e0f9b7c93c5b857df3a7b..4d51e7ffbc05b77603aa4036b864b9ab1f54a1c4 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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
  *   AUTHOR            DATE            MAJOR EVENT
@@ -132,28 +132,6 @@ get_rightop(Expr *clause)
        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
  *****************************************************************************/
index 9ec638a5a436cbfffffd1b58652e437b3a948e89..36223be93fdc823cc088d742b6588eb7f2a5b567 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * 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 @@
 #include "optimizer/clauses.h"
 #include "optimizer/plancat.h"
 #include "parser/parsetree.h"
+#include "rewrite/rewriteManip.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
 #include "utils/lsyscache.h"
@@ -116,78 +117,67 @@ get_relation_info(Oid relationObjectId, RelOptInfo *rel)
            Relation    indexRelation;
            Form_pg_index index;
            IndexOptInfo *info;
+           int         ncolumns;
            int         i;
            int16       amorderstrategy;
 
            /* Extract info from the relation descriptor for the index */
            indexRelation = index_open(indexoid);
+           index = indexRelation->rd_index;
 
            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->indproc = index->indproc; /* functional index ?? */
-           if (VARSIZE(&index->indpred) > VARHDRSZ) /* partial index ?? */
-           {
-               char       *predString;
+           info->ncolumns = ncolumns = index->indnatts;
 
-               predString = DatumGetCString(DirectFunctionCall1(textout,
-                                                                PointerGetDatum(&index->indpred)));
-               info->indpred = (List *) stringToNode(predString);
-               pfree(predString);
-           }
-           else
-               info->indpred = NIL;
-           info->unique = index->indisunique;
+           /*
+            * Need to make classlist and ordering arrays large enough to put
+            * a terminating 0 at the end of each one.
+            */
+           info->indexkeys = (int *) palloc(sizeof(int) * ncolumns);
+           info->classlist = (Oid *) palloc0(sizeof(Oid) * (ncolumns + 1));
+           info->ordering = (Oid *) palloc0(sizeof(Oid) * (ncolumns + 1));
 
-           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] = (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] = 0;
-           info->nkeys = i;
 
            info->relam = indexRelation->rd_rel->relam;
            info->pages = indexRelation->rd_rel->relpages;
            info->tuples = indexRelation->rd_rel->reltuples;
            info->amcostestimate = index_cost_estimator(indexRelation);
-           amorderstrategy = indexRelation->rd_am->amorderstrategy;
 
            /*
             * 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)
            {
                int         oprindex = amorderstrategy - 1;
 
-               for (i = 0; i < info->ncolumns; i++)
+               for (i = 0; i < ncolumns; i++)
                {
                    info->ordering[i] = indexRelation->rd_operator[oprindex];
                    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 */
            info->outer_relids = NULL;
            info->inner_paths = NIL;
@@ -372,15 +362,15 @@ has_unique_index(RelOptInfo *rel, AttrNumber attno)
        IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);
 
        /*
-        * Note: ignore functional and partial indexes, since they don't
-        * allow us to conclude that all attr values are distinct. Also, a
-        * multicolumn unique index doesn't allow us to conclude that just
-        * the specified attr is unique.
+        * Note: ignore partial indexes, since they don't allow us to conclude
+        * that all attr values are distinct.  We don't take any interest in
+        * expressional indexes either. Also, a multicolumn unique index
+        * doesn't allow us to conclude that just the specified attr is
+        * unique.
         */
        if (index->unique &&
-           index->nkeys == 1 &&
+           index->ncolumns == 1 &&
            index->indexkeys[0] == attno &&
-           index->indproc == InvalidOid &&
            index->indpred == NIL)
            return true;
    }
index b7fc22c46bea5d45c9e5f182fd116e70a249ac92..79b36caad75565e10646fea5cb00276a11f1b260 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * 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)
            /* OK, add it to the index definition */
            iparam = makeNode(IndexElem);
            iparam->name = pstrdup(key);
-           iparam->funcname = NIL;
-           iparam->args = NIL;
+           iparam->expr = NULL;
            iparam->opclass = NIL;
            index->indexParams = lappend(index->indexParams, iparam);
        }
@@ -1386,11 +1385,13 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
 
        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,
-                                            iparam->name ? iparam->name :
-                                        strVal(llast(iparam->funcname)),
-                                            "key", cxt->alist);
+                                            iparam->name,
+                                            "key",
+                                            cxt->alist);
        }
        if (index->idxname == NULL)     /* should not happen */
            elog(ERROR, "%s: failed to make implicit index name",
@@ -1454,7 +1455,8 @@ static Query *
 transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
 {
    Query      *qry;
-   RangeTblEntry *rte;
+   RangeTblEntry *rte = NULL;
+   List       *l;
 
    qry = makeNode(Query);
    qry->commandType = CMD_UTILITY;
@@ -1477,6 +1479,32 @@ transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
        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;
    stmt->rangetable = pstate->p_rtable;
 
index 0a7ae361f4924c1cbfc5e48fce97e4cd09b61458..681738253bb0729a17a17a8ead0f8ddae410354a 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * 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
  *   AUTHOR            DATE            MAJOR EVENT
@@ -210,7 +210,7 @@ static void doNegateFloat(Value *v);
                oper_argtypes RuleActionList RuleActionMulti
                opt_column_list columnList opt_name_list
                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
                any_operator expr_list dotted_name attrs
                target_list update_target_list insert_column_list
@@ -276,7 +276,7 @@ static void doNegateFloat(Value *v);
 %type <columnref>  columnref
 %type <alias>  alias_clause
 %type <sortgroupby>        sortby
-%type <ielem>  index_elem func_index
+%type <ielem>  index_elem
 %type <node>   table_ref
 %type <jexpr>  joined_table
 %type <range>  relation_expr
@@ -408,7 +408,7 @@ static void doNegateFloat(Value *v);
 %token         UNIONJOIN
 
 /* 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
 
 /* precedence: lowest to highest */
@@ -2932,7 +2932,7 @@ function_with_argtypes:
  *
  *     QUERY:
  *             create index <indexname> on <relname>
- *               [ using <access> ] "(" (<col> with <op>)+ ")"
+ *               [ using <access> ] "(" ( <col> [ using <opclass> ] )+ ")"
  *               [ where <predicate> ]
  *
  *****************************************************************************/
@@ -2958,70 +2958,48 @@ index_opt_unique:
 
 access_method_clause:
            USING access_method                     { $$ = $2; }
-           /* If btree changes as our default, update pg_get_indexdef() */
            | /*EMPTY*/                             { $$ = DEFAULT_INDEX_TYPE; }
        ;
 
-index_params:
-           index_list                              { $$ = $1; }
-           | func_index                            { $$ = makeList1($1); }
+index_params:  index_elem                          { $$ = makeList1($1); }
+           | index_params ',' index_elem           { $$ = lappend($1, $3); }
        ;
 
-index_list:    index_elem                              { $$ = makeList1($1); }
-           | index_list ',' index_elem             { $$ = lappend($1, $3); }
-       ;
-
-func_index: func_name '(' name_list ')' opt_class
+/*
+ * 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
+               {
+                   $$ = makeNode(IndexElem);
+                   $$->name = $1;
+                   $$->expr = NULL;
+                   $$->opclass = $2;
+               }
+           | func_name '(' expr_list ')' opt_class
                {
+                   FuncCall *n = makeNode(FuncCall);
+                   n->funcname = $1;
+                   n->args = $3;
+                   n->agg_star = FALSE;
+                   n->agg_distinct = FALSE;
+
                    $$ = makeNode(IndexElem);
                    $$->name = NULL;
-                   $$->funcname = $1;
-                   $$->args = $3;
+                   $$->expr = (Node *)n;
                    $$->opclass = $5;
                }
-         ;
-
-index_elem: attr_name opt_class
+           | '(' a_expr ')' opt_class
                {
                    $$ = makeNode(IndexElem);
-                   $$->name = $1;
-                   $$->funcname = NIL;
-                   $$->args = NIL;
-                   $$->opclass = $2;
+                   $$->name = NULL;
+                   $$->expr = $2;
+                   $$->opclass = $4;
                }
        ;
 
-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));
-
-                       if (strcmp(claname, "network_ops") != 0 &&
-                           strcmp(claname, "timespan_ops") != 0 &&
-                           strcmp(claname, "datetime_ops") != 0 &&
-                           strcmp(claname, "lztext_ops") != 0 &&
-                           strcmp(claname, "timestamp_ops") != 0)
-                           $$ = $1;
-                       else
-                           $$ = NIL;
-                   }
-                   else
-                       $$ = $1;
-               }
+opt_class: any_name                                { $$ = $1; }
            | USING any_name                        { $$ = $2; }
            | /*EMPTY*/                             { $$ = NIL; }
        ;
index 7a66a9095e41bd8d343f8961dbecf69cec8841e7..1cad3ec31a39b7d5953f50efad1ec27e153dd3cd 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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,
     * without freezing a snapshot.  By extension we allow SHOW
     * not to set a snapshot.  The other stmts listed are just
     * efficiency hacks.  Beware of listing anything that can
-    * modify the database --- if, say, it has to update a
-    * functional index, then it had better have a snapshot.
+    * modify the database --- if, say, it has to update an
+    * index with expressions that invoke user-defined functions,
+    * then it had better have a snapshot.
     */
    if (! (IsA(utilityStmt, TransactionStmt) ||
           IsA(utilityStmt, LockStmt) ||
index e41d3245818cbbd7873360bcd673fb24e291ee00..1040ff3e2388aea431a2a5ca5a0e6f7fdb1dbbe1 100644 (file)
@@ -3,7 +3,7 @@
  *             back to source text
  *
  * 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.
  *
@@ -535,12 +535,14 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
    Form_pg_index idxrec;
    Form_pg_class idxrelrec;
    Form_pg_am  amrec;
+   List       *indexprs;
+   List       *context;
    Oid         indrelid;
    int         len;
    int         keyno;
-   Oid         keycoltypes[INDEX_MAX_KEYS];
+   Oid         keycoltype;
    StringInfoData buf;
-   StringInfoData keybuf;
+   char       *str;
    char       *sep;
 
    /*
@@ -576,6 +578,30 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
        elog(ERROR, "syscache lookup for AM %u failed", idxrelrec->relam);
    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
     * never be schema-qualified, but the indexed rel's name may be.
@@ -588,76 +614,72 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
                     quote_identifier(NameStr(amrec->amname)));
 
    /*
-    * Collect the indexed attributes in keybuf
+    * Report the indexed attributes
     */
-   initStringInfo(&keybuf);
    sep = "";
-   for (keyno = 0; keyno < INDEX_MAX_KEYS; keyno++)
+   for (keyno = 0; keyno < idxrec->indnatts; 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 = ", ";
 
-       /*
-        * Add the indexed field name
-        */
-       appendStringInfo(&keybuf, "%s", quote_identifier(attname));
+       if (attnum != 0)
+       {
+           /* Simple index column */
+           char       *attname;
 
-       /*
-        * If not a functional index, add the operator class name
-        */
-       if (idxrec->indproc == InvalidOid)
-           get_opclass_name(idxrec->indclass[keyno],
-                            keycoltypes[keyno],
-                            &keybuf);
-   }
+           attname = get_relid_attribute_name(indrelid, attnum);
+           appendStringInfo(&buf, "%s", quote_identifier(attname));
+           keycoltype = get_atttype(indrelid, attnum);
+       }
+       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);
+       }
 
-   if (idxrec->indproc != InvalidOid)
-   {
        /*
-        * For functional index say 'func (attrs) opclass'
+        * Add the operator class name
         */
-       appendStringInfo(&buf, "%s(%s)",
-                        generate_function_name(idxrec->indproc,
-                                               keyno, keycoltypes),
-                        keybuf.data);
-       get_opclass_name(idxrec->indclass[0],
-                        get_func_rettype(idxrec->indproc),
+       get_opclass_name(idxrec->indclass[keyno], keycoltype,
                         &buf);
    }
-   else
-   {
-       /*
-        * Otherwise say 'attr opclass [, ...]'
-        */
-       appendStringInfo(&buf, "%s", keybuf.data);
-   }
 
    appendStringInfoChar(&buf, ')');
 
    /*
     * 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;
-       List       *context;
-       char       *exprstr;
-       char       *str;
+       Datum       predDatum;
+       bool        isnull;
+       char       *predString;
 
-       /* Convert TEXT object to C string */
-       exprstr = DatumGetCString(DirectFunctionCall1(textout,
-                                    PointerGetDatum(&idxrec->indpred)));
-       /* Convert expression to node tree */
-       node = (Node *) stringToNode(exprstr);
+       /* Convert text string to node tree */
+       predDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
+                                   Anum_pg_index_indpred, &isnull);
+       Assert(!isnull);
+       predString = DatumGetCString(DirectFunctionCall1(textout,
+                                                        predDatum));
+       node = (Node *) stringToNode(predString);
+       pfree(predString);
 
        /*
         * If top level is a List, assume it is an implicit-AND structure,
@@ -667,7 +689,6 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
        if (node && IsA(node, List))
            node = (Node *) make_ands_explicit((List *) node);
        /* Deparse */
-       context = deparse_context_for(get_rel_name(indrelid), indrelid);
        str = deparse_expression(node, context, false, false);
        appendStringInfo(&buf, " WHERE %s", str);
    }
@@ -681,7 +702,6 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
    memcpy(VARDATA(indexdef), buf.data, buf.len);
 
    pfree(buf.data);
-   pfree(keybuf.data);
 
    ReleaseSysCache(ht_idx);
    ReleaseSysCache(ht_idxrel);
index 77ef33b878358670ca47ae553158265a32358e5b..724503e9c4c188b5b051d3b6a600e7f7d4d1e471 100644 (file)
@@ -15,7 +15,7 @@
  *
  *
  * 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)
                        indexSelectivity, indexCorrelation);
 
    /*
-    * If it's a functional index, leave the default zero-correlation
-    * estimate in place.  If not, and if we can get an estimate for the
-    * first variable's ordering correlation C from pg_statistic, estimate
+    * If the first column is a simple variable, and we can get an estimate
+    * for its ordering correlation C from pg_statistic, estimate
     * the index correlation as C / number-of-columns. (The idea here is
     * that multiple columns dilute the importance of the first column's
     * ordering, but don't negate it entirely.)
     */
-   if (index->indproc == InvalidOid)
+   if (index->indexkeys[0] != 0)
    {
        Oid         relid;
        HeapTuple   tuple;
@@ -3942,8 +3941,7 @@ btcostestimate(PG_FUNCTION_ARGS)
 
                Assert(nnumbers == 1);
                varCorrelation = numbers[0];
-               for (nKeys = 1; index->indexkeys[nKeys] != 0; nKeys++)
-                    /* skip */ ;
+               nKeys = index->ncolumns;
 
                *indexCorrelation = varCorrelation / nKeys;
 
index 6ab9427dfdb3805cc87ba3040b63d02d987a4a05..14899e2968b06ac8d64f65dcfb2369b69fcc4338 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.184 2003/02/09 06:56:28 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.185 2003/05/28 16:03:59 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -52,6 +52,8 @@
 #include "catalog/pg_type.h"
 #include "commands/trigger.h"
 #include "miscadmin.h"
+#include "optimizer/clauses.h"
+#include "optimizer/planmain.h"
 #include "storage/smgr.h"
 #include "utils/builtins.h"
 #include "utils/catcache.h"
@@ -939,10 +941,9 @@ void
 RelationInitIndexAccessInfo(Relation relation)
 {
    HeapTuple   tuple;
-   Size        iformsize;
-   Form_pg_index iform;
    Form_pg_am  aform;
    MemoryContext indexcxt;
+   MemoryContext oldcontext;
    IndexStrategy strategy;
    Oid        *operator;
    RegProcedure *support;
@@ -952,8 +953,9 @@ RelationInitIndexAccessInfo(Relation relation)
    uint16      amsupport;
 
    /*
-    * Make a copy of the pg_index entry for the index.  Note that this is
-    * a variable-length tuple.
+    * Make a copy of the pg_index entry for the index.  Since pg_index
+    * contains variable-length and possibly-null fields, we have to do
+    * this honestly rather than just treating it as a Form_pg_index struct.
     */
    tuple = SearchSysCache(INDEXRELID,
                           ObjectIdGetDatum(RelationGetRelid(relation)),
@@ -961,11 +963,11 @@ RelationInitIndexAccessInfo(Relation relation)
    if (!HeapTupleIsValid(tuple))
        elog(ERROR, "RelationInitIndexAccessInfo: no pg_index entry for index %u",
             RelationGetRelid(relation));
-   iformsize = tuple->t_len - tuple->t_data->t_hoff;
-   iform = (Form_pg_index) MemoryContextAlloc(CacheMemoryContext, iformsize);
-   memcpy(iform, GETSTRUCT(tuple), iformsize);
+   oldcontext = MemoryContextSwitchTo(CacheMemoryContext);
+   relation->rd_indextuple = heap_copytuple(tuple);
+   relation->rd_index = (Form_pg_index) GETSTRUCT(relation->rd_indextuple);
+   MemoryContextSwitchTo(oldcontext);
    ReleaseSysCache(tuple);
-   relation->rd_index = iform;
 
    /*
     * Make a copy of the pg_am entry for the index's access method
@@ -982,6 +984,9 @@ RelationInitIndexAccessInfo(Relation relation)
    relation->rd_am = aform;
 
    natts = relation->rd_rel->relnatts;
+   if (natts != relation->rd_index->indnatts)
+       elog(ERROR, "RelationInitIndexAccessInfo: relnatts disagrees with indnatts for index %u",
+            RelationGetRelid(relation));
    amstrategies = aform->amstrategies;
    amsupport = aform->amsupport;
 
@@ -1047,9 +1052,15 @@ RelationInitIndexAccessInfo(Relation relation)
     * Fill the strategy map and the support RegProcedure arrays.
     * (supportinfo is left as zeroes, and is filled on-the-fly when used)
     */
-   IndexSupportInitialize(iform,
+   IndexSupportInitialize(relation->rd_index,
                           strategy, operator, support,
                           amstrategies, amsupport, natts);
+
+   /*
+    * expressions and predicate cache will be filled later
+    */
+   relation->rd_indexprs = NIL;
+   relation->rd_indpred = NIL;
 }
 
 /*
@@ -1087,8 +1098,7 @@ IndexSupportInitialize(Form_pg_index iform,
    {
        OpClassCacheEnt *opcentry;
 
-       if (iform->indkey[attIndex] == InvalidAttrNumber ||
-           !OidIsValid(iform->indclass[attIndex]))
+       if (!OidIsValid(iform->indclass[attIndex]))
            elog(ERROR, "IndexSupportInitialize: bogus pg_index tuple");
 
        /* look up the info for this opclass, using a cache */
@@ -1729,8 +1739,8 @@ RelationClearRelation(Relation relation, bool rebuild)
     * with, we can only get rid of these fields:
     */
    FreeTriggerDesc(relation->trigdesc);
-   if (relation->rd_index)
-       pfree(relation->rd_index);
+   if (relation->rd_indextuple)
+       pfree(relation->rd_indextuple);
    if (relation->rd_am)
        pfree(relation->rd_am);
    if (relation->rd_rel)
@@ -2659,6 +2669,136 @@ insert_ordered_oid(List *list, Oid datum)
    return list;
 }
 
+/*
+ * RelationGetIndexExpressions -- get the index expressions for an index
+ *
+ * We cache the result of transforming pg_index.indexprs into a node tree.
+ * If the rel is not an index or has no expressional columns, we return NIL.
+ * Otherwise, the returned tree is copied into the caller's memory context.
+ * (We don't want to return a pointer to the relcache copy, since it could
+ * disappear due to relcache invalidation.)
+ */
+List *
+RelationGetIndexExpressions(Relation relation)
+{
+   List       *result;
+   Datum       exprsDatum;
+   bool        isnull;
+   char       *exprsString;
+   MemoryContext oldcxt;
+
+   /* Quick exit if we already computed the result. */
+   if (relation->rd_indexprs)
+       return (List *) copyObject(relation->rd_indexprs);
+
+   /* Quick exit if there is nothing to do. */
+   if (relation->rd_indextuple == NULL ||
+       heap_attisnull(relation->rd_indextuple, Anum_pg_index_indexprs))
+       return NIL;
+
+   /*
+    * We build the tree we intend to return in the caller's context.
+    * After successfully completing the work, we copy it into the relcache
+    * entry.  This avoids problems if we get some sort of
+    * error partway through.
+    *
+    * We make use of the syscache's copy of pg_index's tupledesc
+    * to access the non-fixed fields of the tuple.  We assume that
+    * the syscache will be initialized before any access of a
+    * partial index could occur.  (This would probably fail if we
+    * were to allow partial indexes on system catalogs.)
+    */
+   exprsDatum = SysCacheGetAttr(INDEXRELID, relation->rd_indextuple,
+                                Anum_pg_index_indexprs, &isnull);
+   Assert(!isnull);
+   exprsString = DatumGetCString(DirectFunctionCall1(textout, exprsDatum));
+   result = (List *) stringToNode(exprsString);
+   pfree(exprsString);
+
+   /*
+    * Run the expressions through eval_const_expressions.  This is not just
+    * an optimization, but is necessary, because the planner will be
+    * comparing them to const-folded qual clauses, and may fail to detect
+    * valid matches without this.
+    */
+   result = (List *) eval_const_expressions((Node *) result);
+
+   /* May as well fix opfuncids too */
+   fix_opfuncids((Node *) result);
+
+   /* Now save a copy of the completed tree in the relcache entry. */
+   oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+   relation->rd_indexprs = (List *) copyObject(result);
+   MemoryContextSwitchTo(oldcxt);
+
+   return result;
+}
+
+/*
+ * RelationGetIndexPredicate -- get the index predicate for an index
+ *
+ * We cache the result of transforming pg_index.indpred into a node tree.
+ * If the rel is not an index or has no predicate, we return NIL.
+ * Otherwise, the returned tree is copied into the caller's memory context.
+ * (We don't want to return a pointer to the relcache copy, since it could
+ * disappear due to relcache invalidation.)
+ */
+List *
+RelationGetIndexPredicate(Relation relation)
+{
+   List       *result;
+   Datum       predDatum;
+   bool        isnull;
+   char       *predString;
+   MemoryContext oldcxt;
+
+   /* Quick exit if we already computed the result. */
+   if (relation->rd_indpred)
+       return (List *) copyObject(relation->rd_indpred);
+
+   /* Quick exit if there is nothing to do. */
+   if (relation->rd_indextuple == NULL ||
+       heap_attisnull(relation->rd_indextuple, Anum_pg_index_indpred))
+       return NIL;
+
+   /*
+    * We build the tree we intend to return in the caller's context.
+    * After successfully completing the work, we copy it into the relcache
+    * entry.  This avoids problems if we get some sort of
+    * error partway through.
+    *
+    * We make use of the syscache's copy of pg_index's tupledesc
+    * to access the non-fixed fields of the tuple.  We assume that
+    * the syscache will be initialized before any access of a
+    * partial index could occur.  (This would probably fail if we
+    * were to allow partial indexes on system catalogs.)
+    */
+   predDatum = SysCacheGetAttr(INDEXRELID, relation->rd_indextuple,
+                               Anum_pg_index_indpred, &isnull);
+   Assert(!isnull);
+   predString = DatumGetCString(DirectFunctionCall1(textout, predDatum));
+   result = (List *) stringToNode(predString);
+   pfree(predString);
+
+   /*
+    * Run the expression through eval_const_expressions.  This is not just
+    * an optimization, but is necessary, because the planner will be
+    * comparing it to const-folded qual clauses, and may fail to detect
+    * valid matches without this.
+    */
+   result = (List *) eval_const_expressions((Node *) result);
+
+   /* May as well fix opfuncids too */
+   fix_opfuncids((Node *) result);
+
+   /* Now save a copy of the completed tree in the relcache entry. */
+   oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
+   relation->rd_indpred = (List *) copyObject(result);
+   MemoryContextSwitchTo(oldcxt);
+
+   return result;
+}
+
 
 /*
  * load_relcache_init_file, write_relcache_init_file
@@ -2829,14 +2969,19 @@ load_relcache_init_file(void)
            if (rel->rd_isnailed)
                nailed_indexes++;
 
-           /* next, read the pg_index tuple form */
+           /* next, read the pg_index tuple */
            if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len))
                goto read_failed;
 
-           rel->rd_index = (Form_pg_index) palloc(len);
-           if ((nread = fread(rel->rd_index, 1, len, fp)) != len)
+           rel->rd_indextuple = (HeapTuple) palloc(len);
+           if ((nread = fread(rel->rd_indextuple, 1, len, fp)) != len)
                goto read_failed;
 
+           /* Fix up internal pointers in the tuple -- see heap_copytuple */
+           rel->rd_indextuple->t_datamcxt = CurrentMemoryContext;
+           rel->rd_indextuple->t_data = (HeapTupleHeader) ((char *) rel->rd_indextuple + HEAPTUPLESIZE);
+           rel->rd_index = (Form_pg_index) GETSTRUCT(rel->rd_indextuple);
+
            /* next, read the access method tuple form */
            if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len))
                goto read_failed;
@@ -2904,6 +3049,7 @@ load_relcache_init_file(void)
                nailed_rels++;
 
            Assert(rel->rd_index == NULL);
+           Assert(rel->rd_indextuple == NULL);
            Assert(rel->rd_am == NULL);
            Assert(rel->rd_indexcxt == NULL);
            Assert(rel->rd_istrat == NULL);
@@ -2917,11 +3063,13 @@ load_relcache_init_file(void)
         * format is complex and subject to change).  They must be rebuilt
         * if needed by RelationCacheInitializePhase2.  This is not
         * expected to be a big performance hit since few system catalogs
-        * have such.
+        * have such.  Ditto for index expressions and predicates.
         */
        rel->rd_rules = NULL;
        rel->rd_rulescxt = NULL;
        rel->trigdesc = NULL;
+       rel->rd_indexprs = NIL;
+       rel->rd_indpred = NIL;
 
        /*
         * Reset transient-state fields in the relcache entry
@@ -3076,26 +3224,15 @@ write_relcache_init_file(void)
        if (rel->rd_rel->relkind == RELKIND_INDEX)
        {
            Form_pg_am  am = rel->rd_am;
-           HeapTuple   tuple;
 
-           /*
-            * We need to write the index tuple form, but this is a bit
-            * tricky since it's a variable-length struct.  Rather than
-            * hoping to intuit the length, fetch the pg_index tuple
-            * afresh using the syscache, and write that.
-            */
-           tuple = SearchSysCache(INDEXRELID,
-                                ObjectIdGetDatum(RelationGetRelid(rel)),
-                                  0, 0, 0);
-           if (!HeapTupleIsValid(tuple))
-               elog(ERROR, "write_relcache_init_file: no pg_index entry for index %u",
-                    RelationGetRelid(rel));
-           len = tuple->t_len - tuple->t_data->t_hoff;
+           /* write the pg_index tuple */
+           /* we assume this was created by heap_copytuple! */
+           len = HEAPTUPLESIZE + rel->rd_indextuple->t_len;
            if (fwrite(&len, 1, sizeof(len), fp) != sizeof(len))
-               elog(FATAL, "cannot write init file -- index tuple form length");
-           if (fwrite(GETSTRUCT(tuple), 1, len, fp) != len)
-               elog(FATAL, "cannot write init file -- index tuple form");
-           ReleaseSysCache(tuple);
+               elog(FATAL, "cannot write init file -- index tuple length");
+
+           if (fwrite(rel->rd_indextuple, 1, len, fp) != len)
+               elog(FATAL, "cannot write init file -- index tuple");
 
            /* next, write the access method tuple form */
            len = sizeof(FormData_pg_am);
index 4aeac0c44e6c52f198bb5df3aa7ce3e8a367ddd7..a20cc1a4207c4f4a98d3a66c0a36f907c7dd8428 100644 (file)
@@ -3,7 +3,7 @@
  *
  * 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 "describe.h"
@@ -711,10 +711,7 @@ describeOneTableDetails(const char *schemaname,
    headers[cols] = NULL;
 
    /* 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),"
                      "\n  (SELECT substring(d.adsrc for 128) FROM pg_catalog.pg_attrdef d"
                      "\n   WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef),"
index 2c28ffcabc55fefc4e1e1c7c18a072f907acbe59..443af8e8d6a9f242ac6ec523d1944b84f30187fd 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * 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 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 200305241
+#define CATALOG_VERSION_NO 200305271
 
 #endif
index 7a17ce92873d92d1fa075569ea0a0098d922675d..512c17a43cfdaa76d57410cc14dc7da99ce2f188 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * 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,
                       Node *expr, List *rtable,
                       DependencyType behavior);
 
+extern void recordDependencyOnSingleRelExpr(const ObjectAddress *depender,
+                      Node *expr, Oid relId,
+                      DependencyType behavior,
+                      DependencyType self_behavior);
+
 /* in pg_depend.c */
 
 extern void recordDependencyOn(const ObjectAddress *depender,
index eb4e5f8814bd0283985c481e621667a8e945e325..5833a92e2e2c7f7d14633ba6803fd712eb9580fe 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * 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 @@
 #include "catalog/pg_index.h"
 #include "nodes/execnodes.h"
 
+
 #define DEFAULT_INDEX_TYPE "btree"
 
 /* Typedef for callback function for IndexBuildHeapScan */
@@ -40,12 +41,12 @@ extern Oid index_create(Oid heapRelationId,
 
 extern void index_drop(Oid indexId);
 
-extern IndexInfo *BuildIndexInfo(Form_pg_index indexStruct);
+extern IndexInfo *BuildIndexInfo(Relation index);
 
 extern void FormIndexDatum(IndexInfo *indexInfo,
               HeapTuple heapTuple,
               TupleDesc heapDescriptor,
-              MemoryContext resultCxt,
+              EState *estate,
               Datum *datum,
               char *nullv);
 
index ecdce77f17d4bdb5938568e4cb60c3be0e541660..2984b862aa5cc3beb1c1b81b7202f374d1ee955a 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * 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
  *   the genbki.sh script reads this file and generates .bki
@@ -35,18 +35,19 @@ CATALOG(pg_index) BKI_WITHOUT_OIDS
 {
    Oid         indexrelid;     /* OID of the index */
    Oid         indrelid;       /* OID of the relation it indexes */
-   regproc     indproc;        /* OID of function for functional index */
-   int2vector  indkey;         /* column numbers of indexed attributes */
+   int2vector  indkey;         /* column numbers of indexed cols, or 0 */
    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        indisprimary;   /* is this index for primary key? */
-   Oid         indreference;   /* oid of index of referenced relation (ie
-                                * - this index for foreign key) */
+   bool        indisclustered; /* is this the index last clustered by? */
 
-   /* 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
-                                * partial index */
+                                * partial index; else NULL */
 } FormData_pg_index;
 
 /* ----------------
@@ -63,13 +64,13 @@ typedef FormData_pg_index *Form_pg_index;
 #define Natts_pg_index                 10
 #define Anum_pg_index_indexrelid       1
 #define Anum_pg_index_indrelid         2
-#define Anum_pg_index_indproc          3
-#define Anum_pg_index_indkey           4
-#define Anum_pg_index_indclass         5
-#define Anum_pg_index_indisclustered   6
-#define Anum_pg_index_indisunique      7
-#define Anum_pg_index_indisprimary     8
-#define Anum_pg_index_indreference     9
+#define Anum_pg_index_indkey           3
+#define Anum_pg_index_indclass         4
+#define Anum_pg_index_indnatts         5
+#define Anum_pg_index_indisunique      6
+#define Anum_pg_index_indisprimary     7
+#define Anum_pg_index_indisclustered   8
+#define Anum_pg_index_indexprs         9
 #define Anum_pg_index_indpred          10
 
 #endif   /* PG_INDEX_H */
index 809cde3da02ec4052cf8a25340407b59269b3894..f9de8fa28c89bbce600b6bee153d919c95f95e35 100644 (file)
@@ -8,7 +8,7 @@
  * <opcamid, opcname> --- that is, there is a row for each valid combination
  * 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,
- * 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.
  *
  * For a given <opcamid, opcintype> pair, there can be at most one row that
@@ -26,7 +26,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * 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
  *   the genbki.sh script reads this file and generates .bki
index 17e27969f5302bcae0a77dd8d681ea0469db7fd1..68a3bb9b1cd60908bc0cebd5763b4159fd33fcc0 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * 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 $
  *
  *-------------------------------------------------------------------------
  */
 /* ----------------
  *   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
  *     retail creation of index entries.
  *
  *     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
+ *                         (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
  *     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?
  * ----------------
  */
@@ -47,12 +45,11 @@ typedef struct IndexInfo
 {
    NodeTag     type;
    int         ii_NumIndexAttrs;
-   int         ii_NumKeyAttrs;
    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_PredicateState; /* list of ExprState */
-   Oid         ii_FuncOid;
-   FmgrInfo    ii_FuncInfo;
    bool        ii_Unique;
 } IndexInfo;
 
index 4ad35b683a765496f6de09615c5bb3b137a3c2f0..25d719dd8fc57d8e7bfc133ced23aa5a32c51001 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * 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
 /*
  * IndexElem - index parameters (used in CREATE INDEX)
  *
- * For a plain index, each 'name' is an attribute name in the heap relation;
- * 'funcname' and 'args' are NIL.  For a functional index, only one IndexElem
- * is allowed. It has name = NULL, funcname = name of function and args =
- * list of attribute names that are the function's arguments.
+ * For a plain index attribute, 'name' is the name of the table column to
+ * index, and 'expr' is NULL.  For an index expression, 'name' is NULL and
+ * 'expr' is the expression tree.
  */
 typedef struct IndexElem
 {
    NodeTag     type;
    char       *name;           /* name of attribute to index, or NULL */
-   List       *funcname;       /* qualified name of function */
-   List       *args;           /* list of names of function arguments */
+   Node       *expr;           /* expression to index, or NULL */
    List       *opclass;        /* name of desired opclass; NIL = default */
 } IndexElem;
 
@@ -1271,8 +1269,8 @@ typedef struct IndexStmt
    char       *accessMethod;   /* name of access method (eg. btree) */
    List       *indexParams;    /* a list of IndexElem */
    Node       *whereClause;    /* qualification (partial-index predicate) */
-   List       *rangetable;     /* range table for qual, filled in by
-                                * transformStmt() */
+   List       *rangetable;     /* range table for qual and/or expressions,
+                                * filled in by transformStmt() */
    bool        unique;         /* is index unique? */
    bool        primary;        /* is index on primary key? */
    bool        isconstraint;   /* is it from a CONSTRAINT clause? */
index 03240d641536caa1d8abb250caebb297232fd34f..4f770b7ca10cfc09eec6d7410247e1e1ad37a863 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * 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
  *     and indexes, but that created confusion without actually doing anything
  *     useful.  So now we have a separate IndexOptInfo struct for indexes.
  *
- *     ncolumns and nkeys are the same except for a functional index,
- *     wherein ncolumns is 1 (the single function output) while nkeys
- *     is the number of table columns passed to the function. classlist[]
- *     and ordering[] have ncolumns entries, while indexkeys[] has nkeys
- *     entries.
+ *     classlist[], indexkeys[], and ordering[] have ncolumns entries.
+ *     Zeroes in the indexkeys[] array indicate index columns that are
+ *     expressions; there is one element in indexprs for each such column.
  *
- *     Note: for historical reasons, the arrays classlist, indexkeys and
- *     ordering have an extra entry that is always zero.  Some code scans
- *     until it sees a zero rather than looking at ncolumns or nkeys.
+ *     Note: for historical reasons, the classlist and ordering arrays have
+ *     an extra entry that is always zero.  Some code scans until it sees a
+ *     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
@@ -250,15 +251,14 @@ typedef struct IndexOptInfo
 
    /* index descriptor information */
    int         ncolumns;       /* number of columns in index */
-   int         nkeys;          /* number of keys used by index */
    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         relam;          /* OID of the access method (in pg_am) */
 
    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 */
    bool        unique;         /* true if a unique index */
 
@@ -289,9 +289,8 @@ typedef struct PathKeyItem
 
    /*
     * 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
-    * functional index.  Someday we might allow arbitrary expressions as
-    * path keys, so don't assume more than you must.
+    * can also point to an arbitrary expression representing the value
+    * indexed by an index expression.
     */
 } PathKeyItem;
 
index 5a38d6ef2ad9782b10c013855062e895dc3e1f00..77dd37659515fbd4ec75e5465aabd1df15df233e 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * 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,
 extern Node *get_leftop(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 Expr *make_notclause(Expr *notclause);
 extern Expr *get_notclausearg(Expr *notclause);
index a9ff7325c1e504428ff59cb0e4171bd0eda98cfd..fd5e0c56b76e825fdbefe16bdd2d13db35c018b4 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * 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
 
    /* These are non-NULL only for an index relation: */
    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 */
 
    /* index access support info (used only for an index relation) */
@@ -142,6 +144,8 @@ typedef struct RelationData
    struct FmgrInfo *rd_supportinfo;    /* lookup info for support
                                         * procedures */
    /* "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 */
    PgStat_Info pgstat_info;
index c04952849129f22f4722f1d5ba7d7d75abdd5495..f28d23a6268f0589cc8b5977e0598a0eda22e3e2 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * 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);
  * Routines to compute/retrieve additional cached information
  */
 extern List *RelationGetIndexList(Relation relation);
+extern List *RelationGetIndexExpressions(Relation relation);
+extern List *RelationGetIndexPredicate(Relation relation);
 
 extern void RelationInitIndexAccessInfo(Relation relation);
 
index 1ab1d5840c2c06c7cfcd6250f91d82a280170fc8..46d6d46ae07e139bc68605d93581e4bce113d289 100755 (executable)
@@ -36,8 +36,8 @@ def list_simple_ind(pgcnx):
            ic.relname AS index_name, a.attname
        FROM pg_class bc, pg_class ic, pg_index i, pg_attribute a
        WHERE i.indrelid = bc.oid AND i.indexrelid = bc.oid
-               AND i.indkey[0] = a.attnum AND a.attrelid = bc.oid
-               AND i.indproc = '0'::oid AND a.attisdropped = 'f'
+               AND i.indkey[0] = a.attnum AND i.indnatts = 1
+               AND a.attrelid = bc.oid AND a.attisdropped = 'f'
        ORDER BY class_name, index_name, attname""")
    return result
 
index 243198d79f2713a0dbef91b4f859c20ab604dd75..d82ed8c4e8931504ef21d3dd7b3d4a28db55ad43 100644 (file)
@@ -81,3 +81,17 @@ 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');
+--
+-- 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');
index 2dd74613d997efe39572c42b92c4a1e62b8bce49..87e1943e5a26afcd7636da3821daea04e44ec7bb 100644 (file)
@@ -75,7 +75,7 @@ FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = relnamespace
 WHERE relhasoids
     AND ((nspname ~ '^pg_') IS NOT FALSE)
     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 
 ---------+---------
 (0 rows)
index fe49d4ec2e2220d2909492dc3aa25edd126f7aab..383a7a1a9f926d9d9c678fc30cbd2dceb8ab7f96 100644 (file)
@@ -104,3 +104,19 @@ INSERT INTO func_index_heap VALUES('QWE','RTY');
 INSERT INTO func_index_heap VALUES('ABCD', 'EF');
 -- but this shouldn't:
 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');
index 50b1a2659b2c286fdccdb3675357f399cac44911..108d4f781245f289005fbec0fe9b5a989e5099bc 100644 (file)
@@ -21,4 +21,4 @@ FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = relnamespace
 WHERE relhasoids
     AND ((nspname ~ '^pg_') IS NOT FALSE)
     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);
index 4ef6d513bf47126b3bbfae1ddf73187c04789cf2..ab5e26c94b5a94e92f82c596b948beec653aa75e 100644 (file)
@@ -7,7 +7,7 @@
 -- Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
 -- 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
 
 
 --
--- lists all simple indices (ie. those that are not defined over a function
--- of several attributes)
+-- lists all simple indices (ie. those that are defined over one simple
+-- column reference)
 --
 SELECT bc.relname AS class_name, 
        ic.relname AS index_name, 
@@ -44,8 +44,8 @@ SELECT bc.relname AS class_name,
   WHERE i.indrelid = bc.oid
      and i.indexrelid = ic.oid
      and i.indkey[0] = a.attnum
+     and i.indnatts = 1
      and a.attrelid = bc.oid
-     and i.indproc = '0'::oid   -- no functional indices
   ORDER BY class_name, index_name, attname;