# # Demonstrate how SIGNAL can be used to enforce integrity constraints. # # Naming: # - PO: Purchase Order # - AB: Address Book # - IN: Inventory # Simplified schema: # # Relation 1: # PO_ORDER (PK: po_id) 1:1 <---> 0:N (FK: po_id) PO_ORDER_LINE # # Relation 2: # IN_INVENTORY (PK: item_id) 1:1 <---> 0:N (FK: item_id) PO_ORDER_LINE # # Relation 3: # +--> 0:1 (PK: person_id) AB_PHYSICAL_PERSON # PO_ORDER (FK: cust_id) 1:1 <--| # +--> 0:1 (PK: company_id) AB_MORAL_PERSON # This is an 'arc' relationship :) # --disable_warnings drop database if exists demo; --enable_warnings create database demo; use demo; create table ab_physical_person ( person_id integer, first_name VARCHAR(50), middle_initial CHAR, last_name VARCHAR(50), primary key (person_id)); create table ab_moral_person ( company_id integer, name VARCHAR(100), primary key (company_id)); create table in_inventory ( item_id integer, descr VARCHAR(50), stock integer, primary key (item_id)); create table po_order ( po_id integer auto_increment, cust_type char, /* arc relationship, see cust_id */ cust_id integer, /* FK to ab_physical_person *OR* ab_moral_person */ primary key (po_id)); create table po_order_line ( po_id integer, /* FK to po_order.po_id */ line_no integer, item_id integer, /* FK to in_inventory.item_id */ qty integer); delimiter $$; --echo # --echo # Schema integrity enforcement --echo # create procedure check_pk_person(in person_type char, in id integer) begin declare x integer; declare msg varchar(128); /* Test integrity constraints for an 'arc' relationship. Based on 'person_type', 'id' points to either a physical person, or a moral person. */ case person_type when 'P' then begin select count(person_id) from ab_physical_person where ab_physical_person.person_id = id into x; if (x != 1) then set msg= concat('No such physical person, PK:', id); SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = msg, MYSQL_ERRNO = 10000; end if; end; when 'M' then begin select count(company_id) from ab_moral_person where ab_moral_person.company_id = id into x; if (x != 1) then set msg= concat('No such moral person, PK:', id); SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = msg, MYSQL_ERRNO = 10000; end if; end; else begin set msg= concat('No such person type:', person_type); SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = msg, MYSQL_ERRNO = 20000; end; end case; end $$ create procedure check_pk_inventory(in id integer) begin declare x integer; declare msg varchar(128); select count(item_id) from in_inventory where in_inventory.item_id = id into x; if (x != 1) then set msg= concat('Failed integrity constraint, table in_inventory, PK:', id); SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = msg, MYSQL_ERRNO = 10000; end if; end $$ create procedure check_pk_order(in id integer) begin declare x integer; declare msg varchar(128); select count(po_id) from po_order where po_order.po_id = id into x; if (x != 1) then set msg= concat('Failed integrity constraint, table po_order, PK:', id); SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = msg, MYSQL_ERRNO = 10000; end if; end $$ create trigger po_order_bi before insert on po_order for each row begin call check_pk_person(NEW.cust_type, NEW.cust_id); end $$ create trigger po_order_bu before update on po_order for each row begin call check_pk_person(NEW.cust_type, NEW.cust_id); end $$ create trigger po_order_line_bi before insert on po_order_line for each row begin call check_pk_order(NEW.po_id); call check_pk_inventory(NEW.item_id); end $$ create trigger po_order_line_bu before update on po_order_line for each row begin call check_pk_order(NEW.po_id); call check_pk_inventory(NEW.item_id); end $$ --echo # --echo # Application helpers --echo # create procedure po_create_order( in p_cust_type char, in p_cust_id integer, out id integer) begin insert into po_order set cust_type = p_cust_type, cust_id = p_cust_id; set id = last_insert_id(); end $$ create procedure po_add_order_line( in po integer, in line integer, in item integer, in q integer) begin insert into po_order_line set po_id = po, line_no = line, item_id = item, qty = q; end $$ delimiter ;$$ --echo # --echo # Create sample data --echo # insert into ab_physical_person values ( 1, "John", "A", "Doe"), ( 2, "Marry", "B", "Smith") ; insert into ab_moral_person values ( 3, "ACME real estate, INC"), ( 4, "Local school") ; insert into in_inventory values ( 100, "Table, dinner", 5), ( 101, "Chair", 20), ( 200, "Table, coffee", 3), ( 300, "School table", 25), ( 301, "School chairs", 50) ; select * from ab_physical_person order by person_id; select * from ab_moral_person order by company_id; select * from in_inventory order by item_id; --echo # --echo # Entering an order --echo # set @my_po = 0; /* John Doe wants 1 table and 4 chairs */ call po_create_order("P", 1, @my_po); call po_add_order_line (@my_po, 1, 100, 1); call po_add_order_line (@my_po, 2, 101, 4); /* Marry Smith wants a coffee table */ call po_create_order("P", 2, @my_po); call po_add_order_line (@my_po, 1, 200, 1); --echo # --echo # Entering bad data in an order --echo # # There is no item 999 in in_inventory --error 10000 call po_add_order_line (@my_po, 1, 999, 1); --echo # --echo # Entering bad data in an unknown order --echo # # There is no order 99 in po_order --error 10000 call po_add_order_line (99, 1, 100, 1); --echo # --echo # Entering an order for an unknown company --echo # # There is no moral person of id 7 --error 10000 call po_create_order("M", 7, @my_po); --echo # --echo # Entering an order for an unknown person type --echo # # There is no person of type X --error 20000 call po_create_order("X", 1, @my_po); /* The local school wants 10 class tables and 20 chairs */ call po_create_order("M", 4, @my_po); call po_add_order_line (@my_po, 1, 300, 10); call po_add_order_line (@my_po, 2, 301, 20); # Raw data select * from po_order; select * from po_order_line; # Creative reporting ... select po_id as "PO#", ( case cust_type when "P" then concat (pp.first_name, " ", pp.middle_initial, " ", pp.last_name) when "M" then mp.name end ) as "Sold to" from po_order po left join ab_physical_person pp on po.cust_id = pp.person_id left join ab_moral_person mp on po.cust_id = company_id ; select po_id as "PO#", ol.line_no as "Line", ol.item_id as "Item", inv.descr as "Description", ol.qty as "Quantity" from po_order_line ol, in_inventory inv where inv.item_id = ol.item_id order by ol.item_id, ol.line_no; drop database demo;