Skip to content

Commit 05649b8

Browse files
committed
Rewrite pg_dump's comment-dumping code to pull over all the comments
in one query, rather than making a separate query for each object that could have a comment. This costs relatively little space (a few tens of K typically) and saves substantial time in databases with many objects. I find it reduces the runtime of 'pg_dump -s regression' by about a third.
1 parent c8aaa5c commit 05649b8

File tree

1 file changed

+215
-93
lines changed

1 file changed

+215
-93
lines changed

src/bin/pg_dump/pg_dump.c

Lines changed: 215 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
* by PostgreSQL
1313
*
1414
* IDENTIFICATION
15-
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.367 2004/03/03 21:28:54 tgl Exp $
15+
* $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.368 2004/03/20 20:09:45 tgl Exp $
1616
*
1717
*-------------------------------------------------------------------------
1818
*/
@@ -67,6 +67,15 @@ extern int optind,
6767
opterr;
6868

6969

70+
typedef struct
71+
{
72+
const char *descr; /* comment for an object */
73+
Oid classoid; /* object class (catalog OID) */
74+
Oid objoid; /* object OID */
75+
int objsubid; /* subobject (table column #) */
76+
} CommentItem;
77+
78+
7079
/* global decls */
7180
bool g_verbose; /* User wants verbose narration of our
7281
* activities. */
@@ -105,6 +114,9 @@ static void dumpTableData(Archive *fout, TableDataInfo *tdinfo);
105114
static void dumpComment(Archive *fout, const char *target,
106115
const char *namespace, const char *owner,
107116
CatalogId catalogId, int subid, DumpId dumpId);
117+
static int findComments(Archive *fout, Oid classoid, Oid objoid,
118+
CommentItem **items);
119+
static int collectComments(Archive *fout, CommentItem **items);
108120
static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
109121
static void dumpNamespace(Archive *fout, NamespaceInfo *nspinfo);
110122
static void dumpType(Archive *fout, TypeInfo *tinfo);
@@ -3880,141 +3892,81 @@ dumpComment(Archive *fout, const char *target,
38803892
const char *namespace, const char *owner,
38813893
CatalogId catalogId, int subid, DumpId dumpId)
38823894
{
3883-
PGresult *res;
3884-
PQExpBuffer query;
3885-
int i_description;
3895+
CommentItem *comments;
3896+
int ncomments;
38863897

38873898
/* Comments are SCHEMA not data */
38883899
if (dataOnly)
38893900
return;
38903901

3891-
/*
3892-
* Note we do NOT change source schema here; preserve the caller's
3893-
* setting, instead.
3894-
*/
3902+
/* Search for comments associated with catalogId, using table */
3903+
ncomments = findComments(fout, catalogId.tableoid, catalogId.oid,
3904+
&comments);
38953905

3896-
/* Build query to find comment */
3897-
3898-
query = createPQExpBuffer();
3899-
3900-
if (fout->remoteVersion >= 70300)
3901-
{
3902-
appendPQExpBuffer(query,
3903-
"SELECT description FROM pg_catalog.pg_description "
3904-
"WHERE classoid = '%u'::pg_catalog.oid and "
3905-
"objoid = '%u'::pg_catalog.oid and objsubid = %d",
3906-
catalogId.tableoid, catalogId.oid, subid);
3907-
}
3908-
else if (fout->remoteVersion >= 70200)
3909-
{
3910-
appendPQExpBuffer(query,
3911-
"SELECT description FROM pg_description "
3912-
"WHERE classoid = '%u'::oid and "
3913-
"objoid = '%u'::oid and objsubid = %d",
3914-
catalogId.tableoid, catalogId.oid, subid);
3915-
}
3916-
else
3906+
/* Is there one matching the subid? */
3907+
while (ncomments > 0)
39173908
{
3918-
/* Note: this will fail to find attribute comments in pre-7.2... */
3919-
appendPQExpBuffer(query, "SELECT description FROM pg_description WHERE objoid = '%u'::oid", catalogId.oid);
3909+
if (comments->objsubid == subid)
3910+
break;
3911+
comments++;
3912+
ncomments--;
39203913
}
39213914

3922-
/* Execute query */
3923-
3924-
res = PQexec(g_conn, query->data);
3925-
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
3926-
39273915
/* If a comment exists, build COMMENT ON statement */
3928-
3929-
if (PQntuples(res) == 1)
3916+
if (ncomments > 0)
39303917
{
3931-
i_description = PQfnumber(res, "description");
3932-
resetPQExpBuffer(query);
3918+
PQExpBuffer query = createPQExpBuffer();
3919+
39333920
appendPQExpBuffer(query, "COMMENT ON %s IS ", target);
3934-
appendStringLiteral(query, PQgetvalue(res, 0, i_description), false);
3921+
appendStringLiteral(query, comments->descr, false);
39353922
appendPQExpBuffer(query, ";\n");
39363923

39373924
ArchiveEntry(fout, nilCatalogId, createDumpId(),
39383925
target, namespace, owner,
39393926
"COMMENT", query->data, "", NULL,
39403927
&(dumpId), 1,
39413928
NULL, NULL);
3942-
}
39433929

3944-
PQclear(res);
3945-
destroyPQExpBuffer(query);
3930+
destroyPQExpBuffer(query);
3931+
}
39463932
}
39473933

