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 (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to