* by PostgreSQL
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.367 2004/03/03 21:28:54 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.368 2004/03/20 20:09:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
opterr;
+typedef struct
+{
+ const char *descr; /* comment for an object */
+ Oid classoid; /* object class (catalog OID) */
+ Oid objoid; /* object OID */
+ int objsubid; /* subobject (table column #) */
+} CommentItem;
+
+
/* global decls */
bool g_verbose; /* User wants verbose narration of our
* activities. */
static void dumpComment(Archive *fout, const char *target,
const char *namespace, const char *owner,
CatalogId catalogId, int subid, DumpId dumpId);
+static int findComments(Archive *fout, Oid classoid, Oid objoid,
+ CommentItem **items);
+static int collectComments(Archive *fout, CommentItem **items);
static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
static void dumpNamespace(Archive *fout, NamespaceInfo *nspinfo);
static void dumpType(Archive *fout, TypeInfo *tinfo);
const char *namespace, const char *owner,
CatalogId catalogId, int subid, DumpId dumpId)
{
- PGresult *res;
- PQExpBuffer query;
- int i_description;
+ CommentItem *comments;
+ int ncomments;
/* Comments are SCHEMA not data */
if (dataOnly)
return;
- /*
- * Note we do NOT change source schema here; preserve the caller's
- * setting, instead.
- */
+ /* Search for comments associated with catalogId, using table */
+ ncomments = findComments(fout, catalogId.tableoid, catalogId.oid,
+ &comments);
- /* Build query to find comment */
-
- query = createPQExpBuffer();
-
- if (fout->remoteVersion >= 70300)
- {
- appendPQExpBuffer(query,
- "SELECT description FROM pg_catalog.pg_description "
- "WHERE classoid = '%u'::pg_catalog.oid and "
- "objoid = '%u'::pg_catalog.oid and objsubid = %d",
- catalogId.tableoid, catalogId.oid, subid);
- }
- else if (fout->remoteVersion >= 70200)
- {
- appendPQExpBuffer(query,
- "SELECT description FROM pg_description "
- "WHERE classoid = '%u'::oid and "
- "objoid = '%u'::oid and objsubid = %d",
- catalogId.tableoid, catalogId.oid, subid);
- }
- else
+ /* Is there one matching the subid? */
+ while (ncomments > 0)
{
- /* Note: this will fail to find attribute comments in pre-7.2... */
- appendPQExpBuffer(query, "SELECT description FROM pg_description WHERE objoid = '%u'::oid", catalogId.oid);
+ if (comments->objsubid == subid)
+ break;
+ comments++;
+ ncomments--;
}
- /* Execute query */
-
- res = PQexec(g_conn, query->data);
- check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
-
/* If a comment exists, build COMMENT ON statement */
-
- if (PQntuples(res) == 1)
+ if (ncomments > 0)
{
- i_description = PQfnumber(res, "description");
- resetPQExpBuffer(query);
+ PQExpBuffer query = createPQExpBuffer();
+
appendPQExpBuffer(query, "COMMENT ON %s IS ", target);
- appendStringLiteral(query, PQgetvalue(res, 0, i_description), false);
+ appendStringLiteral(query, comments->descr, false);
appendPQExpBuffer(query, ";\n");
ArchiveEntry(fout, nilCatalogId, createDumpId(),
"COMMENT", query->data, "", NULL,
&(dumpId), 1,
NULL, NULL);
- }
- PQclear(res);
- destroyPQExpBuffer(query);
+ destroyPQExpBuffer(query);
+ }
}
/*
* dumpTableComment --
*
* As above, but dump comments for both the specified table (or view)
- * and its columns. For speed, we want to do this with only one query.
+ * and its columns.
*/
static void
dumpTableComment(Archive *fout, TableInfo *tbinfo,
const char *reltypename)
{
- PGresult *res;
+ CommentItem *comments;
+ int ncomments;
PQExpBuffer query;
PQExpBuffer target;
- int i_description;
- int i_objsubid;
- int ntups;
- int i;
/* Comments are SCHEMA not data */
if (dataOnly)
return;
- /*
- * Note we do NOT change source schema here; preserve the caller's
- * setting, instead.
- */
+ /* Search for comments associated with relation, using table */
+ ncomments = findComments(fout,
+ tbinfo->dobj.catId.tableoid,
+ tbinfo->dobj.catId.oid,
+ &comments);
- /* Build query to find comments */
+ /* If comments exist, build COMMENT ON statements */
+ if (ncomments <= 0)
+ return;
query = createPQExpBuffer();
target = createPQExpBuffer();
- if (fout->remoteVersion >= 70300)
- {
- appendPQExpBuffer(query, "SELECT description, objsubid FROM pg_catalog.pg_description "
- "WHERE classoid = '%u'::pg_catalog.oid and "
- "objoid = '%u'::pg_catalog.oid "
- "ORDER BY objoid, classoid, objsubid",
- tbinfo->dobj.catId.tableoid, tbinfo->dobj.catId.oid);
- }
- else if (fout->remoteVersion >= 70200)
- {
- appendPQExpBuffer(query, "SELECT description, objsubid FROM pg_description "
- "WHERE classoid = '%u'::oid and "
- "objoid = '%u'::oid "
- "ORDER BY objoid, classoid, objsubid",
- tbinfo->dobj.catId.tableoid, tbinfo->dobj.catId.oid);
- }
- else
- {
- /* Note: this will fail to find attribute comments in pre-7.2... */
- appendPQExpBuffer(query, "SELECT description, 0 as objsubid FROM pg_description WHERE objoid = '%u'::oid",
- tbinfo->dobj.catId.oid);
- }
-
- /* Execute query */
-
- res = PQexec(g_conn, query->data);
- check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
-
- i_description = PQfnumber(res, "description");
- i_objsubid = PQfnumber(res, "objsubid");
-
- /* If comments exist, build COMMENT ON statements */
-
- ntups = PQntuples(res);
- for (i = 0; i < ntups; i++)
+ while (ncomments > 0)
{
- const char *descr = PQgetvalue(res, i, i_description);
- int objsubid = atoi(PQgetvalue(res, i, i_objsubid));
+ const char *descr = comments->descr;
+ int objsubid = comments->objsubid;
if (objsubid == 0)
{
&(tbinfo->dobj.dumpId), 1,
NULL, NULL);
}
+
+ comments++;
+ ncomments--;
}
- PQclear(res);
destroyPQExpBuffer(query);
destroyPQExpBuffer(target);
}
+/*
+ * findComments --
+ *
+ * Find the comment(s), if any, associated with the given object. All the
+ * objsubid values associated with the given classoid/objoid are found with
+ * one search.
+ */
+static int
+findComments(Archive *fout, Oid classoid, Oid objoid,
+ CommentItem **items)
+{
+ /* static storage for table of comments */
+ static CommentItem *comments = NULL;
+ static int ncomments = -1;
+
+ CommentItem *middle = NULL;
+ CommentItem *low;
+ CommentItem *high;
+ int nmatch;
+
+ /* Get comments if we didn't already */
+ if (ncomments < 0)
+ ncomments = collectComments(fout, &comments);
+
+ /*
+ * Pre-7.2, pg_description does not contain classoid, so collectComments
+ * just stores a zero. If there's a collision on object OID, well, you
+ * get duplicate comments.
+ */
+ if (fout->remoteVersion < 70200)
+ classoid = 0;
+
+ /*
+ * Do binary search to find some item matching the object.
+ */
+ low = &comments[0];
+ high = &comments[ncomments-1];
+ while (low <= high)
+ {
+ middle = low + (high - low) / 2;
+
+ if (classoid < middle->classoid)
+ high = middle - 1;
+ else if (classoid > middle->classoid)
+ low = middle + 1;
+ else if (objoid < middle->objoid)
+ high = middle - 1;
+ else if (objoid > middle->objoid)
+ low = middle + 1;
+ else
+ break; /* found a match */
+ }
+
+ if (low > high) /* no matches */
+ {
+ *items = NULL;
+ return 0;
+ }
+
+ /*
+ * Now determine how many items match the object. The search loop
+ * invariant still holds: only items between low and high inclusive
+ * could match.
+ */
+ nmatch = 1;
+ while (middle > low)
+ {
+ if (classoid != middle[-1].classoid ||
+ objoid != middle[-1].objoid)
+ break;
+ middle--;
+ nmatch++;
+ }
+
+ *items = middle;
+
+ middle += nmatch;
+ while (middle <= high)
+ {
+ if (classoid != middle->classoid ||
+ objoid != middle->objoid)
+ break;
+ middle++;
+ nmatch++;
+ }
+
+ return nmatch;
+}
+
+/*
+ * collectComments --
+ *
+ * Construct a table of all comments available for database objects.
+ * We used to do per-object queries for the comments, but it's much faster
+ * to pull them all over at once, and on most databases the memory cost
+ * isn't high.
+ *
+ * The table is sorted by classoid/objid/objsubid for speed in lookup.
+ */
+static int
+collectComments(Archive *fout, CommentItem **items)
+{
+ PGresult *res;
+ PQExpBuffer query;
+ int i_description;
+ int i_classoid;
+ int i_objoid;
+ int i_objsubid;
+ int ntups;
+ int i;
+ CommentItem *comments;
+
+ /*
+ * Note we do NOT change source schema here; preserve the caller's
+ * setting, instead.
+ */
+
+ query = createPQExpBuffer();
+
+ if (fout->remoteVersion >= 70300)
+ {
+ appendPQExpBuffer(query, "SELECT description, classoid, objoid, objsubid "
+ "FROM pg_catalog.pg_description "
+ "ORDER BY classoid, objoid, objsubid");
+ }
+ else if (fout->remoteVersion >= 70200)
+ {
+ appendPQExpBuffer(query, "SELECT description, classoid, objoid, objsubid "
+ "FROM pg_description "
+ "ORDER BY classoid, objoid, objsubid");
+ }
+ else
+ {
+ /* Note: this will fail to find attribute comments in pre-7.2... */
+ appendPQExpBuffer(query, "SELECT description, 0 as classoid, objoid, 0 as objsubid "
+ "FROM pg_description "
+ "ORDER BY objoid");
+ }
+
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
+ /* Construct lookup table containing OIDs in numeric form */
+
+ i_description = PQfnumber(res, "description");
+ i_classoid = PQfnumber(res, "classoid");
+ i_objoid = PQfnumber(res, "objoid");
+ i_objsubid = PQfnumber(res, "objsubid");
+
+ ntups = PQntuples(res);
+
+ comments = (CommentItem *) malloc(ntups * sizeof(CommentItem));
+
+ for (i = 0; i < ntups; i++)
+ {
+ comments[i].descr = PQgetvalue(res, i, i_description);
+ comments[i].classoid = atooid(PQgetvalue(res, i, i_classoid));
+ comments[i].objoid = atooid(PQgetvalue(res, i, i_objoid));
+ comments[i].objsubid = atoi(PQgetvalue(res, i, i_objsubid));
+ }
+
+ /* Do NOT free the PGresult since we are keeping pointers into it */
+ destroyPQExpBuffer(query);
+
+ *items = comments;
+ return ntups;
+}
+
/*
* dumpDumpableObject
*