Skip to content

Commit 6687650

Browse files
committed
COPY and pg_dump failed to cope with zero-column tables. Fix 'em.
1 parent db7e46a commit 6687650

File tree

2 files changed

+75
-53
lines changed

2 files changed

+75
-53
lines changed

src/backend/commands/copy.c

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.196 2003/04/24 21:16:42 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.197 2003/04/25 02:28:22 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -808,10 +808,11 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
808808
* Get info about the columns we need to process.
809809
*
810810
* For binary copy we really only need isvarlena, but compute it all...
811+
* +1's here are to avoid palloc(0) in a zero-column table.
811812
*/
812-
out_functions = (FmgrInfo *) palloc(num_phys_attrs * sizeof(FmgrInfo));
813-
elements = (Oid *) palloc(num_phys_attrs * sizeof(Oid));
814-
isvarlena = (bool *) palloc(num_phys_attrs * sizeof(bool));
813+
out_functions = (FmgrInfo *) palloc((num_phys_attrs + 1) * sizeof(FmgrInfo));
814+
elements = (Oid *) palloc((num_phys_attrs + 1) * sizeof(Oid));
815+
isvarlena = (bool *) palloc((num_phys_attrs + 1) * sizeof(bool));
815816
foreach(cur, attnumlist)
816817
{
817818
int attnum = lfirsti(cur);
@@ -1078,12 +1079,13 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
10781079
* relation, including the input function, the element type (to pass
10791080
* to the input function), and info about defaults and constraints.
10801081
* (We don't actually use the input function if it's a binary copy.)
1082+
* +1's here are to avoid palloc(0) in a zero-column table.
10811083
*/
1082-
in_functions = (FmgrInfo *) palloc(num_phys_attrs * sizeof(FmgrInfo));
1083-
elements = (Oid *) palloc(num_phys_attrs * sizeof(Oid));
1084-
defmap = (int *) palloc(num_phys_attrs * sizeof(int));
1085-
defexprs = (ExprState **) palloc(num_phys_attrs * sizeof(ExprState *));
1086-
constraintexprs = (ExprState **) palloc0(num_phys_attrs * sizeof(ExprState *));
1084+
in_functions = (FmgrInfo *) palloc((num_phys_attrs + 1) * sizeof(FmgrInfo));
1085+
elements = (Oid *) palloc((num_phys_attrs + 1) * sizeof(Oid));
1086+
defmap = (int *) palloc((num_phys_attrs + 1) * sizeof(int));
1087+
defexprs = (ExprState **) palloc((num_phys_attrs + 1) * sizeof(ExprState *));
1088+
constraintexprs = (ExprState **) palloc0((num_phys_attrs + 1) * sizeof(ExprState *));
10871089

10881090
for (i = 0; i < num_phys_attrs; i++)
10891091
{
@@ -1196,8 +1198,8 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
11961198
}
11971199
}
11981200

1199-
values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
1200-
nulls = (char *) palloc(num_phys_attrs * sizeof(char));
1201+
values = (Datum *) palloc((num_phys_attrs + 1) * sizeof(Datum));
1202+
nulls = (char *) palloc((num_phys_attrs + 1) * sizeof(char));
12011203

12021204
/* Make room for a PARAM_EXEC value for domain constraint checks */
12031205
if (hasConstraints)
@@ -1304,9 +1306,31 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
13041306
if (done)
13051307
break; /* out of per-row loop */
13061308

1307-
/* Complain if there are more fields on the input line */
1309+
/*
1310+
* Complain if there are more fields on the input line.
1311+
*
1312+
* Special case: if we're reading a zero-column table, we
1313+
* won't yet have called CopyReadAttribute() at all; so do that
1314+
* and check we have an empty line. Fortunately we can keep that
1315+
* silly corner case out of the main line of execution.
1316+
*/
13081317
if (result == NORMAL_ATTR)
1309-
elog(ERROR, "Extra data after last expected column");
1318+
{
1319+
if (attnumlist == NIL && !file_has_oids)
1320+
{
1321+
string = CopyReadAttribute(delim, &result);
1322+
if (result == NORMAL_ATTR || *string != '\0')
1323+
elog(ERROR, "Extra data after last expected column");
1324+
if (result == END_OF_FILE)
1325+
{
1326+
/* EOF at start of line: all is well */
1327+
done = true;
1328+
break;
1329+
}
1330+
}
1331+
else
1332+
elog(ERROR, "Extra data after last expected column");
1333+
}
13101334

13111335
/*
13121336
* If we got some data on the line, but it was ended by EOF,

src/bin/pg_dump/pg_dump.c

Lines changed: 38 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
* by PostgreSQL
1313
*
1414
* IDENTIFICATION
15-
* $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.326 2003/04/04 20:42:12 momjian Exp $
15+
* $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.327 2003/04/25 02:28:22 tgl Exp $
1616
*
1717
*-------------------------------------------------------------------------
1818
*/
@@ -838,9 +838,6 @@ dumpClasses_nodumpData(Archive *fout, char *oid, void *dctxv)
838838
* possibility of retrieving data in the wrong column order. (The
839839
* default column ordering of COPY will not be what we want in certain
840840
* corner cases involving ADD COLUMN and inheritance.)
841-
*
842-
* NB: caller should have already determined that there are dumpable
843-
* columns, so that fmtCopyColumnList will return something.
844841
*/
845842
if (g_fout->remoteVersion >= 70300)
846843
column_list = fmtCopyColumnList(tbinfo);
@@ -975,6 +972,7 @@ dumpClasses_dumpData(Archive *fout, char *oid, void *dctxv)
975972
PQExpBuffer q = createPQExpBuffer();
976973
PGresult *res;
977974
int tuple;
975+
int nfields;
978976
int field;
979977

