1# 2# Demonstrate how SIGNAL can be used to enforce integrity constraints. 3# 4 5# Naming: 6# - PO: Purchase Order 7# - AB: Address Book 8# - IN: Inventory 9 10# Simplified schema: 11# 12# Relation 1: 13# PO_ORDER (PK: po_id) 1:1 <---> 0:N (FK: po_id) PO_ORDER_LINE 14# 15# Relation 2: 16# IN_INVENTORY (PK: item_id) 1:1 <---> 0:N (FK: item_id) PO_ORDER_LINE 17# 18# Relation 3: 19# +--> 0:1 (PK: person_id) AB_PHYSICAL_PERSON 20# PO_ORDER (FK: cust_id) 1:1 <--| 21# +--> 0:1 (PK: company_id) AB_MORAL_PERSON 22# This is an 'arc' relationship :) 23# 24 25 26--disable_warnings 27drop database if exists demo; 28--enable_warnings 29 30create database demo; 31 32use demo; 33 34create table ab_physical_person ( 35 person_id integer, 36 first_name VARCHAR(50), 37 middle_initial CHAR, 38 last_name VARCHAR(50), 39 primary key (person_id)); 40 41create table ab_moral_person ( 42 company_id integer, 43 name VARCHAR(100), 44 primary key (company_id)); 45 46create table in_inventory ( 47 item_id integer, 48 descr VARCHAR(50), 49 stock integer, 50 primary key (item_id)); 51 52create table po_order ( 53 po_id integer auto_increment, 54 cust_type char, /* arc relationship, see cust_id */ 55 cust_id integer, /* FK to ab_physical_person *OR* ab_moral_person */ 56 primary key (po_id)); 57 58create table po_order_line ( 59 po_id integer, /* FK to po_order.po_id */ 60 line_no integer, 61 item_id integer, /* FK to in_inventory.item_id */ 62 qty integer); 63 64delimiter $$; 65 66--echo # 67--echo # Schema integrity enforcement 68--echo # 69 70create procedure check_pk_person(in person_type char, in id integer) 71begin 72 declare x integer; 73 declare msg varchar(128); 74 75 /* 76 Test integrity constraints for an 'arc' relationship. 77 Based on 'person_type', 'id' points to either a 78 physical person, or a moral person. 79 */ 80 case person_type 81 when 'P' then 82 begin 83 select count(person_id) from ab_physical_person 84 where ab_physical_person.person_id = id 85 into x; 86 87 if (x != 1) 88 then 89 set msg= concat('No such physical person, PK:', id); 90 SIGNAL SQLSTATE '45000' SET 91 MESSAGE_TEXT = msg, 92 MYSQL_ERRNO = 10000; 93 end if; 94 end; 95 96 when 'M' then 97 begin 98 select count(company_id) from ab_moral_person 99 where ab_moral_person.company_id = id 100 into x; 101 102 if (x != 1) 103 then 104 set msg= concat('No such moral person, PK:', id); 105 SIGNAL SQLSTATE '45000' SET 106 MESSAGE_TEXT = msg, 107 MYSQL_ERRNO = 10000; 108 end if; 109 end; 110 111 else 112 begin 113 set msg= concat('No such person type:', person_type); 114 SIGNAL SQLSTATE '45000' SET 115 MESSAGE_TEXT = msg, 116 MYSQL_ERRNO = 20000; 117 end; 118 end case; 119end 120$$ 121 122create procedure check_pk_inventory(in id integer) 123begin 124 declare x integer; 125 declare msg varchar(128); 126 127 select count(item_id) from in_inventory 128 where in_inventory.item_id = id 129 into x; 130 131 if (x != 1) 132 then 133 set msg= concat('Failed integrity constraint, table in_inventory, PK:', 134 id); 135 SIGNAL SQLSTATE '45000' SET 136 MESSAGE_TEXT = msg, 137 MYSQL_ERRNO = 10000; 138 end if; 139end 140$$ 141 142create procedure check_pk_order(in id integer) 143begin 144 declare x integer; 145 declare msg varchar(128); 146 147 select count(po_id) from po_order 148 where po_order.po_id = id 149 into x; 150 151 if (x != 1) 152 then 153 set msg= concat('Failed integrity constraint, table po_order, PK:', id); 154 SIGNAL SQLSTATE '45000' SET 155 MESSAGE_TEXT = msg, 156 MYSQL_ERRNO = 10000; 157 end if; 158end 159$$ 160 161create trigger po_order_bi before insert on po_order 162for each row 163begin 164 call check_pk_person(NEW.cust_type, NEW.cust_id); 165end 166$$ 167 168create trigger po_order_bu before update on po_order 169for each row 170begin 171 call check_pk_person(NEW.cust_type, NEW.cust_id); 172end 173$$ 174 175create trigger po_order_line_bi before insert on po_order_line 176for each row 177begin 178 call check_pk_order(NEW.po_id); 179 call check_pk_inventory(NEW.item_id); 180end 181$$ 182 183create trigger po_order_line_bu before update on po_order_line 184for each row 185begin 186 call check_pk_order(NEW.po_id); 187 call check_pk_inventory(NEW.item_id); 188end 189$$ 190 191--echo # 192--echo # Application helpers 193--echo # 194 195create procedure po_create_order( 196 in p_cust_type char, 197 in p_cust_id integer, 198 out id integer) 199begin 200 insert into po_order set cust_type = p_cust_type, cust_id = p_cust_id; 201 set id = last_insert_id(); 202end 203$$ 204 205create procedure po_add_order_line( 206 in po integer, 207 in line integer, 208 in item integer, 209 in q integer) 210begin 211 insert into po_order_line set 212 po_id = po, line_no = line, item_id = item, qty = q; 213end 214$$ 215 216delimiter ;$$ 217 218--echo # 219--echo # Create sample data 220--echo # 221 222insert into ab_physical_person values 223 ( 1, "John", "A", "Doe"), 224 ( 2, "Marry", "B", "Smith") 225; 226 227insert into ab_moral_person values 228 ( 3, "ACME real estate, INC"), 229 ( 4, "Local school") 230; 231 232insert into in_inventory values 233 ( 100, "Table, dinner", 5), 234 ( 101, "Chair", 20), 235 ( 200, "Table, coffee", 3), 236 ( 300, "School table", 25), 237 ( 301, "School chairs", 50) 238; 239 240select * from ab_physical_person order by person_id; 241select * from ab_moral_person order by company_id; 242select * from in_inventory order by item_id; 243 244--echo # 245--echo # Entering an order 246--echo # 247 248set @my_po = 0; 249 250/* John Doe wants 1 table and 4 chairs */ 251call po_create_order("P", 1, @my_po); 252 253call po_add_order_line (@my_po, 1, 100, 1); 254call po_add_order_line (@my_po, 2, 101, 4); 255 256/* Marry Smith wants a coffee table */ 257call po_create_order("P", 2, @my_po); 258 259call po_add_order_line (@my_po, 1, 200, 1); 260 261--echo # 262--echo # Entering bad data in an order 263--echo # 264 265# There is no item 999 in in_inventory 266--error 10000 267call po_add_order_line (@my_po, 1, 999, 1); 268 269--echo # 270--echo # Entering bad data in an unknown order 271--echo # 272 273# There is no order 99 in po_order 274--error 10000 275call po_add_order_line (99, 1, 100, 1); 276 277--echo # 278--echo # Entering an order for an unknown company 279--echo # 280 281# There is no moral person of id 7 282--error 10000 283call po_create_order("M", 7, @my_po); 284 285--echo # 286--echo # Entering an order for an unknown person type 287--echo # 288 289# There is no person of type X 290--error 20000 291call po_create_order("X", 1, @my_po); 292 293/* The local school wants 10 class tables and 20 chairs */ 294call po_create_order("M", 4, @my_po); 295 296call po_add_order_line (@my_po, 1, 300, 10); 297call po_add_order_line (@my_po, 2, 301, 20); 298 299# Raw data 300select * from po_order; 301select * from po_order_line; 302 303# Creative reporting ... 304 305select po_id as "PO#", 306 ( case cust_type 307 when "P" then concat (pp.first_name, 308 " ", 309 pp.middle_initial, 310 " ", 311 pp.last_name) 312 when "M" then mp.name 313 end ) as "Sold to" 314 from po_order po 315 left join ab_physical_person pp on po.cust_id = pp.person_id 316 left join ab_moral_person mp on po.cust_id = company_id 317; 318 319select po_id as "PO#", 320 ol.line_no as "Line", 321 ol.item_id as "Item", 322 inv.descr as "Description", 323 ol.qty as "Quantity" 324 from po_order_line ol, in_inventory inv 325 where inv.item_id = ol.item_id 326 order by ol.item_id, ol.line_no; 327 328drop database demo; 329 330 331