From 6687650ce6c1a1c688db96df82e8c9173ccbbc57 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Fri, 25 Apr 2003 02:28:22 +0000 Subject: [PATCH] COPY and pg_dump failed to cope with zero-column tables. Fix 'em. --- src/backend/commands/copy.c | 50 +++++++++++++++++------- src/bin/pg_dump/pg_dump.c | 78 ++++++++++++++++++------------------- 2 files changed, 75 insertions(+), 53 deletions(-) diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 7cb530a3cd..ec6e771aed 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.196 2003/04/24 21:16:42 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.197 2003/04/25 02:28:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -808,10 +808,11 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids, * Get info about the columns we need to process. * * For binary copy we really only need isvarlena, but compute it all... + * +1's here are to avoid palloc(0) in a zero-column table. */ - out_functions = (FmgrInfo *) palloc(num_phys_attrs * sizeof(FmgrInfo)); - elements = (Oid *) palloc(num_phys_attrs * sizeof(Oid)); - isvarlena = (bool *) palloc(num_phys_attrs * sizeof(bool)); + out_functions = (FmgrInfo *) palloc((num_phys_attrs + 1) * sizeof(FmgrInfo)); + elements = (Oid *) palloc((num_phys_attrs + 1) * sizeof(Oid)); + isvarlena = (bool *) palloc((num_phys_attrs + 1) * sizeof(bool)); foreach(cur, attnumlist) { int attnum = lfirsti(cur); @@ -1078,12 +1079,13 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids, * relation, including the input function, the element type (to pass * to the input function), and info about defaults and constraints. * (We don't actually use the input function if it's a binary copy.) + * +1's here are to avoid palloc(0) in a zero-column table. */ - in_functions = (FmgrInfo *) palloc(num_phys_attrs * sizeof(FmgrInfo)); - elements = (Oid *) palloc(num_phys_attrs * sizeof(Oid)); - defmap = (int *) palloc(num_phys_attrs * sizeof(int)); - defexprs = (ExprState **) palloc(num_phys_attrs * sizeof(ExprState *)); - constraintexprs = (ExprState **) palloc0(num_phys_attrs * sizeof(ExprState *)); + in_functions = (FmgrInfo *) palloc((num_phys_attrs + 1) * sizeof(FmgrInfo)); + elements = (Oid *) palloc((num_phys_attrs + 1) * sizeof(Oid)); + defmap = (int *) palloc((num_phys_attrs + 1) * sizeof(int)); + defexprs = (ExprState **) palloc((num_phys_attrs + 1) * sizeof(ExprState *)); + constraintexprs = (ExprState **) palloc0((num_phys_attrs + 1) * sizeof(ExprState *)); for (i = 0; i < num_phys_attrs; i++) { @@ -1196,8 +1198,8 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids, } } - values = (Datum *) palloc(num_phys_attrs * sizeof(Datum)); - nulls = (char *) palloc(num_phys_attrs * sizeof(char)); + values = (Datum *) palloc((num_phys_attrs + 1) * sizeof(Datum)); + nulls = (char *) palloc((num_phys_attrs + 1) * sizeof(char)); /* Make room for a PARAM_EXEC value for domain constraint checks */ if (hasConstraints) @@ -1304,9 +1306,31 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids, if (done) break; /* out of per-row loop */ - /* Complain if there are more fields on the input line */ + /* + * Complain if there are more fields on the input line. + * + * Special case: if we're reading a zero-column table, we + * won't yet have called CopyReadAttribute() at all; so do that + * and check we have an empty line. Fortunately we can keep that + * silly corner case out of the main line of execution. + */ if (result == NORMAL_ATTR) - elog(ERROR, "Extra data after last expected column"); + { + if (attnumlist == NIL && !file_has_oids) + { + string = CopyReadAttribute(delim, &result); + if (result == NORMAL_ATTR || *string != '\0') + elog(ERROR, "Extra data after last expected column"); + if (result == END_OF_FILE) + { + /* EOF at start of line: all is well */ + done = true; + break; + } + } + else + elog(ERROR, "Extra data after last expected column"); + } /* * If we got some data on the line, but it was ended by EOF, diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 409a961701..84cf3950ec 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -12,7 +12,7 @@ * by PostgreSQL * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.326 2003/04/04 20:42:12 momjian Exp $ + * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.327 2003/04/25 02:28:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -838,9 +838,6 @@ dumpClasses_nodumpData(Archive *fout, char *oid, void *dctxv) * possibility of retrieving data in the wrong column order. (The * default column ordering of COPY will not be what we want in certain * corner cases involving ADD COLUMN and inheritance.) - * - * NB: caller should have already determined that there are dumpable - * columns, so that fmtCopyColumnList will return something. */ if (g_fout->remoteVersion >= 70300) column_list = fmtCopyColumnList(tbinfo); @@ -975,6 +972,7 @@ dumpClasses_dumpData(Archive *fout, char *oid, void *dctxv) PQExpBuffer q = createPQExpBuffer(); PGresult *res; int tuple; + int nfields; int field; /* @@ -1024,14 +1022,21 @@ dumpClasses_dumpData(Archive *fout, char *oid, void *dctxv) exit_nicely(); } + nfields = PQnfields(res); for (tuple = 0; tuple < PQntuples(res); tuple++) { archprintf(fout, "INSERT INTO %s ", fmtId(classname)); + if (nfields == 0) + { + /* corner case for zero-column table */ + archprintf(fout, "DEFAULT VALUES;\n"); + continue; + } if (attrNames == true) { resetPQExpBuffer(q); appendPQExpBuffer(q, "("); - for (field = 0; field < PQnfields(res); field++) + for (field = 0; field < nfields; field++) { if (field > 0) appendPQExpBuffer(q, ", "); @@ -1041,7 +1046,7 @@ dumpClasses_dumpData(Archive *fout, char *oid, void *dctxv) archprintf(fout, "%s", q->data); } archprintf(fout, "VALUES ("); - for (field = 0; field < PQnfields(res); field++) + for (field = 0; field < nfields; field++) { if (field > 0) archprintf(fout, ", "); @@ -1154,45 +1159,38 @@ dumpClasses(const TableInfo *tblinfo, const int numTables, Archive *fout, if (tblinfo[i].dump) { - const char *column_list; - if (g_verbose) write_msg(NULL, "preparing to dump the contents of table %s\n", classname); - /* Get column list first to check for zero-column table */ - column_list = fmtCopyColumnList(&(tblinfo[i])); - if (column_list) + dumpCtx = (DumpContext *) malloc(sizeof(DumpContext)); + dumpCtx->tblinfo = (TableInfo *) tblinfo; + dumpCtx->tblidx = i; + dumpCtx->oids = oids; + + if (!dumpData) { - dumpCtx = (DumpContext *) malloc(sizeof(DumpContext)); - dumpCtx->tblinfo = (TableInfo *) tblinfo; - dumpCtx->tblidx = i; - dumpCtx->oids = oids; - - if (!dumpData) - { - /* Dump/restore using COPY */ - dumpFn = dumpClasses_nodumpData; - resetPQExpBuffer(copyBuf); - appendPQExpBuffer(copyBuf, "COPY %s %s %sFROM stdin;\n", - fmtId(tblinfo[i].relname), - column_list, + /* Dump/restore using COPY */ + dumpFn = dumpClasses_nodumpData; + resetPQExpBuffer(copyBuf); + appendPQExpBuffer(copyBuf, "COPY %s %s %sFROM stdin;\n", + fmtId(tblinfo[i].relname), + fmtCopyColumnList(&(tblinfo[i])), (oids && tblinfo[i].hasoids) ? "WITH OIDS " : ""); - copyStmt = copyBuf->data; - } - else - { - /* Restore using INSERT */ - dumpFn = dumpClasses_dumpData; - copyStmt = NULL; - } - - ArchiveEntry(fout, tblinfo[i].oid, tblinfo[i].relname, - tblinfo[i].relnamespace->nspname, - tblinfo[i].usename, - "TABLE DATA", NULL, "", "", copyStmt, - dumpFn, dumpCtx); + copyStmt = copyBuf->data; } + else + { + /* Restore using INSERT */ + dumpFn = dumpClasses_dumpData; + copyStmt = NULL; + } + + ArchiveEntry(fout, tblinfo[i].oid, tblinfo[i].relname, + tblinfo[i].relnamespace->nspname, + tblinfo[i].usename, + "TABLE DATA", NULL, "", "", copyStmt, + dumpFn, dumpCtx); } } @@ -6980,7 +6978,7 @@ fmtQualifiedId(const char *schema, const char *id) * Return a column list clause for the given relation. * * Special case: if there are no undropped columns in the relation, return - * NULL, not an invalid "()" column list. + * "", not an invalid "()" column list. */ static const char * fmtCopyColumnList(const TableInfo *ti) @@ -7010,7 +7008,7 @@ fmtCopyColumnList(const TableInfo *ti) } if (!needComma) - return NULL; /* no undropped columns */ + return ""; /* no undropped columns */ appendPQExpBuffer(q, ")"); return q->data;