Teach pg_dump to dump user-defined operator classes. For the moment,
authorTom Lane
Tue, 30 Jul 2002 21:56:04 +0000 (21:56 +0000)
committerTom Lane
Tue, 30 Jul 2002 21:56:04 +0000 (21:56 +0000)
this only works against 7.3 or later databases; the pushups required
to do it without regprocedure/regtype/etc seem more trouble than they're
worth, considering that existing users aren't expecting pg_dump support
for this.

src/bin/pg_dump/common.c
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/pg_dump.h

index 123ef3df056dcbb41c19330567dced91436383a8..0e3ffe91e5ce03ac5cd011dffd01b83aa6e234c9 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/bin/pg_dump/common.c,v 1.66 2002/07/18 23:11:29 petere Exp $
+ *   $Header: /cvsroot/pgsql/src/bin/pg_dump/common.c,v 1.67 2002/07/30 21:56:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -60,6 +60,7 @@ dumpSchema(Archive *fout,
    int         numInherits;
    int         numAggregates;
    int         numOperators;
+   int         numOpclasses;
    NamespaceInfo *nsinfo;
    TypeInfo   *tinfo;
    FuncInfo   *finfo;
@@ -67,6 +68,7 @@ dumpSchema(Archive *fout,
    TableInfo  *tblinfo;
    InhInfo    *inhinfo;
    OprInfo    *oprinfo;
+   OpclassInfo *opcinfo;
 
    if (g_verbose)
        write_msg(NULL, "reading namespaces\n");
@@ -88,6 +90,10 @@ dumpSchema(Archive *fout,
        write_msg(NULL, "reading user-defined operators\n");
    oprinfo = getOperators(&numOperators);
 
+   if (g_verbose)
+       write_msg(NULL, "reading user-defined operator classes\n");
+   opcinfo = getOpclasses(&numOpclasses);
+
    if (g_verbose)
        write_msg(NULL, "reading user-defined tables\n");
    tblinfo = getTables(&numTables);
@@ -170,6 +176,13 @@ dumpSchema(Archive *fout,
        dumpOprs(fout, oprinfo, numOperators);
    }
 
+   if (!dataOnly)
+   {
+       if (g_verbose)
+           write_msg(NULL, "dumping out user-defined operator classes\n");
+       dumpOpclasses(fout, opcinfo, numOpclasses);
+   }
+
    if (!dataOnly)
    {
        if (g_verbose)
index 00725ae95ff48602929b2323641a56af0f5c6ce8..76623f61835ba76cdba29982656802d8b115572c 100644 (file)
@@ -22,7 +22,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.276 2002/07/25 20:52:59 petere Exp $
+ *   $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.277 2002/07/30 21:56:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -118,6 +118,7 @@ static void dumpOneOpr(Archive *fout, OprInfo *oprinfo,
 static const char *convertRegProcReference(const char *proc);
 static const char *convertOperatorReference(const char *opr,
                        OprInfo *g_oprinfo, int numOperators);
+static void dumpOneOpclass(Archive *fout, OpclassInfo *opcinfo);
 static void dumpOneAgg(Archive *fout, AggInfo *agginfo);
 static Oid findLastBuiltinOid_V71(const char *);
 static Oid findLastBuiltinOid_V70(void);
@@ -1754,6 +1755,90 @@ getOperators(int *numOprs)
    return oprinfo;
 }
 
+/*
+ * getOpclasses:
+ *   read all opclasses in the system catalogs and return them in the
+ * OpclassInfo* structure
+ *
+ * numOpclasses is set to the number of opclasses read in
+ */
+OpclassInfo *
+getOpclasses(int *numOpclasses)
+{
+   PGresult   *res;
+   int         ntups;
+   int         i;
+   PQExpBuffer query = createPQExpBuffer();
+   OpclassInfo    *opcinfo;
+   int         i_oid;
+   int         i_opcname;
+   int         i_opcnamespace;
+   int         i_usename;
+
+   /*
+    * find all opclasses, including builtin opclasses;
+    * we filter out system-defined opclasses at dump-out time.
+    */
+
+   /* Make sure we are in proper schema */
+   selectSourceSchema("pg_catalog");
+
+   if (g_fout->remoteVersion >= 70300)
+   {
+       appendPQExpBuffer(query, "SELECT pg_opclass.oid, opcname, "
+                         "opcnamespace, "
+                         "(select usename from pg_user where opcowner = usesysid) as usename "
+                         "from pg_opclass");
+   }
+   else
+   {
+       appendPQExpBuffer(query, "SELECT pg_opclass.oid, opcname, "
+                         "0::oid as opcnamespace, "
+                         "''::name as usename "
+                         "from pg_opclass");
+   }
+
+   res = PQexec(g_conn, query->data);
+   if (!res ||
+       PQresultStatus(res) != PGRES_TUPLES_OK)
+   {
+       write_msg(NULL, "query to obtain list of opclasses failed: %s", PQerrorMessage(g_conn));
+       exit_nicely();
+   }
+
+   ntups = PQntuples(res);
+   *numOpclasses = ntups;
+
+   opcinfo = (OpclassInfo *) malloc(ntups * sizeof(OpclassInfo));
+
+   i_oid = PQfnumber(res, "oid");
+   i_opcname = PQfnumber(res, "opcname");
+   i_opcnamespace = PQfnumber(res, "opcnamespace");
+   i_usename = PQfnumber(res, "usename");
+
+   for (i = 0; i < ntups; i++)
+   {
+       opcinfo[i].oid = strdup(PQgetvalue(res, i, i_oid));
+       opcinfo[i].opcname = strdup(PQgetvalue(res, i, i_opcname));
+       opcinfo[i].opcnamespace = findNamespace(PQgetvalue(res, i, i_opcnamespace),
+                                               opcinfo[i].oid);
+       opcinfo[i].usename = strdup(PQgetvalue(res, i, i_usename));
+
+       if (g_fout->remoteVersion >= 70300)
+       {
+           if (strlen(opcinfo[i].usename) == 0)
+               write_msg(NULL, "WARNING: owner of opclass \"%s\" appears to be invalid\n",
+                         opcinfo[i].opcname);
+       }
+   }
+
+   PQclear(res);
+
+   destroyPQExpBuffer(query);
+
+   return opcinfo;
+}
+
 /*
  * getAggregates:
  *   read all the user-defined aggregates in the system catalogs and
@@ -3981,6 +4066,236 @@ convertOperatorReference(const char *opr,
    return name;
 }
 
+
+/*
+ * dumpOpclasses
+ *   writes out to fout the queries to recreate all the user-defined
+ *   operator classes
+ */
+void
+dumpOpclasses(Archive *fout, OpclassInfo *opcinfo, int numOpclasses)
+{
+   int         i;
+
+   for (i = 0; i < numOpclasses; i++)
+   {
+       /* Dump only opclasses in dumpable namespaces */
+       if (!opcinfo[i].opcnamespace->dump)
+           continue;
+
+       /* OK, dump it */
+       dumpOneOpclass(fout, &opcinfo[i]);
+   }
+}
+
+/*
+ * dumpOneOpclass
+ *   write out a single operator class definition
+ */
+static void
+dumpOneOpclass(Archive *fout, OpclassInfo *opcinfo)
+{
+   PQExpBuffer query = createPQExpBuffer();
+   PQExpBuffer q = createPQExpBuffer();
+   PQExpBuffer delq = createPQExpBuffer();
+   PGresult   *res;
+   int         ntups;
+   int         i_opcintype;
+   int         i_opckeytype;
+   int         i_opcdefault;
+   int         i_amname;
+   int         i_amopstrategy;
+   int         i_amopreqcheck;
+   int         i_amopopr;
+   int         i_amprocnum;
+   int         i_amproc;
+   char       *opcintype;
+   char       *opckeytype;
+   char       *opcdefault;
+   char       *amname;
+   char       *amopstrategy;
+   char       *amopreqcheck;
+   char       *amopopr;
+   char       *amprocnum;
+   char       *amproc;
+   bool        needComma;
+   int         i;
+
+   /*
+    * XXX currently we do not implement dumping of operator classes from
+    * pre-7.3 databases.  This could be done but it seems not worth the
+    * trouble.
+    */
+   if (g_fout->remoteVersion < 70300)
+       return;
+
+   /* Make sure we are in proper schema so regoperator works correctly */
+   selectSourceSchema(opcinfo->opcnamespace->nspname);
+
+   /* Get additional fields from the pg_opclass row */
+   appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
+                     "opckeytype::pg_catalog.regtype, "
+                     "opcdefault, "
+                     "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcamid) AS amname "
+                     "FROM pg_catalog.pg_opclass "
+                     "WHERE oid = '%s'::pg_catalog.oid",
+                     opcinfo->oid);
+
+   res = PQexec(g_conn, query->data);
+   if (!res ||
+       PQresultStatus(res) != PGRES_TUPLES_OK)
+   {
+       write_msg(NULL, "query to obtain opclass details failed: %s", PQerrorMessage(g_conn));
+       exit_nicely();
+   }
+
+   /* Expecting a single result only */
+   ntups = PQntuples(res);
+   if (ntups != 1)
+   {
+       write_msg(NULL, "Got %d rows instead of one from: %s",
+                 ntups, query->data);
+       exit_nicely();
+   }
+
+   i_opcintype = PQfnumber(res, "opcintype");
+   i_opckeytype = PQfnumber(res, "opckeytype");
+   i_opcdefault = PQfnumber(res, "opcdefault");
+   i_amname = PQfnumber(res, "amname");
+
+   opcintype = PQgetvalue(res, 0, i_opcintype);
+   opckeytype = PQgetvalue(res, 0, i_opckeytype);
+   opcdefault = PQgetvalue(res, 0, i_opcdefault);
+   amname = PQgetvalue(res, 0, i_amname);
+
+   /* DROP must be fully qualified in case same name appears in pg_catalog */
+   appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
+                     fmtId(opcinfo->opcnamespace->nspname, force_quotes));
+   appendPQExpBuffer(delq, ".%s",
+                     fmtId(opcinfo->opcname, force_quotes));
+   appendPQExpBuffer(delq, " USING %s;\n",
+                     fmtId(amname, force_quotes));
+
+   /* Build the fixed portion of the CREATE command */
+   appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n\t",
+                     fmtId(opcinfo->opcname, force_quotes));
+   if (strcmp(opcdefault, "t") == 0)
+       appendPQExpBuffer(q, "DEFAULT ");
+   appendPQExpBuffer(q, "FOR TYPE %s USING %s AS\n\t",
+                     opcintype,
+                     fmtId(amname, force_quotes));
+
+   needComma = false;
+
+   if (strcmp(opckeytype, "-") != 0)
+   {
+       appendPQExpBuffer(q, "STORAGE\t%s",
+                         opckeytype);
+       needComma = true;
+   }
+
+   PQclear(res);
+
+   /*
+    * Now fetch and print the OPERATOR entries (pg_amop rows).
+    */
+   resetPQExpBuffer(query);
+
+   appendPQExpBuffer(query, "SELECT amopstrategy, amopreqcheck, "
+                     "amopopr::pg_catalog.regoperator "
+                     "FROM pg_catalog.pg_amop "
+                     "WHERE amopclaid = '%s'::pg_catalog.oid "
+                     "ORDER BY amopstrategy",
+                     opcinfo->oid);
+
+   res = PQexec(g_conn, query->data);
+   if (!res ||
+       PQresultStatus(res) != PGRES_TUPLES_OK)
+   {
+       write_msg(NULL, "query to obtain opclass operators failed: %s", PQerrorMessage(g_conn));
+       exit_nicely();
+   }
+
+   ntups = PQntuples(res);
+
+   i_amopstrategy = PQfnumber(res, "amopstrategy");
+   i_amopreqcheck = PQfnumber(res, "amopreqcheck");
+   i_amopopr = PQfnumber(res, "amopopr");
+
+   for (i = 0; i < ntups; i++)
+   {
+       amopstrategy = PQgetvalue(res, i, i_amopstrategy);
+       amopreqcheck = PQgetvalue(res, i, i_amopreqcheck);
+       amopopr = PQgetvalue(res, i, i_amopopr);
+
+       if (needComma)
+           appendPQExpBuffer(q, " ,\n\t");
+
+       appendPQExpBuffer(q, "OPERATOR\t%s\t%s",
+                         amopstrategy, amopopr);
+       if (strcmp(amopreqcheck, "t") == 0)
+           appendPQExpBuffer(q, "\tRECHECK");
+
+       needComma = true;
+   }
+
+   PQclear(res);
+
+   /*
+    * Now fetch and print the FUNCTION entries (pg_amproc rows).
+    */
+   resetPQExpBuffer(query);
+
+   appendPQExpBuffer(query, "SELECT amprocnum, "
+                     "amproc::pg_catalog.regprocedure "
+                     "FROM pg_catalog.pg_amproc "
+                     "WHERE amopclaid = '%s'::pg_catalog.oid "
+                     "ORDER BY amprocnum",
+                     opcinfo->oid);
+
+   res = PQexec(g_conn, query->data);
+   if (!res ||
+       PQresultStatus(res) != PGRES_TUPLES_OK)
+   {
+       write_msg(NULL, "query to obtain opclass functions failed: %s", PQerrorMessage(g_conn));
+       exit_nicely();
+   }
+
+   ntups = PQntuples(res);
+
+   i_amprocnum = PQfnumber(res, "amprocnum");
+   i_amproc = PQfnumber(res, "amproc");
+
+   for (i = 0; i < ntups; i++)
+   {
+       amprocnum = PQgetvalue(res, i, i_amprocnum);
+       amproc = PQgetvalue(res, i, i_amproc);
+
+       if (needComma)
+           appendPQExpBuffer(q, " ,\n\t");
+
+       appendPQExpBuffer(q, "FUNCTION\t%s\t%s",
+                         amprocnum, amproc);
+
+       needComma = true;
+   }
+
+   PQclear(res);
+
+   appendPQExpBuffer(q, " ;\n");
+
+   ArchiveEntry(fout, opcinfo->oid, opcinfo->opcname,
+                opcinfo->opcnamespace->nspname, opcinfo->usename,
+                "OPERATOR CLASS", NULL,
+                q->data, delq->data,
+                NULL, NULL, NULL);
+
+   destroyPQExpBuffer(query);
+   destroyPQExpBuffer(q);
+   destroyPQExpBuffer(delq);
+}
+
+
 /*
  * dumpAggs
  *   writes out to fout the queries to create all the user-defined aggregates
index 732fdfb74e411aa2ee6d611e955481827032ddf5..b3b943dde08c4516543efc727797474a633f21c5 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
  *
- * $Id: pg_dump.h,v 1.91 2002/07/18 23:11:29 petere Exp $
+ * $Id: pg_dump.h,v 1.92 2002/07/30 21:56:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -86,6 +86,14 @@ typedef struct _oprInfo
    char       *oprcode;        /* as OID, not regproc name */
 } OprInfo;
 