39483934
/*
39493935
* dumpTableComment --
39503936
*
39513937
* As above, but dump comments for both the specified table (or view)
3952-
* and its columns. For speed, we want to do this with only one query.
3938+
* and its columns.
39533939
*/
39543940
static void
39553941
dumpTableComment(Archive *fout, TableInfo *tbinfo,
39563942
const char *reltypename)
39573943
{
3958-
PGresult *res;
3944+
CommentItem *comments;
3945+
int ncomments;
39593946
PQExpBuffer query;
39603947
PQExpBuffer target;
3961-
int i_description;
3962-
int i_objsubid;
3963-
int ntups;
3964-
int i;
39653948

39663949
/* Comments are SCHEMA not data */
39673950
if (dataOnly)
39683951
return;
39693952

3970-
/*
3971-
* Note we do NOT change source schema here; preserve the caller's
3972-
* setting, instead.
3973-
*/
3953+
/* Search for comments associated with relation, using table */
3954+
ncomments = findComments(fout,
3955+
tbinfo->dobj.catId.tableoid,
3956+
tbinfo->dobj.catId.oid,
3957+
&comments);
39743958

3975-
/* Build query to find comments */
3959+
/* If comments exist, build COMMENT ON statements */
3960+
if (ncomments <= 0)
3961+
return;
39763962

39773963
query = createPQExpBuffer();
39783964
target = createPQExpBuffer();
39793965

3980-
if (fout->remoteVersion >= 70300)
3981-
{
3982-
appendPQExpBuffer(query, "SELECT description, objsubid FROM pg_catalog.pg_description "
3983-
"WHERE classoid = '%u'::pg_catalog.oid and "
3984-
"objoid = '%u'::pg_catalog.oid "
3985-
"ORDER BY objoid, classoid, objsubid",
3986-
tbinfo->dobj.catId.tableoid, tbinfo->dobj.catId.oid);
3987-
}
3988-
else if (fout->remoteVersion >= 70200)
3989-
{
3990-
appendPQExpBuffer(query, "SELECT description, objsubid FROM pg_description "
3991-
"WHERE classoid = '%u'::oid and "
3992-
"objoid = '%u'::oid "
3993-
"ORDER BY objoid, classoid, objsubid",
3994-
tbinfo->dobj.catId.tableoid, tbinfo->dobj.catId.oid);
3995-
}
3996-
else
3997-
{
3998-
/* Note: this will fail to find attribute comments in pre-7.2... */
3999-
appendPQExpBuffer(query, "SELECT description, 0 as objsubid FROM pg_description WHERE objoid = '%u'::oid",
4000-
tbinfo->dobj.catId.oid);
4001-
}
4002-
4003-
/* Execute query */
4004-
4005-
res = PQexec(g_conn, query->data);
4006-
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
4007-
4008-
i_description = PQfnumber(res, "description");
4009-
i_objsubid = PQfnumber(res, "objsubid");
4010-
4011-
/* If comments exist, build COMMENT ON statements */
4012-
4013-
ntups = PQntuples(res);
4014-
for (i = 0; i < ntups; i++)
3966+
while (ncomments > 0)
40153967
{
4016-
const char *descr = PQgetvalue(res, i, i_description);
4017-
int objsubid = atoi(PQgetvalue(res, i, i_objsubid));
3968+
const char *descr = comments->descr;
3969+
int objsubid = comments->objsubid;
40183970

40193971
if (objsubid == 0)
40203972
{
@@ -4054,13 +4006,183 @@ dumpTableComment(Archive *fout, TableInfo *tbinfo,
40544006
&(tbinfo->dobj.dumpId), 1,
40554007
NULL, NULL);
40564008
}
4009+
4010+
comments++;
4011+
ncomments--;
40574012
}
40584013

4059-
PQclear(res);
40604014
destroyPQExpBuffer(query);
40614015
destroyPQExpBuffer(target);
40624016
}
40634017

