@ -83,6 +83,7 @@ typedef struct
List * ckconstraints ; /* CHECK constraints */
List * ckconstraints ; /* CHECK constraints */
List * fkconstraints ; /* FOREIGN KEY constraints */
List * fkconstraints ; /* FOREIGN KEY constraints */
List * ixconstraints ; /* index-creating constraints */
List * ixconstraints ; /* index-creating constraints */
List * nnconstraints ; /* NOT NULL constraints */
List * likeclauses ; /* LIKE clauses that need post-processing */
List * likeclauses ; /* LIKE clauses that need post-processing */
List * extstats ; /* cloned extended statistics */
List * extstats ; /* cloned extended statistics */
List * blist ; /* "before list" of things to do before
List * blist ; /* "before list" of things to do before
@ -244,6 +245,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
cxt . ckconstraints = NIL ;
cxt . ckconstraints = NIL ;
cxt . fkconstraints = NIL ;
cxt . fkconstraints = NIL ;
cxt . ixconstraints = NIL ;
cxt . ixconstraints = NIL ;
cxt . nnconstraints = NIL ;
cxt . likeclauses = NIL ;
cxt . likeclauses = NIL ;
cxt . extstats = NIL ;
cxt . extstats = NIL ;
cxt . blist = NIL ;
cxt . blist = NIL ;
@ -348,6 +350,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
*/
*/
stmt - > tableElts = cxt . columns ;
stmt - > tableElts = cxt . columns ;
stmt - > constraints = cxt . ckconstraints ;
stmt - > constraints = cxt . ckconstraints ;
stmt - > nnconstraints = cxt . nnconstraints ;
result = lappend ( cxt . blist , stmt ) ;
result = lappend ( cxt . blist , stmt ) ;
result = list_concat ( result , cxt . alist ) ;
result = list_concat ( result , cxt . alist ) ;
@ -537,6 +540,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
bool saw_default ;
bool saw_default ;
bool saw_identity ;
bool saw_identity ;
bool saw_generated ;
bool saw_generated ;
bool need_notnull = false ;
ListCell * clist ;
ListCell * clist ;
cxt - > columns = lappend ( cxt - > columns , column ) ;
cxt - > columns = lappend ( cxt - > columns , column ) ;
@ -634,10 +638,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
constraint - > cooked_expr = NULL ;
constraint - > cooked_expr = NULL ;
column - > constraints = lappend ( column - > constraints , constraint ) ;
column - > constraints = lappend ( column - > constraints , constraint ) ;
constraint = makeNode ( Constraint ) ;
/* have a NOT NULL constraint added later */
constraint - > contype = CONSTR_NOTNULL ;
need_notnull = true ;
constraint - > location = - 1 ;
column - > constraints = lappend ( column - > constraints , constraint ) ;
}
}
/* Process column constraints, if any... */
/* Process column constraints, if any... */
@ -655,7 +657,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
switch ( constraint - > contype )
switch ( constraint - > contype )
{
{
case CONSTR_NULL :
case CONSTR_NULL :
if ( saw_nullable & & column - > is_not_null )
if ( ( saw_nullable & & column - > is_not_null ) | | need_not null )
ereport ( ERROR ,
ereport ( ERROR ,
( errcode ( ERRCODE_SYNTAX_ERROR ) ,
( errcode ( ERRCODE_SYNTAX_ERROR ) ,
errmsg ( " conflicting NULL/NOT NULL declarations for column \" %s \" of table \" %s \" " ,
errmsg ( " conflicting NULL/NOT NULL declarations for column \" %s \" of table \" %s \" " ,
@ -667,15 +669,58 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
break ;
break ;
case CONSTR_NOTNULL :
case CONSTR_NOTNULL :
if ( saw_nullable & & ! column - > is_not_null )
/*
* For NOT NULL declarations , we need to mark the column as
* not nullable , and set things up to have a CHECK constraint
* created . Also , duplicate NOT NULL declarations are not
* allowed .
*/
if ( saw_nullable )
{
if ( ! column - > is_not_null )
ereport ( ERROR ,
ereport ( ERROR ,
( errcode ( ERRCODE_SYNTAX_ERROR ) ,
( errcode ( ERRCODE_SYNTAX_ERROR ) ,
errmsg ( " conflicting NULL/NOT NULL declarations for column \" %s \" of table \" %s \" " ,
errmsg ( " conflicting NULL/NOT NULL declarations for column \" %s \" of table \" %s \" " ,
column - > colname , cxt - > relation - > relname ) ,
column - > colname , cxt - > relation - > relname ) ,
parser_errposition ( cxt - > pstate ,
parser_errposition ( cxt - > pstate ,
constraint - > location ) ) ) ;
constraint - > location ) ) ) ;
else
ereport ( ERROR ,
errcode ( ERRCODE_SYNTAX_ERROR ) ,
errmsg ( " redundant NOT NULL declarations for column \" %s \" of table \" %s \" " ,
column - > colname , cxt - > relation - > relname ) ,
parser_errposition ( cxt - > pstate ,
constraint - > location ) ) ;
}
/*
* If this is the first time we see this column being marked
* not null , keep track to later add a NOT NULL constraint .
*/
if ( ! column - > is_not_null )
{
Constraint * notnull ;
column - > is_not_null = true ;
column - > is_not_null = true ;
saw_nullable = true ;
saw_nullable = true ;
notnull = makeNode ( Constraint ) ;
notnull - > contype = CONSTR_NOTNULL ;
notnull - > conname = constraint - > conname ;
notnull - > deferrable = false ;
notnull - > initdeferred = false ;
notnull - > location = - 1 ;
notnull - > colname = column - > colname ;
notnull - > skip_validation = false ;
notnull - > initially_valid = true ;
cxt - > nnconstraints = lappend ( cxt - > nnconstraints , notnull ) ;
/* Don't need this anymore, if we had it */
need_notnull = false ;
}
break ;
break ;
case CONSTR_DEFAULT :
case CONSTR_DEFAULT :
@ -725,16 +770,19 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
column - > identity = constraint - > generated_when ;
column - > identity = constraint - > generated_when ;
saw_identity = true ;
saw_identity = true ;
/* An identity column is implicitly NOT NULL */
/*
if ( saw_nullable & & ! column - > is_not_null )
* Identity columns are always NOT NULL , but we may have a
* constraint already .
*/
if ( ! saw_nullable )
need_notnull = true ;
else if ( ! column - > is_not_null )
ereport ( ERROR ,
ereport ( ERROR ,
( errcode ( ERRCODE_SYNTAX_ERROR ) ,
( errcode ( ERRCODE_SYNTAX_ERROR ) ,
errmsg ( " conflicting NULL/NOT NULL declarations for column \" %s \" of table \" %s \" " ,
errmsg ( " conflicting NULL/NOT NULL declarations for column \" %s \" of table \" %s \" " ,
column - > colname , cxt - > relation - > relname ) ,
column - > colname , cxt - > relation - > relname ) ,
parser_errposition ( cxt - > pstate ,
parser_errposition ( cxt - > pstate ,
constraint - > location ) ) ) ;
constraint - > location ) ) ) ;
column - > is_not_null = true ;
saw_nullable = true ;
break ;
break ;
}
}
@ -758,6 +806,11 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
case CONSTR_CHECK :
case CONSTR_CHECK :
cxt - > ckconstraints = lappend ( cxt - > ckconstraints , constraint ) ;
cxt - > ckconstraints = lappend ( cxt - > ckconstraints , constraint ) ;
/*
* XXX If the user says CHECK ( IS NOT NULL ) , should we turn
* that into a regular NOT NULL constraint ?
*/
break ;
break ;
case CONSTR_PRIMARY :
case CONSTR_PRIMARY :
@ -840,6 +893,29 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
constraint - > location ) ) ) ;
constraint - > location ) ) ) ;
}
}
/*
* If we need a NOT NULL constraint for SERIAL or IDENTITY , and one was
* not explicitly specified , add one now .
*/
if ( need_notnull & & ! ( saw_nullable & & column - > is_not_null ) )
{
Constraint * notnull ;
column - > is_not_null = true ;
notnull = makeNode ( Constraint ) ;
notnull - > contype = CONSTR_NOTNULL ;
notnull - > conname = NULL ;
notnull - > deferrable = false ;
notnull - > initdeferred = false ;
notnull - > location = - 1 ;
notnull - > colname = column - > colname ;
notnull - > skip_validation = false ;
notnull - > initially_valid = true ;
cxt - > nnconstraints = lappend ( cxt - > nnconstraints , notnull ) ;
}
/*
/*
* If needed , generate ALTER FOREIGN TABLE ALTER COLUMN statement to add
* If needed , generate ALTER FOREIGN TABLE ALTER COLUMN statement to add
* per - column foreign data wrapper options to this column after creation .
* per - column foreign data wrapper options to this column after creation .
@ -915,6 +991,10 @@ transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
cxt - > ckconstraints = lappend ( cxt - > ckconstraints , constraint ) ;
cxt - > ckconstraints = lappend ( cxt - > ckconstraints , constraint ) ;
break ;
break ;
case CONSTR_NOTNULL :
cxt - > nnconstraints = lappend ( cxt - > nnconstraints , constraint ) ;
break ;
case CONSTR_FOREIGN :
case CONSTR_FOREIGN :
if ( cxt - > isforeign )
if ( cxt - > isforeign )
ereport ( ERROR ,
ereport ( ERROR ,
@ -926,7 +1006,6 @@ transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
break ;
break ;
case CONSTR_NULL :
case CONSTR_NULL :
case CONSTR_NOTNULL :
case CONSTR_DEFAULT :
case CONSTR_DEFAULT :
case CONSTR_ATTR_DEFERRABLE :
case CONSTR_ATTR_DEFERRABLE :
case CONSTR_ATTR_NOT_DEFERRABLE :
case CONSTR_ATTR_NOT_DEFERRABLE :
@ -962,6 +1041,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
AclResult aclresult ;
AclResult aclresult ;
char * comment ;
char * comment ;
ParseCallbackState pcbstate ;
ParseCallbackState pcbstate ;
bool process_notnull_constraints ;
setup_parser_errposition_callback ( & pcbstate , cxt - > pstate ,
setup_parser_errposition_callback ( & pcbstate , cxt - > pstate ,
table_like_clause - > relation - > location ) ;
table_like_clause - > relation - > location ) ;
@ -1043,6 +1123,8 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
def - > inhcount = 0 ;
def - > inhcount = 0 ;
def - > is_local = true ;
def - > is_local = true ;
def - > is_not_null = attribute - > attnotnull ;
def - > is_not_null = attribute - > attnotnull ;
if ( attribute - > attnotnull )
process_notnull_constraints = true ;
def - > is_from_type = false ;
def - > is_from_type = false ;
def - > storage = 0 ;
def - > storage = 0 ;
def - > raw_default = NULL ;
def - > raw_default = NULL ;
@ -1124,14 +1206,19 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
* we don ' t yet know what column numbers the copied columns will have in
* we don ' t yet know what column numbers the copied columns will have in
* the finished table . If any of those options are specified , add the
* the finished table . If any of those options are specified , add the
* LIKE clause to cxt - > likeclauses so that expandTableLikeClause will be
* LIKE clause to cxt - > likeclauses so that expandTableLikeClause will be
* called after we do know that . Also , remember the relation OID so that
* called after we do know that ; in addition , do that if there are any NOT
* NULL constraints , because those must be propagated even if not
* explicitly requested .
*
* In order for this to work , we remember the relation OID so that
* expandTableLikeClause is certain to open the same table .
* expandTableLikeClause is certain to open the same table .
*/
*/
if ( table_like_clause - > options &
if ( ( table_like_clause - > options &
( CREATE_TABLE_LIKE_DEFAULTS |
( CREATE_TABLE_LIKE_DEFAULTS |
CREATE_TABLE_LIKE_GENERATED |
CREATE_TABLE_LIKE_GENERATED |
CREATE_TABLE_LIKE_CONSTRAINTS |
CREATE_TABLE_LIKE_CONSTRAINTS |
CREATE_TABLE_LIKE_INDEXES ) )
CREATE_TABLE_LIKE_INDEXES ) ) | |
process_notnull_constraints )
{
{
table_like_clause - > relationOid = RelationGetRelid ( relation ) ;
table_like_clause - > relationOid = RelationGetRelid ( relation ) ;
cxt - > likeclauses = lappend ( cxt - > likeclauses , table_like_clause ) ;
cxt - > likeclauses = lappend ( cxt - > likeclauses , table_like_clause ) ;
@ -1203,6 +1290,7 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause)
TupleConstr * constr ;
TupleConstr * constr ;
AttrMap * attmap ;
AttrMap * attmap ;
char * comment ;
char * comment ;
ListCell * lc ;
/*
/*
* Open the relation referenced by the LIKE clause . We should still have
* Open the relation referenced by the LIKE clause . We should still have
@ -1382,6 +1470,20 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause)
}
}
}
}
/*
* Copy NOT NULL constraints , too ( these do not require any option to have
* been given ) .
*/
foreach ( lc , RelationGetNotNullConstraints ( relation , false ) )
{
AlterTableCmd * atsubcmd ;
atsubcmd = makeNode ( AlterTableCmd ) ;
atsubcmd - > subtype = AT_AddConstraint ;
atsubcmd - > def = ( Node * ) lfirst_node ( Constraint , lc ) ;
atsubcmds = lappend ( atsubcmds , atsubcmd ) ;
}
/*
/*
* If we generated any ALTER TABLE actions above , wrap them into a single
* If we generated any ALTER TABLE actions above , wrap them into a single
* ALTER TABLE command . Stick it at the front of the result , so it runs
* ALTER TABLE command . Stick it at the front of the result , so it runs
@ -2059,10 +2161,12 @@ transformIndexConstraints(CreateStmtContext *cxt)
ListCell * lc ;
ListCell * lc ;
/*
/*
* Run through the constraints that need to generate an index . For PRIMARY
* Run through the constraints that need to generate an index , and do so .
* KEY , mark each column as NOT NULL and create an index . For UNIQUE or
*
* EXCLUDE , create an index as for PRIMARY KEY , but do not insist on NOT
* For PRIMARY KEY , in addition we set each column ' s attnotnull flag true .
* NULL .
* We do not create a separate CHECK ( IS NOT NULL ) constraint , as that
* would be redundant : the PRIMARY KEY constraint itself fulfills that
* role . Other constraint types don ' t need any NOT NULL markings .
*/
*/
foreach ( lc , cxt - > ixconstraints )
foreach ( lc , cxt - > ixconstraints )
{
{
@ -2136,9 +2240,7 @@ transformIndexConstraints(CreateStmtContext *cxt)
}
}
/*
/*
* Now append all the IndexStmts to cxt - > alist . If we generated an ALTER
* Now append all the IndexStmts to cxt - > alist .
* TABLE SET NOT NULL statement to support a primary key , it ' s already in
* cxt - > alist .
*/
*/
cxt - > alist = list_concat ( cxt - > alist , finalindexlist ) ;
cxt - > alist = list_concat ( cxt - > alist , finalindexlist ) ;
}
}
@ -2146,12 +2248,10 @@ transformIndexConstraints(CreateStmtContext *cxt)
/*
/*
* transformIndexConstraint
* transformIndexConstraint
* Transform one UNIQUE , PRIMARY KEY , or EXCLUDE constraint for
* Transform one UNIQUE , PRIMARY KEY , or EXCLUDE constraint for
* transformIndexConstraints .
* transformIndexConstraints . An IndexStmt is returned .
*
*
* We return an IndexStmt . For a PRIMARY KEY constraint , we additionally
* For a PRIMARY KEY constraint , we additionally force the columns to be
* produce NOT NULL constraints , either by marking ColumnDefs in cxt - > columns
* marked as NOT NULL , without producing a CHECK ( IS NOT NULL ) constraint .
* as is_not_null or by adding an ALTER TABLE SET NOT NULL command to
* cxt - > alist .
*/
*/
static IndexStmt *
static IndexStmt *
transformIndexConstraint ( Constraint * constraint , CreateStmtContext * cxt )
transformIndexConstraint ( Constraint * constraint , CreateStmtContext * cxt )
@ -2417,7 +2517,6 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
{
{
char * key = strVal ( lfirst ( lc ) ) ;
char * key = strVal ( lfirst ( lc ) ) ;
bool found = false ;
bool found = false ;
bool forced_not_null = false ;
ColumnDef * column = NULL ;
ColumnDef * column = NULL ;
ListCell * columns ;
ListCell * columns ;
IndexElem * iparam ;
IndexElem * iparam ;
@ -2438,13 +2537,14 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
* column is defined in the new table . For PRIMARY KEY , we
* column is defined in the new table . For PRIMARY KEY , we
* can apply the NOT NULL constraint cheaply here . . . unless
* can apply the NOT NULL constraint cheaply here . . . unless
* the column is marked is_from_type , in which case marking it
* the column is marked is_from_type , in which case marking it
* here would be ineffective ( see MergeAttributes ) .
* here would be ineffective ( see MergeAttributes ) . Note that
* this isn ' t effective in ALTER TABLE either , unless the
* column is being added in the same command .
*/
*/
if ( constraint - > contype = = CONSTR_PRIMARY & &
if ( constraint - > contype = = CONSTR_PRIMARY & &
! column - > is_from_type )
! column - > is_from_type )
{
{
column - > is_not_null = true ;
column - > is_not_null = true ;
forced_not_null = true ;
}
}
}
}
else if ( SystemAttributeByName ( key ) ! = NULL )
else if ( SystemAttributeByName ( key ) ! = NULL )
@ -2487,14 +2587,6 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
if ( strcmp ( key , inhname ) = = 0 )
if ( strcmp ( key , inhname ) = = 0 )
{
{
found = true ;
found = true ;
/*
* It ' s tempting to set forced_not_null if the
* parent column is already NOT NULL , but that
* seems unsafe because the column ' s NOT NULL
* marking might disappear between now and
* execution . Do the runtime check to be safe .
*/
break ;
break ;
}
}
}
}
@ -2548,15 +2640,11 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
iparam - > nulls_ordering = SORTBY_NULLS_DEFAULT ;
iparam - > nulls_ordering = SORTBY_NULLS_DEFAULT ;
index - > indexParams = lappend ( index - > indexParams , iparam ) ;
index - > indexParams = lappend ( index - > indexParams , iparam ) ;
/*
if ( constraint - > contype = = CONSTR_PRIMARY )
* For a primary - key column , also create an item for ALTER TABLE
* SET NOT NULL if we couldn ' t ensure it via is_not_null above .
*/
if ( constraint - > contype = = CONSTR_PRIMARY & & ! forced_not_null )
{
{
AlterTableCmd * notnullcmd = makeNode ( AlterTableCmd ) ;
AlterTableCmd * notnullcmd = makeNode ( AlterTableCmd ) ;
notnullcmd - > subtype = AT_SetNotNull ;
notnullcmd - > subtype = AT_SetAttNotNull ;
notnullcmd - > name = pstrdup ( key ) ;
notnullcmd - > name = pstrdup ( key ) ;
notnullcmds = lappend ( notnullcmds , notnullcmd ) ;
notnullcmds = lappend ( notnullcmds , notnullcmd ) ;
}
}
@ -3328,6 +3416,7 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
cxt . isalter = true ;
cxt . isalter = true ;
cxt . columns = NIL ;
cxt . columns = NIL ;
cxt . ckconstraints = NIL ;
cxt . ckconstraints = NIL ;
cxt . nnconstraints = NIL ;
cxt . fkconstraints = NIL ;
cxt . fkconstraints = NIL ;
cxt . ixconstraints = NIL ;
cxt . ixconstraints = NIL ;
cxt . likeclauses = NIL ;
cxt . likeclauses = NIL ;
@ -3571,8 +3660,8 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
/*
/*
* We assume here that cxt . alist contains only IndexStmts and possibly
* We assume here that cxt . alist contains only IndexStmts and possibly
* ALTER TABLE SET NOT NULL statements generated from primary key
* AT_SetAttNotNull statements generated from primary key constraints .
* constraints . We absorb the subcommands of the latter directly .
* We absorb the subcommands of the latter directly .
*/
*/
if ( IsA ( istmt , IndexStmt ) )
if ( IsA ( istmt , IndexStmt ) )
{
{
@ -3600,14 +3689,21 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
{
{
newcmd = makeNode ( AlterTableCmd ) ;
newcmd = makeNode ( AlterTableCmd ) ;
newcmd - > subtype = AT_AddConstraint ;
newcmd - > subtype = AT_AddConstraint ;
newcmd - > def = ( Node * ) lfirst ( l ) ;
newcmd - > def = ( Node * ) lfirst_node ( Constraint , l ) ;
newcmds = lappend ( newcmds , newcmd ) ;
newcmds = lappend ( newcmds , newcmd ) ;
}
}
foreach ( l , cxt . fkconstraints )
foreach ( l , cxt . fkconstraints )
{
{
newcmd = makeNode ( AlterTableCmd ) ;
newcmd = makeNode ( AlterTableCmd ) ;
newcmd - > subtype = AT_AddConstraint ;
newcmd - > subtype = AT_AddConstraint ;
newcmd - > def = ( Node * ) lfirst ( l ) ;
newcmd - > def = ( Node * ) lfirst_node ( Constraint , l ) ;
newcmds = lappend ( newcmds , newcmd ) ;
}
foreach ( l , cxt . nnconstraints )
{
newcmd = makeNode ( AlterTableCmd ) ;
newcmd - > subtype = AT_AddConstraint ;
newcmd - > def = ( Node * ) lfirst_node ( Constraint , l ) ;
newcmds = lappend ( newcmds , newcmd ) ;
newcmds = lappend ( newcmds , newcmd ) ;
}
}