+typedef struct _opclassInfo
+{
+   char       *oid;
+   char       *opcname;
+   NamespaceInfo *opcnamespace;    /* link to containing namespace */
+   char       *usename;
+} OpclassInfo;
+
 typedef struct _tableInfo
 {
    /*
@@ -192,6 +200,7 @@ extern TypeInfo *getTypes(int *numTypes);
 extern FuncInfo *getFuncs(int *numFuncs);
 extern AggInfo *getAggregates(int *numAggregates);
 extern OprInfo *getOperators(int *numOperators);
+extern OpclassInfo *getOpclasses(int *numOpclasses);
 extern TableInfo *getTables(int *numTables);
 extern InhInfo *getInherits(int *numInherits);
 
@@ -207,6 +216,8 @@ extern void dumpCasts(Archive *fout, FuncInfo *finfo, int numFuncs,
                      TypeInfo *tinfo, int numTypes);
 extern void dumpAggs(Archive *fout, AggInfo agginfo[], int numAggregates);
 extern void dumpOprs(Archive *fout, OprInfo *oprinfo, int numOperators);
+extern void dumpOpclasses(Archive *fout,
+                         OpclassInfo *opcinfo, int numOpclasses);
 extern void dumpTables(Archive *fout, TableInfo tblinfo[], int numTables,
                       const bool aclsSkip,
                       const bool schemaOnly, const bool dataOnly);