/* flagInhAttrs -
* for each dumpable table in tblinfo, flag its inherited attributes
- * so when we dump the table out, we don't dump out the inherited attributes
+ *
+ * What we need to do here is detect child columns that inherit NOT NULL
+ * bits from their parents (so that we needn't specify that again for the
+ * child) and child columns that have DEFAULT NULL when their parents had
+ * some non-null default. In the latter case, we make up a dummy AttrDefInfo
+ * object so that we'll correctly emit the necessary DEFAULT NULL clause;
+ * otherwise the backend will apply an inherited default to the column.
*
* modifies tblinfo
*/
TableInfo *tbinfo = &(tblinfo[i]);
int numParents;
TableInfo **parents;
- TableInfo *parent;
/* Sequences and views never have parents */
if (tbinfo->relkind == RELKIND_SEQUENCE ||
if (numParents == 0)
continue; /* nothing to see here, move along */
- /*----------------------------------------------------------------
- * For each attr, check the parent info: if no parent has an attr
- * with the same name, then it's not inherited. If there *is* an
- * attr with the same name, then only dump it if:
- *
- * - it is NOT NULL and zero parents are NOT NULL
- * OR
- * - it has a default value AND the default value does not match
- * all parent default values, or no parents specify a default.
- *
- * See discussion on -hackers around 2-Apr-2001.
- *----------------------------------------------------------------
- */
+ /* For each column, search for matching column names in parent(s) */
for (j = 0; j < tbinfo->numatts; j++)
{
- bool foundAttr; /* Attr was found in a parent */
bool foundNotNull; /* Attr was NOT NULL in a parent */
- bool defaultsMatch; /* All non-empty defaults match */
- bool defaultsFound; /* Found a default in a parent */
- AttrDefInfo *attrDef;
-
- foundAttr = false;
- foundNotNull = false;
- defaultsMatch = true;
- defaultsFound = false;
+ bool foundDefault; /* Found a default in a parent */
- attrDef = tbinfo->attrdefs[j];
+ /* no point in examining dropped columns */
+ if (tbinfo->attisdropped[j])
+ continue;
+ foundNotNull = false;
+ foundDefault = false;
for (k = 0; k < numParents; k++)
{
+ TableInfo *parent = parents[k];
int inhAttrInd;
- parent = parents[k];
inhAttrInd = strInArray(tbinfo->attnames[j],
parent->attnames,
parent->numatts);
-
- if (inhAttrInd != -1)
+ if (inhAttrInd >= 0)
{
- AttrDefInfo *inhDef = parent->attrdefs[inhAttrInd];
-
- foundAttr = true;
foundNotNull |= parent->notnull[inhAttrInd];
- if (inhDef != NULL)
- {
- defaultsFound = true;
-
- /*
- * If any parent has a default and the child doesn't,
- * we have to emit an explicit DEFAULT NULL clause for
- * the child, else the parent's default will win.
- */
- if (attrDef == NULL)
- {
- attrDef = (AttrDefInfo *) malloc(sizeof(AttrDefInfo));
- attrDef->dobj.objType = DO_ATTRDEF;
- attrDef->dobj.catId.tableoid = 0;
- attrDef->dobj.catId.oid = 0;
- AssignDumpId(&attrDef->dobj);
- attrDef->adtable = tbinfo;
- attrDef->adnum = j + 1;
- attrDef->adef_expr = strdup("NULL");
-
- attrDef->dobj.name = strdup(tbinfo->dobj.name);
- attrDef->dobj.namespace = tbinfo->dobj.namespace;
-
- attrDef->dobj.dump = tbinfo->dobj.dump;
-
- attrDef->separate = false;
- addObjectDependency(&tbinfo->dobj,
- attrDef->dobj.dumpId);
-
- tbinfo->attrdefs[j] = attrDef;
- }
- if (strcmp(attrDef->adef_expr, inhDef->adef_expr) != 0)
- {
- defaultsMatch = false;
-
- /*
- * Whenever there is a non-matching parent
- * default, add a dependency to force the parent
- * default to be dumped first, in case the
- * defaults end up being dumped as separate
- * commands. Otherwise the parent default will
- * override the child's when it is applied.
- */
- addObjectDependency(&attrDef->dobj,
- inhDef->dobj.dumpId);
- }
- }
+ foundDefault |= (parent->attrdefs[inhAttrInd] != NULL);
}
}
- /*
- * Based on the scan of the parents, decide if we can rely on the
- * inherited attr
- */
- if (foundAttr) /* Attr was inherited */
+ /* Remember if we found inherited NOT NULL */
+ tbinfo->inhNotNull[j] = foundNotNull;
+
+ /* Manufacture a DEFAULT NULL clause if necessary */
+ if (foundDefault && tbinfo->attrdefs[j] == NULL)
{
- /* Set inherited flag by default */
- tbinfo->inhAttrs[j] = true;
- tbinfo->inhAttrDef[j] = true;
- tbinfo->inhNotNull[j] = true;
-
- /*
- * Clear it if attr had a default, but parents did not, or
- * mismatch
- */
- if ((attrDef != NULL) && (!defaultsFound || !defaultsMatch))
+ AttrDefInfo *attrDef;
+
+ attrDef = (AttrDefInfo *) malloc(sizeof(AttrDefInfo));
+ attrDef->dobj.objType = DO_ATTRDEF;
+ attrDef->dobj.catId.tableoid = 0;
+ attrDef->dobj.catId.oid = 0;
+ AssignDumpId(&attrDef->dobj);
+ attrDef->dobj.name = strdup(tbinfo->dobj.name);
+ attrDef->dobj.namespace = tbinfo->dobj.namespace;
+ attrDef->dobj.dump = tbinfo->dobj.dump;
+
+ attrDef->adtable = tbinfo;
+ attrDef->adnum = j + 1;
+ attrDef->adef_expr = strdup("NULL");
+
+ /* Will column be dumped explicitly? */
+ if (shouldPrintColumn(tbinfo, j))
{
- tbinfo->inhAttrs[j] = false;
- tbinfo->inhAttrDef[j] = false;
+ attrDef->separate = false;
+ /* No dependency needed: NULL cannot have dependencies */
}
-
- /*
- * Clear it if NOT NULL and none of the parents were NOT NULL
- */
- if (tbinfo->notnull[j] && !foundNotNull)
+ else
{
- tbinfo->inhAttrs[j] = false;
- tbinfo->inhNotNull[j] = false;
+ /* column will be suppressed, print default separately */
+ attrDef->separate = true;
+ /* ensure it comes out after the table */
+ addObjectDependency(&attrDef->dobj,
+ tbinfo->dobj.dumpId);
}
- /* Clear it if attr has local definition */
- if (tbinfo->attislocal[j])
- tbinfo->inhAttrs[j] = false;
+ tbinfo->attrdefs[j] = attrDef;
}
}
}
* attstattarget doesn't exist in 7.1. It does exist in 7.2, but
* we don't dump it because we can't tell whether it's been
* explicitly set or was just a default.
+ *
+ * attislocal doesn't exist before 7.3, either; in older databases
+ * we just assume that inherited columns had no local definition.
*/
appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, "
"-1 AS attstattarget, a.attstorage, "
tbinfo->attlen = (int *) malloc(ntups * sizeof(int));
tbinfo->attalign = (char *) malloc(ntups * sizeof(char));
tbinfo->attislocal = (bool *) malloc(ntups * sizeof(bool));
- tbinfo->notnull = (bool *) malloc(ntups * sizeof(bool));
- tbinfo->attrdefs = (AttrDefInfo **) malloc(ntups * sizeof(AttrDefInfo *));
tbinfo->attoptions = (char **) malloc(ntups * sizeof(char *));
tbinfo->attcollation = (Oid *) malloc(ntups * sizeof(Oid));
- tbinfo->inhAttrs = (bool *) malloc(ntups * sizeof(bool));
- tbinfo->inhAttrDef = (bool *) malloc(ntups * sizeof(bool));
+ tbinfo->notnull = (bool *) malloc(ntups * sizeof(bool));
tbinfo->inhNotNull = (bool *) malloc(ntups * sizeof(bool));
+ tbinfo->attrdefs = (AttrDefInfo **) malloc(ntups * sizeof(AttrDefInfo *));
hasdefaults = false;
for (j = 0; j < ntups; j++)
if (PQgetvalue(res, j, i_atthasdef)[0] == 't')
hasdefaults = true;
/* these flags will be set in flagInhAttrs() */
- tbinfo->inhAttrs[j] = false;
- tbinfo->inhAttrDef[j] = false;
tbinfo->inhNotNull[j] = false;
}
{
int adnum;
+ adnum = atoi(PQgetvalue(res, j, 2));
+
+ if (adnum <= 0 || adnum > ntups)
+ {
+ write_msg(NULL, "invalid adnum value %d for table \"%s\"\n",
+ adnum, tbinfo->dobj.name);
+ exit_nicely();
+ }
+
+ /*
+ * dropped columns shouldn't have defaults, but just in case,
+ * ignore 'em
+ */
+ if (tbinfo->attisdropped[adnum - 1])
+ continue;
+
attrdefs[j].dobj.objType = DO_ATTRDEF;
attrdefs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, 0));
attrdefs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, 1));
AssignDumpId(&attrdefs[j].dobj);
attrdefs[j].adtable = tbinfo;
- attrdefs[j].adnum = adnum = atoi(PQgetvalue(res, j, 2));
+ attrdefs[j].adnum = adnum;
attrdefs[j].adef_expr = strdup(PQgetvalue(res, j, 3));
attrdefs[j].dobj.name = strdup(tbinfo->dobj.name);
/*
* Defaults on a VIEW must always be dumped as separate ALTER
* TABLE commands. Defaults on regular tables are dumped as
- * part of the CREATE TABLE if possible. To check if it's
- * safe, we mark the default as needing to appear before the
- * CREATE.
+ * part of the CREATE TABLE if possible, which it won't be
+ * if the column is not going to be emitted explicitly.
*/
if (tbinfo->relkind == RELKIND_VIEW)
{
addObjectDependency(&attrdefs[j].dobj,
tbinfo->dobj.dumpId);
}
+ else if (!shouldPrintColumn(tbinfo, adnum - 1))
+ {
+ /* column will be suppressed, print default separately */
+ attrdefs[j].separate = true;
+ /* needed in case pre-7.3 DB: */
+ addObjectDependency(&attrdefs[j].dobj,
+ tbinfo->dobj.dumpId);
+ }
else
{
attrdefs[j].separate = false;
+ /*
+ * Mark the default as needing to appear before the table,
+ * so that any dependencies it has must be emitted before
+ * the CREATE TABLE. If this is not possible, we'll
+ * change to "separate" mode while sorting dependencies.
+ */
addObjectDependency(&tbinfo->dobj,
attrdefs[j].dobj.dumpId);
}
- if (adnum <= 0 || adnum > ntups)
- {
- write_msg(NULL, "invalid adnum value %d for table \"%s\"\n",
- adnum, tbinfo->dobj.name);
- exit_nicely();
- }
tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
}
PQclear(res);
destroyPQExpBuffer(q);
}
+/*
+ * Test whether a column should be printed as part of table's CREATE TABLE.
+ * Column number is zero-based.
+ *
+ * Normally this is always true, but it's false for dropped columns, as well
+ * as those that were inherited without any local definition. (If we print
+ * such a column it will mistakenly get pg_attribute.attislocal set to true.)
+ * However, in binary_upgrade mode, we must print all such columns anyway and
+ * fix the attislocal/attisdropped state later, so as to keep control of the
+ * physical column order.
+ *
+ * This function exists because there are scattered nonobvious places that
+ * must be kept in sync with this decision.
+ */
+bool
+shouldPrintColumn(TableInfo *tbinfo, int colno)
+{
+ if (binary_upgrade)
+ return true;
+ return (tbinfo->attislocal[colno] && !tbinfo->attisdropped[colno]);
+}
+
/*
* getTSParsers:
fmtId(tbinfo->dobj.name));
/*
- * In case of a binary upgrade, we dump the table normally and attach
- * it to the type afterward.
+ * Attach to type, if reloftype; except in case of a binary upgrade,
+ * we dump the table normally and attach it to the type afterward.
*/
if (tbinfo->reloftype && !binary_upgrade)
appendPQExpBuffer(q, " OF %s", tbinfo->reloftype);
+
+ /* Dump the attributes */
actual_atts = 0;
for (j = 0; j < tbinfo->numatts; j++)
{
/*
- * Normally, dump if it's one of the table's own attrs, and not
- * dropped. But for binary upgrade, dump all the columns.
+ * Normally, dump if it's locally defined in this table, and not
+ * dropped. But for binary upgrade, we'll dump all the columns,
+ * and then fix up the dropped and nonlocal cases below.
*/
- if ((!tbinfo->inhAttrs[j] && !tbinfo->attisdropped[j]) ||
- binary_upgrade)
+ if (shouldPrintColumn(tbinfo, j))
{
/*
- * Default value --- suppress if inherited (except in
- * binary-upgrade case, where we're not doing normal
- * inheritance) or if it's to be printed separately.
+ * Default value --- suppress if to be printed separately.
*/
- bool has_default = (tbinfo->attrdefs[j] != NULL
- && (!tbinfo->inhAttrDef[j] || binary_upgrade)
- && !tbinfo->attrdefs[j]->separate);
+ bool has_default = (tbinfo->attrdefs[j] != NULL &&
+ !tbinfo->attrdefs[j]->separate);
/*
* Not Null constraint --- suppress if inherited, except in
- * binary-upgrade case.
+ * binary-upgrade case where that won't work.
*/
- bool has_notnull = (tbinfo->notnull[j]
- && (!tbinfo->inhNotNull[j] || binary_upgrade));
+ bool has_notnull = (tbinfo->notnull[j] &&
+ (!tbinfo->inhNotNull[j] ||
+ binary_upgrade));
- if (tbinfo->reloftype && !has_default && !has_notnull && !binary_upgrade)
+ /* Skip column if fully defined by reloftype */
+ if (tbinfo->reloftype &&
+ !has_default && !has_notnull && !binary_upgrade)
continue;
/* Format properly if not first attr */
}
}
- /* Loop dumping statistics and storage statements */
+ /*
+ * Dump additional per-column properties that we can't handle in the
+ * main CREATE TABLE command.
+ */
for (j = 0; j < tbinfo->numatts; j++)
{
+ /* None of this applies to dropped columns */
+ if (tbinfo->attisdropped[j])
+ continue;
+
+ /*
+ * If we didn't dump the column definition explicitly above, and
+ * it is NOT NULL and did not inherit that property from a parent,
+ * we have to mark it separately.
+ */
+ if (!shouldPrintColumn(tbinfo, j) &&
+ tbinfo->notnull[j] && !tbinfo->inhNotNull[j])
+ {
+ appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
+ fmtId(tbinfo->dobj.name));
+ appendPQExpBuffer(q, "ALTER COLUMN %s SET NOT NULL;\n",
+ fmtId(tbinfo->attnames[j]));
+ }
+
/*
* Dump per-column statistics information. We only issue an ALTER
* TABLE statement if the attstattarget entry for this column is
* non-negative (i.e. it's not the default value)
*/
- if (tbinfo->attstattarget[j] >= 0 &&
- !tbinfo->attisdropped[j])
+ if (tbinfo->attstattarget[j] >= 0)
{
appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
fmtId(tbinfo->dobj.name));
* Dump per-column storage information. The statement is only
* dumped if the storage has been changed from the type's default.
*/
- if (!tbinfo->attisdropped[j] && tbinfo->attstorage[j] != tbinfo->typstorage[j])
+ if (tbinfo->attstorage[j] != tbinfo->typstorage[j])
{
switch (tbinfo->attstorage[j])
{
PQExpBuffer q;
PQExpBuffer delq;
- /* Only print it if "separate" mode is selected */
- if (!tbinfo->dobj.dump || !adinfo->separate || dataOnly)
+ /* Skip if table definition not to be dumped */
+ if (!tbinfo->dobj.dump || dataOnly)
return;
- /* Don't print inherited defaults, either, except for binary upgrade */
- if (tbinfo->inhAttrDef[adnum - 1] && !binary_upgrade)
+ /* Skip if not "separate"; it was dumped in the table's definition */
+ if (!adinfo->separate)
return;
q = createPQExpBuffer();
delq = createPQExpBuffer();
- appendPQExpBuffer(q, "ALTER TABLE %s ",
+ appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
fmtId(tbinfo->dobj.name));
appendPQExpBuffer(q, "ALTER COLUMN %s SET DEFAULT %s;\n",
fmtId(tbinfo->attnames[adnum - 1]),