Extend pg_get_indexdef() to know about index predicates. Also, tweak
authorTom Lane
Mon, 1 Oct 2001 20:15:26 +0000 (20:15 +0000)
committerTom Lane
Mon, 1 Oct 2001 20:15:26 +0000 (20:15 +0000)
it to suppress index opclass output for opclasses that are the default
for their datatype; only non-default opclasses are shown explicitly.
This is expected to improve portability of the CREATE INDEX command
across future versions of Postgres --- we've changed index opclasses
too often in the past to think we won't do so again.

src/backend/utils/adt/ruleutils.c

index a5f9d2ace1a0c319137d339c343e945381079f9d..6d4d926078d4385131b14580f9d44f83174741da 100644 (file)
@@ -3,7 +3,7 @@
  *             back to source text
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.82 2001/08/12 21:35:19 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.83 2001/10/01 20:15:26 tgl Exp $
  *
  *   This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -41,7 +41,9 @@
 #include 
 
 #include "catalog/heap.h"
+#include "catalog/index.h"
 #include "catalog/pg_index.h"
+#include "catalog/pg_opclass.h"
 #include "catalog/pg_operator.h"
 #include "catalog/pg_shadow.h"
 #include "executor/spi.h"
@@ -94,10 +96,6 @@ static void *plan_getrule = NULL;
 static char *query_getrule = "SELECT * FROM pg_rewrite WHERE rulename = $1";
 static void *plan_getview = NULL;
 static char *query_getview = "SELECT * FROM pg_rewrite WHERE rulename = $1";
-static void *plan_getam = NULL;
-static char *query_getam = "SELECT * FROM pg_am WHERE oid = $1";
-static void *plan_getopclass = NULL;
-static char *query_getopclass = "SELECT * FROM pg_opclass WHERE oid = $1";
 
 
 /* ----------
@@ -138,6 +136,8 @@ static void get_sublink_expr(Node *node, deparse_context *context);
 static void get_from_clause(Query *query, deparse_context *context);
 static void get_from_clause_item(Node *jtnode, Query *query,
                     deparse_context *context);
+static void get_opclass_name(Oid opclass, bool only_nondefault,
+                            StringInfo buf);
 static bool tleIsArrayAssign(TargetEntry *tle);
 static char *quote_identifier(char *ident);
 static char *get_relation_name(Oid relid);
@@ -341,48 +341,16 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
    HeapTuple   ht_idx;
    HeapTuple   ht_idxrel;
    HeapTuple   ht_indrel;
-   HeapTuple   spi_tup;
-   TupleDesc   spi_ttc;
-   int         spi_fno;
    Form_pg_index idxrec;
    Form_pg_class idxrelrec;
    Form_pg_class indrelrec;
-   Datum       spi_args[1];
-   char        spi_nulls[2];
-   int         spirc;
+   Form_pg_am  amrec;
    int         len;
    int         keyno;
    StringInfoData buf;
    StringInfoData keybuf;
    char       *sep;
 
-   /*
-    * Connect to SPI manager
-    */
-   if (SPI_connect() != SPI_OK_CONNECT)
-       elog(ERROR, "get_indexdef: cannot connect to SPI manager");
-
-   /*
-    * On the first call prepare the plans to lookup pg_am and pg_opclass.
-    */
-   if (plan_getam == NULL)
-   {
-       Oid         argtypes[1];
-       void       *plan;
-
-       argtypes[0] = OIDOID;
-       plan = SPI_prepare(query_getam, 1, argtypes);
-       if (plan == NULL)
-           elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getam);
-       plan_getam = SPI_saveplan(plan);
-
-       argtypes[0] = OIDOID;
-       plan = SPI_prepare(query_getopclass, 1, argtypes);
-       if (plan == NULL)
-           elog(ERROR, "SPI_prepare() failed for \"%s\"", query_getopclass);
-       plan_getopclass = SPI_saveplan(plan);
-   }
-
    /*
     * Fetch the pg_index tuple by the Oid of the index
     */
@@ -414,21 +382,14 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
    indrelrec = (Form_pg_class) GETSTRUCT(ht_indrel);
 
    /*
-    * Get the am name for the index relation
+    * Fetch the pg_am tuple of the index' access method
+    *
+    * There is no syscache for this, so use index.c subroutine.
     */
