Peter Eisentraut wrote:
> On 7/4/13 5:06 PM, Alvaro Herrera wrote:
> > FWIW if changing the behavior of NOT NULL constraints is desired, I
> > still have the patch to catalogue them around, if anyone wants to play
> > around. I haven't gotten around to finishing it up, yet :-(
>
> If your latest patch isn't publicly available, I'd like to see it. I
> might work on that later this year.
Here it is. Note that it is quite incomplete: there are parts that are
not even close to compiling. I think I was trying to figure out how to
determine whether a column was of a composite type.
I might get back to it eventually, so if you start working on it, do let
me know.
--
Álvaro Herrera http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
diff --git a/src/backend/commands/constraint.c b/src/backend/commands/constraint.c
index 8ac8373..74f0766 100644
--- a/src/backend/commands/constraint.c
+++ b/src/backend/commands/constraint.c
@@ -14,12 +14,16 @@
#include "postgres.h"
#include "catalog/index.h"
+#include "catalog/pg_constraint.h"
+#include "commands/constraint.h"
#include "commands/trigger.h"
#include "executor/executor.h"
#include "utils/builtins.h"
#include "utils/rel.h"
+#include "utils/syscache.h"
#include "utils/tqual.h"
+static char *tryExtractNotNull_internal(Node *node, Relation rel);
/*
* unique_key_recheck - trigger function to do a deferred uniqueness check.
@@ -188,3 +192,166 @@ unique_key_recheck(PG_FUNCTION_ARGS)
return PointerGetDatum(NULL);
}
+
+/*
+ * Create a Constraint node representing a "col IS NOT NULL" expression
+ * using a ColumnRef within, for the given relation and column.
+ *
+ * If the constraint name is not provided, it is generated.
+ */
+Constraint *
+createCheckNotNullConstraint(Oid nspid, char *constraint_name,
+ const char *relname, const char *colname)
+{
+ ColumnRef *colref;
+ NullTest *nulltest;
+
+ colref = (ColumnRef *) makeNode(ColumnRef);
+ colref->fields = list_make1(makeString(pstrdup(colname)));
+
+ nulltest = (NullTest *) makeNode(NullTest);
+ nulltest->argisrow = false; /* FIXME -- may be bogus! */
+ nulltest->nulltesttype = IS_NOT_NULL;
+ nulltest->arg = (Expr *) colref;
+
+ return makeCheckConstraint(nspid, constraint_name, relname, colname,
+ nulltest);
+}
+
+/*
+ * Create a Constraint node representing "col IS DISTINCT FROM NULL". This is
+ * just like the above, but this is intended to be used for columns with
+ * composite types, which have slightly different rules.
+ */
+Constraint *
+createCheckDistinctNotNullConstraint(Oid nspid, char *constraint_name,
+ const char *relname, const char *colname,
+ Oid column_type)
+{
+ Constraint *check;
+ ColumnRef *colref;
+ A_Expr *expr;
+
+ colref = (ColumnRef *) makeNode(ColumnRef);
+ colref->fields = list_make1(makeString(pstrdup(colname)));
+
+ expr = makeSimpleA_Expr(AEXPR_DISTINCT, "=", colref, makeNullAConst(-1),
+ -1);
+
+ return makeCheckConstraint(nspid, constraint_name, relname, colname, expr);
+
+ return check;
+}
+
+static Constraint *
+makeCheckConstraint(Oid nspid, char *constraint_name, const char *relname,
+ const char *colname, Node *expr)
+{
+ Constraint *check = makeNode(Constraint);
+
+ check->contype = CONSTR_CHECK;
+ check->location = -1;
+ check->conname = constraint_name ? constraint_name :
+ ChooseConstraintName(relname, colname, "not_null", nspid,
+ NIL);
+ check->raw_expr = raw_expr;
+ check->cooked_expr = NULL;
+
+}
+
+/*
+ * Given a CHECK constraint, examine it and determine whether it is CHECK (col
+ * IS NOT NULL). If it is, return the column name for which it is. Otherwise
+ * return NULL.
+ */
+char *
+tryExtractNotNullFromCheckConstr(Constraint *constr)
+{
+ char *retval;
+
+ Assert(constr->contype == CONSTR_CHECK);
+
+ if (constr->raw_expr != NULL)
+ {
+ retval = tryExtractNotNull_internal(constr->raw_expr, NULL);
+ if (retval != NULL)
+ return retval;
+ }
+
+ if (constr->cooked_expr != NULL)
+ {
+ return tryExtractNotNull_internal(stringToNode(constr->cooked_expr),
+ NULL);
+ }
+
+ return NULL;
+}
+
+/*
+ * As above, but use a pg_constraint row as input.
+ *
+ * tupdesc is pg_constraint's tuple descriptor, and rel is the relation the
+ * constraint is for.
+ */
+char *
+tryExtractNotNullFromCatalog(HeapTuple constrTup, TupleDesc tupdesc,
+ Relation rel)
+{
+ Datum val;
+ bool isnull;
+ char *conbin;
+ Node *node;
+
+ val = SysCacheGetAttr(CONSTROID, constrTup, Anum_pg_constraint_conbin,
+ &isnull);
+ if (isnull)
+ elog(ERROR, "null conbin for constraint %u",
+ HeapTupleGetOid(constrTup));
+ conbin = TextDatumGetCString(val);
+ node = (Node *) stringToNode(conbin);
+
+ return tryExtractNotNull_internal(node, rel);
+}
+
+/*
+ * Worker for the above
+ */
+static char *
+tryExtractNotNull_internal(Node *node, Relation rel)
+{
+ if (IsA(node, NullTest))
+ {
+ NullTest *nulltest = (NullTest *) node;
+
+ if (nulltest->nulltesttype == IS_NOT_NULL)
+ {
+ if (IsA(nulltest->arg, ColumnRef))
+ {
+ ColumnRef *colref = (ColumnRef *) nulltest->arg;
+
+ if (list_length(colref->fields) == 1)
+ return strVal(linitial(colref->fields));
+ }
+ if (IsA(nulltest->arg, Var))
+ {
+ Var *var = (Var *) nulltest->arg;
+ TupleDesc tupdesc;
+
+ if (!RelationIsValid(rel))
+ elog(ERROR,
+ "no relation given to extract constraint from");
+ tupdesc = RelationGetDescr(rel);
+ return NameStr(tupdesc->attrs[var->varattno - 1]->attname);
+ }
+ }
+ }
+
+ /*
+ * XXX Need to check a few more possible wordings of NOT NULL:
+ *
+ * - foo IS DISTINCT FROM NULL
+ * - NOT (foo IS NULL)
+ */
+
+ return NULL;
+}
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index dc0665e..a3bff7d 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -231,6 +231,7 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
create->inhRelations = NIL;
create->ofTypename = NULL;
create->constraints = NIL;
+ create->notnullcols = NIL;
create->options = into->options;
create->oncommit = into->onCommit;
create->tablespacename = into->tableSpaceName;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 00fe113..429b5b4 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -43,6 +43,7 @@
#include "catalog/toasting.h"
#include "commands/cluster.h"
#include "commands/comment.h"
+#include "commands/constraint.h"
#include "commands/defrem.h"
#include "commands/sequence.h"
#include "commands/tablecmds.h"
@@ -54,7 +55,6 @@
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
-#include "nodes/parsenodes.h"
#include "optimizer/clauses.h"
#include "optimizer/planner.h"
#include "parser/parse_clause.h"
@@ -251,8 +251,9 @@ struct DropRelationCallbackState
#define ATT_FOREIGN_TABLE 0x0010
static void truncate_check_rel(Relation rel);
-static List *MergeAttributes(List *schema, List *supers, char relpersistence,
- List **supOids, List **supconstr, int *supOidCount);
+static List *MergeAttributes(List *schema, List *notnullcols, List *supers,
+ char relpersistence, List **supOids, List **supconstr,
+ int *supOidCount);
static bool MergeCheckConstraint(List *constraints, char *name, Node *expr);
static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel);
static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
@@ -313,8 +314,9 @@ static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
static void ATPrepAddOids(List **wqueue, Relation rel, bool recurse,
AlterTableCmd *cmd, LOCKMODE lockmode);
static void ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode);
-static void ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
- const char *colName, LOCKMODE lockmode);
+static void ATExecSetNotNull(List **wqueue, AlteredTableInfo *tab,
+ Relation rel, char *constrname, const char *colName,
+ LOCKMODE lockmode);
static void ATExecColumnDefault(Relation rel, const char *colName,
Node *newDefault, LOCKMODE lockmode);
static void ATPrepSetStatistics(Relation rel, const char *colName,
@@ -341,7 +343,13 @@ static void ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
static void ATAddCheckConstraint(List **wqueue,
AlteredTableInfo *tab, Relation rel,
Constraint *constr,
- bool recurse, bool recursing, LOCKMODE lockmode);
+ bool recurse, bool recursing,
+ LOCKMODE lockmode);
+static void ATAddCheckConstraint_internal(List **wqueue,
+ AlteredTableInfo *tab, Relation rel,
+ Constraint *constr,
+ bool recurse, bool recursing,
+ bool check_it, LOCKMODE lockmode);
static void ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
Constraint *fkconstraint, LOCKMODE lockmode);
static void ATExecDropConstraint(Relation rel, const char *constrName,
@@ -535,7 +543,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
* Look up inheritance ancestors and generate relation schema, including
* inherited attributes.
*/
- schema = MergeAttributes(schema, stmt->inhRelations,
+ schema = MergeAttributes(schema, stmt->notnullcols, stmt->inhRelations,
stmt->relation->relpersistence,
&inheritOids, &old_constraints, &parentOidCount);
@@ -546,6 +554,29 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
*/
descriptor = BuildDescForRelation(schema);
+ /*
+ * For each column that has a NOT NULL marking, create a fake
+ * CHECK constraint.
+ */
+ for (attno = FirstAttributeNumber;
+ attno <= descriptor->natts;
+ attno++)
+ {
+ ColumnDef *colDef = lfirst(listptr);
+
+ if (colDef->is_not_null)
+ {
+ if (column type is composite)
+ check =
+ createCheckDistinctNotNullConstraint(stmt->relation->rd_rel->relnamespace,
+
+ else
+ check = createCheckNotNullConstraint( .. );
+
+ stmt->constraints = lappend(stmt->constraints, check);
+ }
+ }
+
localHasOids = interpretOidsOption(stmt->options);
descriptor->tdhasoid = (localHasOids || parentOidCount > 0);
@@ -1288,6 +1319,8 @@ storage_name(char c)
* Input arguments:
* 'schema' is the column/attribute definition for the table. (It's a list
* of ColumnDef's.) It is destructively changed.
+ * 'notnullcols' is a list of column names that have NOT NULL constraints.
+ * Some of these columns may already have is_not_null already set.
* 'supers' is a list of names (as RangeVar nodes) of parent relations.
* 'relpersistence' is a persistence type of the table.
*
@@ -1340,10 +1373,12 @@ storage_name(char c)
*----------
*/
static List *
-MergeAttributes(List *schema, List *supers, char relpersistence,
- List **supOids, List **supconstr, int *supOidCount)
+MergeAttributes(List *schema, List *notnullcols, List *supers,
+ char relpersistence, List **supOids, List **supconstr,
+ int *supOidCount)
{
ListCell *entry;
+ ListCell *cell;
List *inhSchema = NIL;
List *parentOids = NIL;
List *constraints = NIL;
@@ -1805,6 +1840,26 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
}
/*
+ * If we have NOT NULL constraint declarations, set the is_not_null bits
+ * in the correspoding ColumnDef elements.
+ */
+ foreach (cell, notnullcols)
+ {
+ char *colname = lfirst(cell);
+
+ foreach (entry, schema)
+ {
+ ColumnDef *coldef = lfirst(entry);
+
+ if (strcmp(coldef->colname, colname) == 0)
+ {
+ coldef->is_not_null = true;
+ break; /* no need to keep looking */
+ }
+ }
+ }
+
+ /*
* If we found any conflicting parent default values, check to make sure
* they were overridden by the child.
*/
@@ -3218,7 +3273,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
ATExecDropNotNull(rel, cmd->name, lockmode);
break;
case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
- ATExecSetNotNull(tab, rel, cmd->name, lockmode);
+ ATExecSetNotNull(wqueue, tab, rel, NULL, cmd->name, lockmode);
break;
case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
ATExecSetStatistics(rel, cmd->name, cmd->def, lockmode);
@@ -4835,12 +4890,13 @@ ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode)
* ALTER TABLE ALTER COLUMN SET NOT NULL
*/
static void
-ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
- const char *colName, LOCKMODE lockmode)
+ATExecSetNotNull(List **wqueue, AlteredTableInfo *tab, Relation rel,
+ char *constrname, const char *colName, LOCKMODE lockmode)
{
HeapTuple tuple;
AttrNumber attnum;
Relation attr_rel;
+ Constraint *newconstr;
/*
* lookup the attribute
@@ -4880,6 +4936,20 @@ ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
tab->new_notnull = true;
}
+ /*
+ * We also need to add a new pg_constraint row. Use
+ * ATAddCheckConstraint_internal for that, but let it know that it
+ * doesn't need to test the constraint; we already informed it above,
+ * if necessary.
+ */
+ newconstr = createCheckNotNullConstraint(rel->rd_rel->relnamespace,
+ constrname,
+ NameStr(rel->rd_rel->relname),
+ colName);
+
+ ATAddCheckConstraint_internal(wqueue, tab, rel, newconstr,
+ true, false, false, lockmode);
+
heap_close(attr_rel, RowExclusiveLock);
}
@@ -5570,6 +5640,39 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
Constraint *constr, bool recurse, bool recursing,
LOCKMODE lockmode)
{
+ char *colname;
+
+ /*
+ * If the constraint we're adding is CHECK (col IS NOT NULL), then we route
+ * it through ATExecSetNotNull instead of working directly with it; that
+ * function is responsible for getting back to us to recurse, etc.
+ *
+ * The reason for this is to get the attnotnull bit set for the column, and
+ * also to avoid having a second NOT NULL constraint for a column that
+ * might already have one. (XXX is the latter actually a desirable
+ * property? Consider inherited tables here.)
+ */
+ Assert(constr->contype == CONSTR_CHECK);
+
+ colname = tryExtractNotNullFromCheckConstr(constr);
+ if (colname != NULL)
+ {
+ ATExecSetNotNull(wqueue, tab, rel, constr->conname,
+ colname, lockmode);
+ return;
+ }
+
+
+ /* Not a single-column NOT NULL constraint -- do the regular dance */
+ ATAddCheckConstraint_internal(wqueue, tab, rel, constr, recurse,
+ recursing, true, lockmode);
+}
+
+static void
+ATAddCheckConstraint_internal(List **wqueue, AlteredTableInfo *tab,
+ Relation rel, Constraint *constr, bool recurse,
+ bool recursing, bool check_it, LOCKMODE lockmode)
+{
List *newcons;
ListCell *lcon;
List *children;
@@ -5669,8 +5772,9 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
childtab = ATGetQueueEntry(wqueue, childrel);
/* Recurse to child */
- ATAddCheckConstraint(wqueue, childtab, childrel,
- constr, recurse, true, lockmode);
+ ATAddCheckConstraint_internal(wqueue, childtab, childrel,
+ constr, recurse, true, check_it,
+ lockmode);
heap_close(childrel, NoLock);
}
@@ -6933,6 +7037,7 @@ ATExecDropConstraint(Relation rel, const char *constrName,
while (HeapTupleIsValid(tuple = systable_getnext(scan)))
{
ObjectAddress conobj;
+ char *colName;
con = (Form_pg_constraint) GETSTRUCT(tuple);
@@ -6946,6 +7051,19 @@ ATExecDropConstraint(Relation rel, const char *constrName,
errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
constrName, RelationGetRelationName(rel))));
+ /*
+ * If it's a CHECK constraint, verify whether it is NOT NULL.
+ * If it is, then we may need to unset the attnotnull bit as well.
+ */
+ colName = tryExtractNotNullFromCatalog(tuple,
+ RelationGetDescr(conrel),
+ rel);
+ if (colName != NULL)
+ {
+ /* do something! */
+ elog(NOTICE, "colname is %s", colName);
+ }
+
is_no_inherit_constraint = con->connoinherit;
/*
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index accda01..24a562b 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -37,6 +37,7 @@
#include "catalog/pg_operator.h"
#include "catalog/pg_type.h"
#include "commands/comment.h"
+#include "commands/constraint.h"
#include "commands/defrem.h"
#include "commands/tablecmds.h"
#include "commands/tablespace.h"
@@ -75,6 +76,7 @@ typedef struct
List *ckconstraints; /* CHECK constraints */
List *fkconstraints; /* FOREIGN KEY constraints */
List *ixconstraints; /* index-creating constraints */
+ List *notnulls; /* list of column names declared NOT NULL */
List *inh_indexes; /* cloned indexes from INCLUDING INDEXES */
List *blist; /* "before list" of things to do before
* creating the table */
@@ -100,6 +102,8 @@ typedef struct
static void transformColumnDefinition(CreateStmtContext *cxt,
ColumnDef *column);
+static Constraint *transformNotNullConstraint(CreateStmtContext *cxt,
+ Constraint *constraint, ColumnDef *column);
static void transformTableConstraint(CreateStmtContext *cxt,
Constraint *constraint);
static void transformTableLikeClause(CreateStmtContext *cxt,
@@ -205,6 +209,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
cxt.ckconstraints = NIL;
cxt.fkconstraints = NIL;
cxt.ixconstraints = NIL;
+ cxt.notnulls = NIL;
cxt.inh_indexes = NIL;
cxt.blist = NIL;
cxt.alist = NIL;
@@ -269,6 +274,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
*/
stmt->tableElts = cxt.columns;
stmt->constraints = cxt.ckconstraints;
+ stmt->notnullcols = cxt.notnulls;
result = lappend(cxt.blist, stmt);
result = list_concat(result, cxt.alist);
@@ -471,6 +477,9 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
foreach(clist, column->constraints)
{
+ Constraint *newckconstr;
+ char *colname;
+
constraint = lfirst(clist);
Assert(IsA(constraint, Constraint));
@@ -489,6 +498,11 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
break;
case CONSTR_NOTNULL:
+ /*
+ * For NOT NULL declarations, we need to mark the column as
+ * not nullable. (We don't create a CHECK constraint here;
+ * that will be done in DefineRelation).
+ */
if (saw_nullable && !column->is_not_null)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
@@ -498,8 +512,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
constraint->location)));
column->is_not_null = TRUE;
saw_nullable = true;
- break;
-
+ break;
case CONSTR_DEFAULT:
if (saw_default)
ereport(ERROR,
@@ -515,6 +528,21 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
case CONSTR_CHECK:
cxt->ckconstraints = lappend(cxt->ckconstraints, constraint);
+ /*
+ * If there is a CHECK (foo IS NOT NULL) constraint
+ * declaration, we check the column name used in the
+ * constraint. If it's the same name as the column being
+ * defined, set the is_not_null flag in the column definition;
+ * otherwise remember the column name for later.
+ */
+ colname = tryExtractNotNullFromCheckConstr(constraint);
+ if (colname != NULL)
+ {
+ if (strcmp(colname, column->colname) == 0)
+ column->is_not_null = true;
+ else
+ cxt->notnulls = lappend(cxt->notnulls, colname);
+ }
break;
case CONSTR_PRIMARY:
@@ -586,6 +614,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
static void
transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
{
+ char *colname;
+
switch (constraint->contype)
{
case CONSTR_PRIMARY:
@@ -596,6 +626,9 @@ transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
case CONSTR_CHECK:
cxt->ckconstraints = lappend(cxt->ckconstraints, constraint);
+ colname = tryExtractNotNullFromCheckConstr(constraint);
+ if (colname != NULL)
+ cxt->notnulls = lappend(cxt->notnulls, colname);
break;
case CONSTR_FOREIGN:
@@ -621,6 +654,30 @@ transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
}
/*
+ * Given a NOT NULL column declaration, transform it into a new Constraint node
+ * representing the equivalent CHECK (col) IS NOT NULL.
+ */
+static Constraint *
+transformNotNullConstraint(CreateStmtContext *cxt, Constraint *constraint,
+ ColumnDef *column)
+{
+ Constraint *check;
+ Oid nspid;
+
+ if (cxt->rel)
+ nspid = RelationGetNamespace(cxt->rel);
+ else
+ nspid = RangeVarGetCreationNamespace(cxt->relation);
+
+ check = createCheckNotNullConstraint(nspid,
+ NULL,
+ cxt->relation->relname,
+ column->colname);
+
+ return check;
+}
+
+/*
* transformTableLikeClause
*
* Change the LIKE <srctable> portion of a CREATE TABLE statement into
@@ -2343,6 +2400,7 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString)
cxt.ckconstraints = NIL;
cxt.fkconstraints = NIL;
cxt.ixconstraints = NIL;
+ cxt.notnulls = NIL;
cxt.inh_indexes = NIL;
cxt.blist = NIL;
cxt.alist = NIL;
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 09ca6dd..95bb2d5 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -5729,7 +5729,35 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
resetPQExpBuffer(q);
- if (fout->remoteVersion >= 90200)
+ if (fout->remoteVersion >= 90300)
+ {
+ /*
+ * In 9.3, NOT NULL constraints are in pg_constraint and will be
+ * dumped as table constraints, so it's unnecessary to dump them
+ * here.
+ */
+ appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, "
+ "a.attstattarget, a.attstorage, t.typstorage, "
+ "false as attnotnull, a.atthasdef, a.attisdropped, "
+ "a.attlen, a.attalign, a.attislocal, "
+ "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
+ "array_to_string(a.attoptions, ', ') AS attoptions, "
+ "CASE WHEN a.attcollation <> t.typcollation "
+ "THEN a.attcollation ELSE 0 END AS attcollation, "
+ "pg_catalog.array_to_string(ARRAY("
+ "SELECT pg_catalog.quote_ident(option_name) || "
+ "' ' || pg_catalog.quote_literal(option_value) "
+ "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
+ "ORDER BY option_name"
+ "), E',\n ') AS attfdwoptions "
+ "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
+ "ON a.atttypid = t.oid "
+ "WHERE a.attrelid = '%u'::pg_catalog.oid "
+ "AND a.attnum > 0::pg_catalog.int2 "
+ "ORDER BY a.attrelid, a.attnum",
+ tbinfo->dobj.catId.oid);
+ }
+ else if (fout->remoteVersion >= 90200)
{
/*
* attfdwoptions is new in 9.2.
diff --git a/src/include/commands/constraint.h b/src/include/commands/constraint.h
new file mode 100644
index 0000000..c8f8a43
--- /dev/null
+++ b/src/include/commands/constraint.h
@@ -0,0 +1,33 @@
+/*-------------------------------------------------------------------------
+ *
+ * constraint.h
+ * PostgreSQL CONSTRAINT support declarations
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/include/commands/constraint.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef CONSTRAINT_H
+#define CONSTRAINT_H
+
+#include "nodes/parsenodes.h"
+#include "utils/relcache.h"
+
+extern Constraint *createCheckNotNullConstraint(Oid nspid,
+ char *constraint_name, const char *relname,
+ const char *colname);
+
+extern Constraint *createCheckDistinctNotNullConstraint(Oid nspid,
+ char *constraint_name, const char *relname,
+ const char *colname);
+
+extern char *tryExtractNotNullFromCheckConstr(Constraint *constr);
+
+extern char *tryExtractNotNullFromCatalog(HeapTuple constrTup,
+ TupleDesc tupdesc, Relation rel);
+
+#endif /* CONSTRAINT_H */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index f433166..bfc4cf5 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1439,10 +1439,11 @@ typedef struct VariableShowStmt
* Create Table Statement
*
* NOTE: in the raw gram.y output, ColumnDef and Constraint nodes are
- * intermixed in tableElts, and constraints is NIL. After parse analysis,
- * tableElts contains just ColumnDefs, and constraints contains just
- * Constraint nodes (in fact, only CONSTR_CHECK nodes, in the present
- * implementation).
+ * intermixed in tableElts, and constraints and notnullcols are NIL. After
+ * parse analysis, tableElts contains just ColumnDefs, notnullcols has been
+ * filled with not-nullable column names from various sources, and constraints
+ * contains just Constraint nodes (in fact, only CONSTR_CHECK nodes, in the
+ * present implementation).
* ----------------------
*/
@@ -1455,6 +1456,7 @@ typedef struct CreateStmt
* inhRelation) */
TypeName *ofTypename; /* OF typename */
List *constraints; /* constraints (list of Constraint nodes) */
+ List *notnullcols; /* list of column names with NOT NULL */
List *options; /* options from WITH clause */
OnCommitAction oncommit; /* what do we do at COMMIT? */
char *tablespacename; /* table space to use, or NULL */
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers