Character with Table Inheritance create table character ( name varchar(20) primary key, description text, location varchar(20) references location(name)); create table monster () inherits (character); create table player () inherits (character);
Problems Primary and foreign key constraints are not inherited (hopefully will be in future PostgreSQL releases) Work round this using functions and triggers
Primary key: Character and descendants /* character.name is a primary key; pkey_character_name checks the inheritance hierarcy from character to ensure that name is unique and not null */ create function pkey_character_name() returns trigger as $pkey_character_name$ BEGIN if (exists (select * from character where character.name = NEW.name)) then raise exception cannot have more than one character named %.', NEW.name; end if; return NEW; END $pkey_character_name$ language plpgsql;
Triggering the not null and unique checks on monster.name create table monster () inherits (character); create trigger pkey_character_name before insert on monster for each row execute procedure pkey_character_name(); The same is needed for other descendants of character (such as player)
Foreign key reference to location: character and descendants create function valid_location() returns trigger as $valid_location$ BEGIN if not exists (select name from location where location.name = NEW.location) then raise exception 'There is no location called %', NEW.location; end if; return NEW; END $valid_location$ language plpgsql;
Triggering the referential integrity constraint on character.location create trigger valid_location before insert on monster for each row execute procedure valid_location(); The same is done for player And the same for item, which also refers to location.name And for the descendants of item
More descendants of item create table light_source (is_lit boolean) inherits (item); create table portable_light_source () inherits (portable_item, light_source); And each of these has triggers to enforce entity and referential integrity constraints.
A domain-specific constraint /* The location of a portable item is the same as the location of its holder. When a new portable item is added to the database, its location is set to the location of its holder. */ create function no_bilocation () returns trigger as $no_bilocation$ BEGIN if (NEW.held_by != 'nobody then NEW.location := (select location from character where character.name = NEW.held_by); end if; return NEW; END $no_bilocation$ language plpgsql;
Triggering no_bilocation create trigger no_bilocation before insert on portable_item for each row execute procedure no_bilocation(); create trigger no_bilocation before insert on portable_light_source for each row execute procedure no_bilocation();
Another domain-specific constraint /* when a character changes location, all the portable items held by that character should move as well. */ create function move_portable_items () returns trigger as $move_portable_items$ BEGIN update portable_item set location = NEW.location where portable_item.held_by = NEW.name; return NEW; END $move_portable_items$ language plpgsql;
Triggering move_portable_items create trigger move_portable_items after update on character for each row execute procedure move_portable_items();
Yet another domain-specific constraint /* no_remote_pickup ensures that the held_by attribute of a portable item can only be updated to the name of a holder whose location is the same as that of the item; in other words, a character must move to the place where an item is before picking up the item. */ create function no_remote_pickup() returns trigger as $no_remote_pickup$ BEGIN if NEW.location != (select location from character where character.name = NEW.held_by) then raise exception '% must move to % in order to pick up %', NEW.held_by, NEW.location, NEW.name; end if; return NEW; END $no_remote_pickup$ language plpgsql;
Table Inheritance Convenient, but with some problems Check constraints and not null constraints are inherited, but other kinds of constraints are not Unique, Primary key and foreign key constraints are not inherited Some SQL commands default to accessing descendants; others do not Commands that default to accessing descendants use ONLY to avoid doing so
User defined composite types PostgreSQL also enables user defined composite types Composite types allow table elements to contain structured data Composite types are a kind of user defined type like those discussed in connection with object-relational database management systems.
Functions and Triggers Primary use: to implement domain-specific constraints at the database level Also used to work round lack of constraint inheritance in this example Typically: Define a function that returns a named trigger Then add that trigger to one or more tables
Conclusion Modern relational database management systems provide various extras But it is important to weigh up the benefits of these against their costs