? src/test/regress/sql/.inherit.sql.swp Index: src/backend/commands/indexcmds.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/commands/indexcmds.c,v retrieving revision 1.92 diff -c -r1.92 indexcmds.c *** src/backend/commands/indexcmds.c 2002/10/21 22:06:19 1.92 --- src/backend/commands/indexcmds.c 2002/12/05 13:40:06 *************** *** 19,29 **** --- 19,31 ---- #include "catalog/catalog.h" #include "catalog/catname.h" #include "catalog/dependency.h" + #include "catalog/heap.h" #include "catalog/index.h" #include "catalog/namespace.h" #include "catalog/pg_opclass.h" #include "catalog/pg_proc.h" #include "commands/defrem.h" + #include "commands/tablecmds.h" #include "miscadmin.h" #include "optimizer/clauses.h" #include "optimizer/planmain.h" *************** *** 166,171 **** --- 168,221 ---- CheckPredicate(cnfPred, rangetable, relationId); } + + /* + * Test that all of the attributes in a primary key are marked + * as not null, otherwise attempt to ALTER TABLE .. SET NOT NULL + */ + if (primary && !IsFuncIndex(attributeList)) + { + List *keys; + + /* + * We assume that analyze.c confirmed the keys exist. We simply + * need to check for nullness here + */ + foreach(keys, attributeList) + { + IndexElem *key = (IndexElem *) lfirst(keys); + HeapTuple atttuple; + + /* System attributes are never null */ + if (SystemAttributeByName(key->name, rel->rd_rel->relhasoids)) + continue; + + atttuple = SearchSysCacheAttName(relationId, key->name); + if (HeapTupleIsValid(atttuple)) + { + /* + * We require pre-existing column to be already marked + * NOT NULL. + * + * TODO: If the type doesn't allow NULLs then the + * attribute doesn't require the NOT NULL constraint + * + * XXX: Should the ALTER TABLE .. SET NOT NULL cascade + * to child tables if it is an inherited attribute? + */ + if(!((Form_pg_attribute) GETSTRUCT(atttuple))->attnotnull) + AlterTableAlterColumnSetNotNull(relationId, false, + key->name); + } + else + elog(ERROR, "DefineIndex: column \"%s\" named in key does not exist", + key->name); + + ReleaseSysCache(atttuple); + } + } + + /* * Prepare arguments for index_create, primarily an IndexInfo * structure *************** *** 296,302 **** tuple = SearchSysCacheAttName(relId, arg); if (!HeapTupleIsValid(tuple)) ! elog(ERROR, "DefineIndex: attribute \"%s\" not found", arg); att = (Form_pg_attribute) GETSTRUCT(tuple); indexInfo->ii_KeyAttrNumbers[nargs] = att->attnum; argTypes[nargs] = att->atttypid; --- 346,352 ---- tuple = SearchSysCacheAttName(relId, arg); if (!HeapTupleIsValid(tuple)) ! elog(ERROR, "DefineIndex: column \"%s\" named in key does not exist", arg); att = (Form_pg_attribute) GETSTRUCT(tuple); indexInfo->ii_KeyAttrNumbers[nargs] = att->attnum; argTypes[nargs] = att->atttypid; Index: src/backend/parser/analyze.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/parser/analyze.c,v retrieving revision 1.254 diff -c -r1.254 analyze.c *** src/backend/parser/analyze.c 2002/11/15 02:50:07 1.254 --- src/backend/parser/analyze.c 2002/12/05 13:41:14 *************** *** 1076,1083 **** index->whereClause = NULL; /* ! * Make sure referenced keys exist. If we are making a PRIMARY ! * KEY index, also make sure they are NOT NULL. */ foreach(keys, constraint->keys) { --- 1076,1085 ---- index->whereClause = NULL; /* ! * DefineIndex will check that the attributes exist, and that they ! * are set to NOT NULL. But, if it's a new table it can be faster ! * to mark them is_not_null prior to table creation rather than to ! * alter in the constraint afterward. */ foreach(keys, constraint->keys) { *************** *** 1089,1193 **** { column = lfirst(columns); Assert(IsA(column, ColumnDef)); ! if (strcmp(column->colname, key) == 0) { - found = true; - break; - } - } - if (found) - { - /* found column in the new table; force it to be NOT NULL */ - if (constraint->contype == CONSTR_PRIMARY) column->is_not_null = TRUE; ! } ! else if (SystemAttributeByName(key, cxt->hasoids) != NULL) ! { ! /* ! * column will be a system column in the new table, so ! * accept it. System columns can't ever be null, so no ! * need to worry about PRIMARY/NOT NULL constraint. ! */ ! found = true; ! } ! else if (cxt->inhRelations) ! { ! /* try inherited tables */ ! List *inher; ! ! foreach(inher, cxt->inhRelations) ! { ! RangeVar *inh = lfirst(inher); ! Relation rel; ! int count; ! ! Assert(IsA(inh, RangeVar)); ! rel = heap_openrv(inh, AccessShareLock); ! if (rel->rd_rel->relkind != RELKIND_RELATION) ! elog(ERROR, "inherited table \"%s\" is not a relation", ! inh->relname); ! for (count = 0; count < rel->rd_att->natts; count++) ! { ! Form_pg_attribute inhattr = rel->rd_att->attrs[count]; ! char *inhname = NameStr(inhattr->attname); ! ! if (inhattr->attisdropped) ! continue; ! if (strcmp(key, inhname) == 0) ! { ! found = true; ! ! /* ! * If the column is inherited, we currently ! * have no easy way to force it to be NOT ! * NULL. Only way I can see to fix this would ! * be to convert the inherited-column info to ! * ColumnDef nodes before we reach this point, ! * and then create the table from those nodes ! * rather than referencing the parent tables ! * later. That would likely be cleaner, but ! * too much work to contemplate right now. ! * Instead, raise an error if the inherited ! * column won't be NOT NULL. (Would a WARNING ! * be more reasonable?) ! */ ! if (constraint->contype == CONSTR_PRIMARY && ! !inhattr->attnotnull) ! elog(ERROR, "inherited attribute \"%s\" cannot be a PRIMARY KEY because it is not marked NOT NULL", ! inhname); ! break; ! } ! } ! heap_close(rel, NoLock); ! if (found) ! break; ! } ! } ! else if (OidIsValid(cxt->relOid)) ! { ! /* ALTER TABLE case: does column already exist? */ ! HeapTuple atttuple; ! ! atttuple = SearchSysCacheAttName(cxt->relOid, key); ! if (HeapTupleIsValid(atttuple)) ! { ! found = true; ! ! /* ! * We require pre-existing column to be already marked ! * NOT NULL. ! */ ! if (constraint->contype == CONSTR_PRIMARY && ! !((Form_pg_attribute) GETSTRUCT(atttuple))->attnotnull) ! elog(ERROR, "Existing attribute \"%s\" cannot be a PRIMARY KEY because it is not marked NOT NULL", ! key); ! ReleaseSysCache(atttuple); } } - - if (!found) - elog(ERROR, "%s: column \"%s\" named in key does not exist", - cxt->stmtType, key); /* Check for PRIMARY KEY(foo, foo) */ foreach(columns, index->indexParams) --- 1091,1102 ---- { column = lfirst(columns); Assert(IsA(column, ColumnDef)); ! if (strcmp(column->colname, key) == 0 && constraint->contype == CONSTR_PRIMARY) { column->is_not_null = TRUE; ! break; } } /* Check for PRIMARY KEY(foo, foo) */ foreach(columns, index->indexParams) Index: src/test/regress/expected/alter_table.out =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/test/regress/expected/alter_table.out,v retrieving revision 1.53 diff -c -r1.53 alter_table.out *** src/test/regress/expected/alter_table.out 2002/10/19 01:35:43 1.53 --- src/test/regress/expected/alter_table.out 2002/12/05 13:43:06 *************** *** 508,514 **** create table atacc1 ( test int ); -- add a unique constraint (fails) alter table atacc1 add constraint atacc_test1 unique (test1); ! ERROR: ALTER TABLE: column "test1" named in key does not exist drop table atacc1; -- something a little more complicated create table atacc1 ( test int, test2 int); --- 508,515 ---- create table atacc1 ( test int ); -- add a unique constraint (fails) alter table atacc1 add constraint atacc_test1 unique (test1); ! NOTICE: ALTER TABLE / ADD UNIQUE will create implicit index 'atacc_test1' for table 'atacc1' ! ERROR: DefineIndex: attribute "test1" not found drop table atacc1; -- something a little more complicated create table atacc1 ( test int, test2 int); *************** *** 539,555 **** create table atacc1 ( test int ); -- add a primary key constraint alter table atacc1 add constraint atacc_test1 primary key (test); ! ERROR: Existing attribute "test" cannot be a PRIMARY KEY because it is not marked NOT NULL -- insert first value insert into atacc1 (test) values (2); -- should fail insert into atacc1 (test) values (2); -- should succeed insert into atacc1 (test) values (4); -- inserting NULL should fail insert into atacc1 (test) values(NULL); ! -- try adding a primary key oid constraint alter table atacc1 add constraint atacc_oid1 primary key(oid); NOTICE: ALTER TABLE / ADD PRIMARY KEY will create implicit index 'atacc_oid1' for table 'atacc1' drop table atacc1; -- let's do one where the primary key constraint fails when added --- 540,563 ---- create table atacc1 ( test int ); -- add a primary key constraint alter table atacc1 add constraint atacc_test1 primary key (test); ! NOTICE: ALTER TABLE / ADD PRIMARY KEY will create implicit index 'atacc_test1' for table 'atacc1' -- insert first value insert into atacc1 (test) values (2); -- should fail insert into atacc1 (test) values (2); + ERROR: Cannot insert a duplicate key into unique index atacc_test1 -- should succeed insert into atacc1 (test) values (4); -- inserting NULL should fail insert into atacc1 (test) values(NULL); ! ERROR: ExecInsert: Fail to add null value in not null attribute test ! -- try adding a second primary key (should fail) alter table atacc1 add constraint atacc_oid1 primary key(oid); + ERROR: ALTER TABLE / PRIMARY KEY multiple primary keys for table 'atacc1' are not allowed + -- drop first primary key constraint + alter table atacc1 drop constraint atacc_test1 restrict; + -- try adding a primary key on oid (should succeed) + alter table atacc1 add constraint atacc_oid1 primary key(oid); NOTICE: ALTER TABLE / ADD PRIMARY KEY will create implicit index 'atacc_oid1' for table 'atacc1' drop table atacc1; -- let's do one where the primary key constraint fails when added *************** *** 559,565 **** insert into atacc1 (test) values (2); -- add a primary key (fails) alter table atacc1 add constraint atacc_test1 primary key (test); ! ERROR: Existing attribute "test" cannot be a PRIMARY KEY because it is not marked NOT NULL insert into atacc1 (test) values (3); drop table atacc1; -- let's do another one where the primary key constraint fails when added --- 567,574 ---- insert into atacc1 (test) values (2); -- add a primary key (fails) alter table atacc1 add constraint atacc_test1 primary key (test); ! NOTICE: ALTER TABLE / ADD PRIMARY KEY will create implicit index 'atacc_test1' for table 'atacc1' ! ERROR: Cannot create unique index. Table contains non-unique values insert into atacc1 (test) values (3); drop table atacc1; -- let's do another one where the primary key constraint fails when added *************** *** 568,574 **** insert into atacc1 (test) values (NULL); -- add a primary key (fails) alter table atacc1 add constraint atacc_test1 primary key (test); ! ERROR: Existing attribute "test" cannot be a PRIMARY KEY because it is not marked NOT NULL insert into atacc1 (test) values (3); drop table atacc1; -- let's do one where the primary key constraint fails --- 577,584 ---- insert into atacc1 (test) values (NULL); -- add a primary key (fails) alter table atacc1 add constraint atacc_test1 primary key (test); ! NOTICE: ALTER TABLE / ADD PRIMARY KEY will create implicit index 'atacc_test1' for table 'atacc1' ! ERROR: ALTER TABLE: Attribute "test" contains NULL values insert into atacc1 (test) values (3); drop table atacc1; -- let's do one where the primary key constraint fails *************** *** 576,598 **** create table atacc1 ( test int ); -- add a primary key constraint (fails) alter table atacc1 add constraint atacc_test1 primary key (test1); ! ERROR: ALTER TABLE: column "test1" named in key does not exist drop table atacc1; -- something a little more complicated create table atacc1 ( test int, test2 int); -- add a primary key constraint alter table atacc1 add constraint atacc_test1 primary key (test, test2); ! ERROR: Existing attribute "test" cannot be a PRIMARY KEY because it is not marked NOT NULL -- try adding a second primary key - should fail alter table atacc1 add constraint atacc_test2 primary key (test); ! ERROR: Existing attribute "test" cannot be a PRIMARY KEY because it is not marked NOT NULL -- insert initial value insert into atacc1 (test,test2) values (4,4); -- should fail insert into atacc1 (test,test2) values (4,4); insert into atacc1 (test,test2) values (NULL,3); insert into atacc1 (test,test2) values (3, NULL); insert into atacc1 (test,test2) values (NULL,NULL); -- should all succeed insert into atacc1 (test,test2) values (4,5); insert into atacc1 (test,test2) values (5,4); --- 586,613 ---- create table atacc1 ( test int ); -- add a primary key constraint (fails) alter table atacc1 add constraint atacc_test1 primary key (test1); ! NOTICE: ALTER TABLE / ADD PRIMARY KEY will create implicit index 'atacc_test1' for table 'atacc1' ! ERROR: DefineIndex: column "test1" named in key does not exist drop table atacc1; -- something a little more complicated create table atacc1 ( test int, test2 int); -- add a primary key constraint alter table atacc1 add constraint atacc_test1 primary key (test, test2); ! NOTICE: ALTER TABLE / ADD PRIMARY KEY will create implicit index 'atacc_test1' for table 'atacc1' -- try adding a second primary key - should fail alter table atacc1 add constraint atacc_test2 primary key (test); ! ERROR: ALTER TABLE / PRIMARY KEY multiple primary keys for table 'atacc1' are not allowed -- insert initial value insert into atacc1 (test,test2) values (4,4); -- should fail insert into atacc1 (test,test2) values (4,4); + ERROR: Cannot insert a duplicate key into unique index atacc_test1 insert into atacc1 (test,test2) values (NULL,3); + ERROR: ExecInsert: Fail to add null value in not null attribute test insert into atacc1 (test,test2) values (3, NULL); + ERROR: ExecInsert: Fail to add null value in not null attribute test2 insert into atacc1 (test,test2) values (NULL,NULL); + ERROR: ExecInsert: Fail to add null value in not null attribute test -- should all succeed insert into atacc1 (test,test2) values (4,5); insert into atacc1 (test,test2) values (5,4); *************** *** 887,899 **** alter table atacc1 rename "........pg.dropped.1........" to x; ERROR: renameatt: attribute "........pg.dropped.1........" does not exist alter table atacc1 add primary key(a); ! ERROR: ALTER TABLE: column "a" named in key does not exist alter table atacc1 add primary key("........pg.dropped.1........"); ! ERROR: ALTER TABLE: column "........pg.dropped.1........" named in key does not exist alter table atacc1 add unique(a); ! ERROR: ALTER TABLE: column "a" named in key does not exist alter table atacc1 add unique("........pg.dropped.1........"); ! ERROR: ALTER TABLE: column "........pg.dropped.1........" named in key does not exist alter table atacc1 add check (a > 3); ERROR: Attribute "a" not found alter table atacc1 add check ("........pg.dropped.1........" > 3); --- 902,918 ---- alter table atacc1 rename "........pg.dropped.1........" to x; ERROR: renameatt: attribute "........pg.dropped.1........" does not exist alter table atacc1 add primary key(a); ! NOTICE: ALTER TABLE / ADD PRIMARY KEY will create implicit index 'atacc1_pkey' for table 'atacc1' ! ERROR: DefineIndex: column "a" named in key does not exist alter table atacc1 add primary key("........pg.dropped.1........"); ! NOTICE: ALTER TABLE / ADD PRIMARY KEY will create implicit index 'atacc1_pkey' for table 'atacc1' ! ERROR: DefineIndex: column "........pg.dropped.1........" named in key does not exist alter table atacc1 add unique(a); ! NOTICE: ALTER TABLE / ADD UNIQUE will create implicit index 'atacc1_a_key' for table 'atacc1' ! ERROR: DefineIndex: attribute "a" not found alter table atacc1 add unique("........pg.dropped.1........"); ! NOTICE: ALTER TABLE / ADD UNIQUE will create implicit index 'atacc1_........pg.dropped.1........_key' for table 'atacc1' ! ERROR: DefineIndex: attribute "........pg.dropped.1........" not found alter table atacc1 add check (a > 3); ERROR: Attribute "a" not found alter table atacc1 add check ("........pg.dropped.1........" > 3); Index: src/test/regress/expected/inherit.out =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/test/regress/expected/inherit.out,v retrieving revision 1.6 diff -c -r1.6 inherit.out *** src/test/regress/expected/inherit.out 2002/03/06 06:10:56 1.6 --- src/test/regress/expected/inherit.out 2002/12/05 13:43:16 *************** *** 534,536 **** --- 534,541 ---- ---------+----+----+----+---- (0 rows) + -- Confirm PRIMARY KEY adds NULL to child + CREATE TABLE z (b TEXT, PRIMARY KEY(aa, b)) inherits (a); + NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index 'z_pkey' for table 'z' + INSERT INTO z VALUES (NULL, 'text'); -- fails + ERROR: ExecInsert: Fail to add null value in not null attribute aa Index: src/test/regress/expected/sanity_check.out =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/test/regress/expected/sanity_check.out,v retrieving revision 1.22 diff -c -r1.22 sanity_check.out *** src/test/regress/expected/sanity_check.out 2002/08/19 19:33:36 1.22 --- src/test/regress/expected/sanity_check.out 2002/12/05 13:43:16 *************** *** 62,68 **** shighway | t tenk1 | t tenk2 | t ! (52 rows) -- -- another sanity check: every system catalog that has OIDs should have --- 62,69 ---- shighway | t tenk1 | t tenk2 | t ! z | t ! (53 rows) -- -- another sanity check: every system catalog that has OIDs should have Index: src/test/regress/sql/alter_table.sql =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/test/regress/sql/alter_table.sql,v retrieving revision 1.34 diff -c -r1.34 alter_table.sql *** src/test/regress/sql/alter_table.sql 2002/10/19 01:35:43 1.34 --- src/test/regress/sql/alter_table.sql 2002/12/05 13:43:27 *************** *** 415,421 **** insert into atacc1 (test) values (4); -- inserting NULL should fail insert into atacc1 (test) values(NULL); ! -- try adding a primary key oid constraint alter table atacc1 add constraint atacc_oid1 primary key(oid); drop table atacc1; --- 415,425 ---- insert into atacc1 (test) values (4); -- inserting NULL should fail insert into atacc1 (test) values(NULL); ! -- try adding a second primary key (should fail) ! alter table atacc1 add constraint atacc_oid1 primary key(oid); ! -- drop first primary key constraint ! alter table atacc1 drop constraint atacc_test1 restrict; ! -- try adding a primary key on oid (should succeed) alter table atacc1 add constraint atacc_oid1 primary key(oid); drop table atacc1; Index: src/test/regress/sql/inherit.sql =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/test/regress/sql/inherit.sql,v retrieving revision 1.3 diff -c -r1.3 inherit.sql *** src/test/regress/sql/inherit.sql 2001/01/05 06:34:22 1.3 --- src/test/regress/sql/inherit.sql 2002/12/05 13:43:28 *************** *** 92,94 **** --- 92,98 ---- SELECT relname, b.* FROM ONLY b, pg_class where b.tableoid = pg_class.oid; SELECT relname, c.* FROM ONLY c, pg_class where c.tableoid = pg_class.oid; SELECT relname, d.* FROM ONLY d, pg_class where d.tableoid = pg_class.oid; + + -- Confirm PRIMARY KEY adds NULL to child + CREATE TABLE z (b TEXT, PRIMARY KEY(aa, b)) inherits (a); + INSERT INTO z VALUES (NULL, 'text'); -- fails