4018+
/*
4019+
* findComments --
4020+
*
4021+
* Find the comment(s), if any, associated with the given object. All the
4022+
* objsubid values associated with the given classoid/objoid are found with
4023+
* one search.
4024+
*/
4025+
static int
4026+
findComments(Archive *fout, Oid classoid, Oid objoid,
4027+
CommentItem **items)
4028+
{
4029+
/* static storage for table of comments */
4030+
static CommentItem *comments = NULL;
4031+
static int ncomments = -1;
4032+
4033+
CommentItem *middle = NULL;
4034+
CommentItem *low;
4035+
CommentItem *high;
4036+
int nmatch;
4037+
4038+
/* Get comments if we didn't already */
4039+
if (ncomments < 0)
4040+
ncomments = collectComments(fout, &comments);
4041+
4042+
/*
4043+
* Pre-7.2, pg_description does not contain classoid, so collectComments
4044+
* just stores a zero. If there's a collision on object OID, well, you
4045+
* get duplicate comments.
4046+
*/
4047+
if (fout->remoteVersion < 70200)
4048+
classoid = 0;
4049+
4050+
/*
4051+
* Do binary search to find some item matching the object.
4052+
*/
4053+
low = &comments[0];
4054+
high = &comments[ncomments-1];
4055+
while (low <= high)
4056+
{
4057+
middle = low + (high - low) / 2;
4058+
4059+
if (classoid < middle->classoid)
4060+
high = middle - 1;
4061+
else if (classoid > middle->classoid)
4062+
low = middle + 1;
4063+
else if (objoid < middle->objoid)
4064+
high = middle - 1;
4065+
else if (objoid > middle->objoid)
4066+
low = middle + 1;
4067+
else
4068+
break; /* found a match */
4069+
}
4070+
4071+
if (low > high) /* no matches */
4072+
{
4073+
*items = NULL;
4074+
return 0;
4075+
}
4076+
4077+
/*
4078+
* Now determine how many items match the object. The search loop
4079+
* invariant still holds: only items between low and high inclusive
4080+
* could match.
4081+
*/
4082+
nmatch = 1;
4083+
while (middle > low)
4084+
{
4085+
if (classoid != middle[-1].classoid ||
4086+
objoid != middle[-1].objoid)
4087+
break;
4088+
middle--;
4089+
nmatch++;
4090+
}
4091+
4092+
*items = middle;
4093+
4094+
middle += nmatch;
4095+
while (middle <= high)
4096+
{
4097+
if (classoid != middle->classoid ||
4098+
objoid != middle->objoid)
4099+
break;
4100+
middle++;
4101+
nmatch++;
4102+
}
4103+
4104+
return nmatch;
4105+
}
4106+
4107+
/*
4108+
* collectComments --
4109+
*
4110+
* Construct a table of all comments available for database objects.
4111+
* We used to do per-object queries for the comments, but it's much faster
4112+
* to pull them all over at once, and on most databases the memory cost
4113+
* isn't high.
4114+
*
4115+
* The table is sorted by classoid/objid/objsubid for speed in lookup.
4116+
*/
4117+
static int
4118+
collectComments(Archive *fout, CommentItem **items)
4119+
{
4120+
PGresult *res;
4121+
PQExpBuffer query;
4122+
int i_description;
4123+
int i_classoid;
4124+
int i_objoid;
4125+
int i_objsubid;
4126+
int ntups;
4127+
int i;
4128+
CommentItem *comments;
4129+
4130+
/*
4131+
* Note we do NOT change source schema here; preserve the caller's
4132+
* setting, instead.
4133+
*/
4134+
4135+
query = createPQExpBuffer();
4136+
4137+
if (fout->remoteVersion >= 70300)
4138+
{
4139+
appendPQExpBuffer(query, "SELECT description, classoid, objoid, objsubid "
4140+
"FROM pg_catalog.pg_description "
4141+
"ORDER BY classoid, objoid, objsubid");
4142+
}
4143+
else if (fout->remoteVersion >= 70200)
4144+
{
4145+
appendPQExpBuffer(query, "SELECT description, classoid, objoid, objsubid "
4146+
"FROM pg_description "
4147+
"ORDER BY classoid, objoid, objsubid");
4148+
}
4149+
else
4150+
{
4151+
/* Note: this will fail to find attribute comments in pre-7.2... */
4152+
appendPQExpBuffer(query, "SELECT description, 0 as classoid, objoid, 0 as objsubid "
4153+
"FROM pg_description "
4154+
"ORDER BY objoid");
4155+
}
4156+
4157+
res = PQexec(g_conn, query->data);
4158+
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
4159+
4160+
/* Construct lookup table containing OIDs in numeric form */
4161+
4162+
i_description = PQfnumber(res, "description");
4163+
i_classoid = PQfnumber(res, "classoid");
4164+
i_objoid = PQfnumber(res, "objoid");
4165+
i_objsubid = PQfnumber(res, "objsubid");
4166+
4167+
ntups = PQntuples(res);
4168+
4169+
comments = (CommentItem *) malloc(ntups * sizeof(CommentItem));
4170+
4171+
for (i = 0; i < ntups; i++)
4172+
{
4173+
comments[i].descr = PQgetvalue(res, i, i_description);
4174+
comments[i].classoid = atooid(PQgetvalue(res, i, i_classoid));
4175+
comments[i].objoid = atooid(PQgetvalue(res, i, i_objoid));
4176+
comments[i].objsubid = atoi(PQgetvalue(res, i, i_objsubid));
4177+
}
4178+
4179+
/* Do NOT free the PGresult since we are keeping pointers into it */
4180+
destroyPQExpBuffer(query);
4181+
4182+
*items = comments;
4183+
return ntups;
4184+
}
4185+
40644186
/*
40654187
* dumpDumpableObject
40664188
*

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy