COPY and pg_dump failed to cope with zero-column tables. Fix 'em.
authorTom Lane
Fri, 25 Apr 2003 02:28:22 +0000 (02:28 +0000)
committerTom Lane
Fri, 25 Apr 2003 02:28:22 +0000 (02:28 +0000)
src/backend/commands/copy.c
src/bin/pg_dump/pg_dump.c

index 7cb530a3cda5239470aa3a479aa3b6ddf4961b12..ec6e771aedcaba3f81655d96c545115d1806c40b 100644 (file)
@@ -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,
index 409a961701a1605920f9022033462c5a150d1563..84cf3950ec1004d4e727750001f0e34a8756688c 100644 (file)
@@ -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;
+           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,
+           if (!dumpData)
+           {
+               /* 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;