From 4ad658cac9aa990f29c23e632d3578ed30aa9232 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Tue, 30 Jul 2002 21:56:04 +0000 Subject: [PATCH] Teach pg_dump to dump user-defined operator classes. For the moment, 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 | 15 +- src/bin/pg_dump/pg_dump.c | 317 +++++++++++++++++++++++++++++++++++++- src/bin/pg_dump/pg_dump.h | 13 +- 3 files changed, 342 insertions(+), 3 deletions(-) diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c index 123ef3df05..0e3ffe91e5 100644 --- a/src/bin/pg_dump/common.c +++ b/src/bin/pg_dump/common.c @@ -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) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 00725ae95f..76623f6183 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -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 diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 732fdfb74e..b3b943dde0 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -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); -- GitLab