980978
/*
@@ -1024,14 +1022,21 @@ dumpClasses_dumpData(Archive *fout, char *oid, void *dctxv)
10241022
exit_nicely();
10251023
}
10261024

1025+
nfields = PQnfields(res);
10271026
for (tuple = 0; tuple < PQntuples(res); tuple++)
10281027
{
10291028
archprintf(fout, "INSERT INTO %s ", fmtId(classname));
1029+
if (nfields == 0)
1030+
{
1031+
/* corner case for zero-column table */
1032+
archprintf(fout, "DEFAULT VALUES;\n");
1033+
continue;
1034+
}
10301035
if (attrNames == true)
10311036
{
10321037
resetPQExpBuffer(q);
10331038
appendPQExpBuffer(q, "(");
1034-
for (field = 0; field < PQnfields(res); field++)
1039+
for (field = 0; field < nfields; field++)
10351040
{
10361041
if (field > 0)
10371042
appendPQExpBuffer(q, ", ");
@@ -1041,7 +1046,7 @@ dumpClasses_dumpData(Archive *fout, char *oid, void *dctxv)
10411046
archprintf(fout, "%s", q->data);
10421047
}
10431048
archprintf(fout, "VALUES (");
1044-
for (field = 0; field < PQnfields(res); field++)
1049+
for (field = 0; field < nfields; field++)
10451050
{
10461051
if (field > 0)
10471052
archprintf(fout, ", ");
@@ -1154,45 +1159,38 @@ dumpClasses(const TableInfo *tblinfo, const int numTables, Archive *fout,
11541159

11551160
if (tblinfo[i].dump)
11561161
{
1157-
const char *column_list;
1158-
11591162
if (g_verbose)
11601163
write_msg(NULL, "preparing to dump the contents of table %s\n",
11611164
classname);
11621165

1163-
/* Get column list first to check for zero-column table */
1164-
column_list = fmtCopyColumnList(&(tblinfo[i]));
1165-
if (column_list)
1166-
{
1167-
dumpCtx = (DumpContext *) malloc(sizeof(DumpContext));
1168-
dumpCtx->tblinfo = (TableInfo *) tblinfo;
1169-
dumpCtx->tblidx = i;
1170-
dumpCtx->oids = oids;
1166+
dumpCtx = (DumpContext *) malloc(sizeof(DumpContext));
1167+
dumpCtx->tblinfo = (TableInfo *) tblinfo;
1168+
dumpCtx->tblidx = i;
1169+
dumpCtx->oids = oids;
11711170

1172-
if (!dumpData)
1173-
{
1174-
/* Dump/restore using COPY */
1175-
dumpFn = dumpClasses_nodumpData;
1176-
resetPQExpBuffer(copyBuf);
1177-
appendPQExpBuffer(copyBuf, "COPY %s %s %sFROM stdin;\n",
1178-
fmtId(tblinfo[i].relname),
1179-
column_list,
1171+
if (!dumpData)
1172+
{
1173+
/* Dump/restore using COPY */
1174+
dumpFn = dumpClasses_nodumpData;
1175+
resetPQExpBuffer(copyBuf);
1176+
appendPQExpBuffer(copyBuf, "COPY %s %s %sFROM stdin;\n",
1177+
fmtId(tblinfo[i].relname),
1178+
fmtCopyColumnList(&(tblinfo[i])),
11801179
(oids && tblinfo[i].hasoids) ? "WITH OIDS " : "");
1181-
copyStmt = copyBuf->data;
1182-
}
1183-
else
1184-
{
1185-
/* Restore using INSERT */
1186-
dumpFn = dumpClasses_dumpData;
1187-
copyStmt = NULL;
1188-
}
1189-
1190-
ArchiveEntry(fout, tblinfo[i].oid, tblinfo[i].relname,
1191-
tblinfo[i].relnamespace->nspname,
1192-
tblinfo[i].usename,
1193-
"TABLE DATA", NULL, "", "", copyStmt,
1194-
dumpFn, dumpCtx);
1180+
copyStmt = copyBuf->data;
11951181
}
1182+
else
1183+
{
1184+
/* Restore using INSERT */
1185+
dumpFn = dumpClasses_dumpData;
1186+
copyStmt = NULL;
1187+
}
1188+
1189+
ArchiveEntry(fout, tblinfo[i].oid, tblinfo[i].relname,
1190+
tblinfo[i].relnamespace->nspname,
1191+
tblinfo[i].usename,
1192+
"TABLE DATA", NULL, "", "", copyStmt,
1193+
dumpFn, dumpCtx);
11961194
}
11971195
}
11981196

@@ -6980,7 +6978,7 @@ fmtQualifiedId(const char *schema, const char *id)
69806978
* Return a column list clause for the given relation.
69816979
*
69826980
* Special case: if there are no undropped columns in the relation, return
6983-
* NULL, not an invalid "()" column list.
6981+
* "", not an invalid "()" column list.
69846982
*/
69856983
static const char *
69866984
fmtCopyColumnList(const TableInfo *ti)
@@ -7010,7 +7008,7 @@ fmtCopyColumnList(const TableInfo *ti)
70107008
}
70117009

70127010
if (!needComma)
7013-
return NULL; /* no undropped columns */
7011+
return ""; /* no undropped columns */
70147012

70157013
appendPQExpBuffer(q, ")");
70167014
return q->data;

0 commit comments

Comments
 (0)