-   spi_args[0] = ObjectIdGetDatum(idxrelrec->relam);
-   spi_nulls[0] = ' ';
-   spi_nulls[1] = '\0';
-   spirc = SPI_execp(plan_getam, spi_args, spi_nulls, 1);
-   if (spirc != SPI_OK_SELECT)
-       elog(ERROR, "failed to get pg_am tuple for index %s",
-            NameStr(idxrelrec->relname));
-   if (SPI_processed != 1)
-       elog(ERROR, "failed to get pg_am tuple for index %s",
-            NameStr(idxrelrec->relname));
-   spi_tup = SPI_tuptable->vals[0];
-   spi_ttc = SPI_tuptable->tupdesc;
-   spi_fno = SPI_fnumber(spi_ttc, "amname");
+   amrec = AccessMethodObjectIdGetForm(idxrelrec->relam,
+                                       CurrentMemoryContext);
+   if (!amrec)
+       elog(ERROR, "lookup for AM %u failed", idxrelrec->relam);
 
    /*
     * Start the index definition
@@ -436,13 +397,12 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
    initStringInfo(&buf);
    appendStringInfo(&buf, "CREATE %sINDEX %s ON %s USING %s (",
                     idxrec->indisunique ? "UNIQUE " : "",
-                 quote_identifier(pstrdup(NameStr(idxrelrec->relname))),
-                 quote_identifier(pstrdup(NameStr(indrelrec->relname))),
-                    quote_identifier(SPI_getvalue(spi_tup, spi_ttc,
-                                                  spi_fno)));
+                    quote_identifier(NameStr(idxrelrec->relname)),
+                    quote_identifier(NameStr(indrelrec->relname)),
+                    quote_identifier(NameStr(amrec->amname)));
 
    /*
-    * Collect the indexed attributes
+    * Collect the indexed attributes in keybuf
     */
    initStringInfo(&keybuf);
    sep = "";
@@ -465,29 +425,14 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
         * If not a functional index, add the operator class name
         */
        if (idxrec->indproc == InvalidOid)
-       {
-           spi_args[0] = ObjectIdGetDatum(idxrec->indclass[keyno]);
-           spi_nulls[0] = ' ';
-           spi_nulls[1] = '\0';
-           spirc = SPI_execp(plan_getopclass, spi_args, spi_nulls, 1);
-           if (spirc != SPI_OK_SELECT)
-               elog(ERROR, "failed to get pg_opclass tuple %u", idxrec->indclass[keyno]);
-           if (SPI_processed != 1)
-               elog(ERROR, "failed to get pg_opclass tuple %u", idxrec->indclass[keyno]);
-           spi_tup = SPI_tuptable->vals[0];
-           spi_ttc = SPI_tuptable->tupdesc;
-           spi_fno = SPI_fnumber(spi_ttc, "opcname");
-           appendStringInfo(&keybuf, " %s",
-                         quote_identifier(SPI_getvalue(spi_tup, spi_ttc,
-                                                       spi_fno)));
-       }
+           get_opclass_name(idxrec->indclass[keyno], true, &keybuf);
    }
 
-   /*
-    * For functional index say 'func (attrs) opclass'
-    */
    if (idxrec->indproc != InvalidOid)
    {
+       /*
+        * For functional index say 'func (attrs) opclass'
+        */
        HeapTuple   proctup;
        Form_pg_proc procStruct;
 
@@ -498,58 +443,67 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
            elog(ERROR, "cache lookup for proc %u failed", idxrec->indproc);
        procStruct = (Form_pg_proc) GETSTRUCT(proctup);
 
-       appendStringInfo(&buf, "%s(%s) ",
-                quote_identifier(pstrdup(NameStr(procStruct->proname))),
+       appendStringInfo(&buf, "%s(%s)",
+                        quote_identifier(NameStr(procStruct->proname)),
                         keybuf.data);
+       get_opclass_name(idxrec->indclass[0], true, &buf);
 
-       spi_args[0] = ObjectIdGetDatum(idxrec->indclass[0]);
-       spi_nulls[0] = ' ';
-       spi_nulls[1] = '\0';
-       spirc = SPI_execp(plan_getopclass, spi_args, spi_nulls, 1);
-       if (spirc != SPI_OK_SELECT)
-           elog(ERROR, "failed to get pg_opclass tuple %u", idxrec->indclass[0]);
-       if (SPI_processed != 1)
-           elog(ERROR, "failed to get pg_opclass tuple %u", idxrec->indclass[0]);
-       spi_tup = SPI_tuptable->vals[0];
-       spi_ttc = SPI_tuptable->tupdesc;
-       spi_fno = SPI_fnumber(spi_ttc, "opcname");
-       appendStringInfo(&buf, "%s",
-                        quote_identifier(SPI_getvalue(spi_tup, spi_ttc,
-                                                      spi_fno)));
        ReleaseSysCache(proctup);
    }
    else
-
+   {
        /*
-        * For the others say 'attr opclass [, ...]'
+        * Otherwise say 'attr opclass [, ...]'
         */
        appendStringInfo(&buf, "%s", keybuf.data);
+   }
+
+   appendStringInfo(&buf, ")");
 
    /*
-    * Finish
+    * If it's a partial index, decompile and append the predicate
     */
-   appendStringInfo(&buf, ")");
+   if (VARSIZE(&idxrec->indpred) > VARHDRSZ)
+   {
+       Node    *node;
+       List    *context;
+       char    *exprstr;
+       char    *str;
+
+       /* Convert TEXT object to C string */
+       exprstr = DatumGetCString(DirectFunctionCall1(textout,
+                                                     PointerGetDatum(&idxrec->indpred)));
+       /* Convert expression to node tree */
+       node = (Node *) stringToNode(exprstr);
+       /*
+        * If top level is a List, assume it is an implicit-AND structure,
+        * and convert to explicit AND.  This is needed for partial index
+        * predicates.
+        */
+       if (node && IsA(node, List))
+           node = (Node *) make_ands_explicit((List *) node);
+       /* Deparse */
+       context = deparse_context_for(NameStr(indrelrec->relname),
+                                     idxrec->indrelid);
+       str = deparse_expression(node, context, false);
+       appendStringInfo(&buf, " WHERE %s", str);
+   }
 
    /*
-    * Create the result in upper executor memory, and free objects
+    * Create the result as a TEXT datum, and free working data
     */
    len = buf.len + VARHDRSZ;
-   indexdef = SPI_palloc(len);
+   indexdef = (text *) palloc(len);
    VARATT_SIZEP(indexdef) = len;
    memcpy(VARDATA(indexdef), buf.data, buf.len);
 
    pfree(buf.data);
    pfree(keybuf.data);
+   pfree(amrec);
    ReleaseSysCache(ht_idx);
    ReleaseSysCache(ht_idxrel);
    ReleaseSysCache(ht_indrel);
 
-   /*
-    * Disconnect from SPI manager
-    */
-   if (SPI_finish() != SPI_OK_FINISH)
-       elog(ERROR, "get_viewdef: SPI_finish() failed");
-
    PG_RETURN_TEXT_P(indexdef);
 }
 
@@ -2546,6 +2500,32 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
    dpns->namespace = sv_namespace;
 }
 
+/* ----------
+ * get_opclass_name            - fetch name of an index operator class
+ *
+ * The opclass name is appended (after a space) to buf.
+ * If "only_nondefault" is true, the opclass name is appended only if
+ * it isn't the default for its datatype.
+ * ----------
+ */
+static void
+get_opclass_name(Oid opclass, bool only_nondefault,
+                StringInfo buf)
+{
+   HeapTuple   ht_opc;
+   Form_pg_opclass opcrec;
+
+   ht_opc = SearchSysCache(CLAOID,
+                           ObjectIdGetDatum(opclass),
+                           0, 0, 0);
+   if (!HeapTupleIsValid(ht_opc))
+       elog(ERROR, "cache lookup failed for opclass %u", opclass);
+   opcrec = (Form_pg_opclass) GETSTRUCT(ht_opc);
+   if (!only_nondefault || !opcrec->opcdefault)
+       appendStringInfo(buf, " %s",
+                        quote_identifier(NameStr(opcrec->opcname)));
+   ReleaseSysCache(ht_opc);
+}
 
 /* ----------
  * tleIsArrayAssign            - check for array assignment