home *** CD-ROM | disk | FTP | other *** search
/ PC World 1998 October / PCWorld_1998-10_cd.bin / software / prehled / komix / DATA.Z / genproc.tcl < prev    next >
Text File  |  1996-06-05  |  14KB  |  487 lines

  1. #---------------------------------------------------------------------------
  2. #
  3. # Copyright (c) 1992-1995 by Cadre Technologies Inc.
  4. #
  5. # This software is furnished under a license and may be used only in
  6. # accordance with the terms of such license and with the inclusion of
  7. # the above copyright notice. This software or any other copies thereof
  8. # may not be provided or otherwise made available to any other person.
  9. # No title to and ownership of the software is hereby transferred.
  10. #
  11. # The information in this software is subject to change without notice
  12. # and should not be construed as a commitment by Cadre Technologies Inc.
  13. #
  14. #---------------------------------------------------------------------------
  15. #
  16. #    File        : @(#)genproc.tcl    2.1
  17. #    Original date    : July 1992
  18. #    Description    : Implementation / configuration of Informix R.I.
  19. #                         rules. These are used in the database procedures.
  20. #
  21. #---------------------------------------------------------------------------
  22. #
  23. # @(#)genproc.tcl    2.1 19 Apr 1996 Copyright 1992-1995 Cadre Technologies Inc.
  24. #
  25. #---------------------------------------------------------------------------
  26.  
  27. #---------------------------------------------------------------------------
  28. # Configuartion arrays. Each array is indexed with "operation,rule" where
  29. # operation is one of (ins, del, up) and rule is the rule as returned
  30. # by get_*_rule
  31.  
  32. #---------------------------------------------------------------------------
  33. # R.I. rules to check before the operation is performed. These are the checks
  34. # that are not enforced by the Informix R.I. mechanism. The following arrays
  35. # are defined:
  36. #    imp_ri_rules_before    rules to be checked for each import
  37. #    exp_ri_rules_before    rules to be checked for each export
  38.  
  39. #---------------------------------------------------------------------------
  40. # import rules
  41. global imp_ri_rules_before
  42. set imp_ri_rules_before(del,rej_exist)        rej_exist_del
  43. set imp_ri_rules_before(upd,rej_exist)        rej_exist_upd
  44. #set imp_ri_rules_before(del,del_in_master)    del_in_master
  45. set imp_ri_rules_before(del,rej_last)        rej_last
  46. set imp_ri_rules_before(del,rej_last_in_master)    rej_exist_del
  47.  
  48. #---------------------------------------------------------------------------
  49. # export rules
  50. # (none)
  51.  
  52. #---------------------------------------------------------------------------
  53. # R.I. rules to check and possibly repair the R.I. after an Informix R.I.
  54. # violation has been detected. The following arrays are defined:
  55. #
  56. #    imp_ri_rules_after    rules to be checked for each import
  57. #    exp_ri_rules_after    rules to be checked for each export
  58.  
  59.  
  60. #---------------------------------------------------------------------------
  61. # import rules
  62. global imp_ri_rules_after
  63. set imp_ri_rules_after(ins,ins_in_master)    ins_in_master
  64. set imp_ri_rules_after(upd,ins_in_master)    ins_in_master
  65.  
  66. #---------------------------------------------------------------------------
  67. # export rules
  68. global exp_ri_rules_after
  69. set exp_ri_rules_after(del,del_in_detail)    del_in_detail
  70. set exp_ri_rules_after(del,nullify_detail)    nullify_detail
  71.  
  72. #---------------------------------------------------------------------------
  73. # Rules that are not implemented
  74. #
  75. global exp_not_implemented
  76. set exp_not_implemented(ins,rej_not_exist)    1
  77. set exp_not_implemented(upd,rej_not_exist)    1
  78. global imp_not_implemented
  79.  
  80.  
  81. #---------------------------------------------------------------------------
  82. #
  83. #                 PROCEDURES THAT IMPLEMENT THE R.I. RULES
  84. #
  85. #---------------------------------------------------------------------------
  86.  
  87. # Determine if the complete tuple is needed for a delete (only the key is
  88. # specified).
  89. #
  90. proc tuple_needed_for_del {table} {
  91.  
  92.     if { ![lempty [get_col_list $table NONKEYS]] } {
  93.     #
  94.     # Check all the (import) rules.
  95.     # Export rules never need the complete tuple
  96.     #
  97.  
  98.     foreach link [$table importSet] {
  99.         case [$link getDelType] in {
  100.         {rej_exist rej_last rej_last_in_master del_in_master} {
  101.             return 1
  102.         }
  103.         }
  104.     }
  105.     }
  106.     # no reasons found for a complete tuple
  107.     return 0
  108. }
  109.  
  110.  
  111. # Generate code to get the complete tuple for a delete, if that is
  112. # necessary
  113. #
  114. proc gen_get_tuple_for_del {section table} {
  115.     if {![tuple_needed_for_del $table]} {
  116.     return
  117.     }
  118.     expand_text $section {
  119.     ~[gen_simple_data_decl_4gl $section $table NONKEYS "DEFINE p_" "" ";\n"]
  120.     SELECT ~[gen_col_list $section $table NONKEYS]
  121.     INTO ~[gen_col_list $section $table NONKEYS "p_"]
  122.     FROM ~[$table getUniqueName]
  123.     WHERE ~[gen_compare $section $table KEYS "" "" "p_" "" " AND\n"];
  124.     }
  125. }
  126.  
  127.  
  128. # Reject operation if imported key exists in master
  129. # This is the case if the imported key is not NULL, the existance of
  130. # the foreign key is guaranteed by Informix.
  131. # This procedure does the real work for both rej_exist_del and rej_exist_upd
  132. #
  133. proc rej_exist_master {section link prefix} {
  134.     expand_text $section {
  135.     IF ~[gen_col_listc $current_section [$link columnSet] $prefix \
  136.          " IS NOT NULL" " AND\n"] THEN
  137.         RAISE EXCEPTION 111, 0, "Referential integrity violated";
  138.     END IF;
  139.     }
  140. }
  141.  
  142. # Reject delete operation if imported key exists in master
  143. #
  144. proc rej_exist_del {section link} {
  145.     rej_exist_master $section $link p_
  146. }
  147.  
  148. # Reject update operation if imported key exists in master
  149. #
  150. proc rej_exist_upd {section link} {
  151.     rej_exist_master $section $link o_
  152. }
  153.  
  154. # Determine if del_in_detail can be performed by a query. This is only the
  155. # case if all the RI rules are enforced by Informix. Otherwise the del_in_detail
  156. # must be performed by calling the appropriate stored procedure
  157. #
  158. proc query_for_del_detail {link} {
  159.     set detail [$link detail]
  160.     foreach link [$detail importSet] {
  161.     if {[$link getDelType] != "none"} {
  162.         return 0
  163.     }
  164.     }
  165.     foreach link [$detail exportSet] {
  166.     set rule [$link getDelType]
  167.     if {$rule != "none" && $rule != "rej_exist"} {
  168.         return 0
  169.     }
  170.     }
  171.     return 1
  172. }
  173.  
  174.  
  175. # Delete in detail by calling the appropriate stored procedure
  176. #
  177. proc del_by_proc {section link} {
  178.     set columns [$link columnSet]
  179.     set detail [$link detail]
  180.     expand_text $section {
  181.     BEGIN
  182.         ~[gen_simple_data_decl_4gl $section $detail KEYS "DEFINE l_" ";\n"];
  183.         FOREACH SELECT ~[gen_col_list $section $detail KEYS]
  184.             INTO   ~[gen_col_list $section $detail KEYS "l_"]
  185.             FROM   ~[$detail getUniqueName]
  186.             WHERE  ~[gen_compare_cl $section $link "" "" "p_" "" \
  187.                 " AND\n"]
  188.         CALL pdel_~[$detail getUniqueName](~[
  189.                 gen_col_list $section $detail KEYS "l_"]);
  190.         END FOREACH
  191.     END
  192.     }
  193. }
  194.  
  195.  
  196. # delete tuples with the exported key from the detail table
  197. #
  198. proc del_in_detail {section link} {
  199.     if [query_for_del_detail $link] {
  200.     expand_text $section {
  201.         DELETE
  202.         FROM ~[[$link detail] getUniqueName]
  203.         WHERE ~[gen_compare_cl $section $link "" "" "p_" "" \
  204.            " AND\n"];
  205.     }
  206.     } else {
  207.     del_by_proc $section $link
  208.     }
  209. }
  210.  
  211.  
  212. # delete tuples with the imported key from the master table
  213. #
  214. proc del_in_master {section link} {
  215.     expand_text $section {
  216.     DELETE
  217.     FROM ~[[$link master] getUniqueName]
  218.     WHERE ~[gen_compare_cl $section $link "p_" "" "" "" \
  219.            " AND\n"];
  220.     }
  221. }
  222.  
  223.  
  224. # Reject delete on last imported key in current table
  225. #
  226. proc rej_last {section link} {
  227.     expand_text $section {
  228.         BEGIN
  229.             DEFINE counter INT;
  230.  
  231.             SELECT COUNT(*)
  232.             INTO counter
  233.             FROM ~[[$link detail] getUniqueName]
  234.             WHERE ~[gen_compare_cl $section $link "" "" "p_" "" " AND\n"];
  235.             IF counter = 1 THEN
  236.             RAISE EXCEPTION 111, 0, "Referential integrity violated";
  237.             END IF;
  238.         END
  239.     }
  240. }
  241.  
  242.  
  243. # Update the value of a column that is "serial"
  244. #
  245. proc update_serial {section link} {
  246.     foreach col [$link columnSet] {
  247.     if [is_serial [get_type_4gl $col]] {
  248.         set name [$col getUniqueName]
  249.         expand_text $section {
  250.         IF p_~$name == 0 THEN
  251.             SELECT MAX(~$name)
  252.             INTO p_~$name
  253.             FROM ~[[$link master] getUniqueName];
  254.         END IF;
  255.         }
  256.         return;
  257.     }
  258.     }
  259. }
  260.  
  261. #
  262. #    Generate parameters for a CALL in an EXCEPTION block
  263. #
  264.  
  265. proc gen_par_list {link} {
  266.  
  267.     set parlist ""
  268.     set implist [gen_sorted_columns $link]
  269.  
  270.     foreach imp $implist {
  271.     if {$parlist == ""} {
  272.         set parlist "p_[$imp getUniqueName]"
  273.     } else {
  274.         set parlist "$parlist, p_[$imp getUniqueName]"
  275.     }
  276.     }
  277.  
  278.     set mastertable [$link master]
  279.     set mastercolumns [get_col_list $mastertable "NONKEYS"]
  280.  
  281.     foreach master $mastercolumns {
  282.     set parlist "$parlist, NULL"
  283.     }
  284.  
  285.     return $parlist
  286. }
  287.  
  288. #
  289. # Insert imported key in master table
  290. #
  291.  
  292. proc ins_in_master {section link} {
  293.  
  294.     expand_text $section {
  295.         BEGIN
  296.             DEFINE counter INT;
  297.  
  298.             SELECT COUNT(*)
  299.             INTO counter
  300.             FROM ~[[$link master] getUniqueName]
  301.             WHERE ~[gen_compare_cl $section $link "p_" "" "" "" " AND\n"];
  302.             IF counter = 0 THEN
  303.                 CALL pins_~[[$link master] getUniqueName](~[gen_par_list $link]);
  304.             END IF;
  305.         END
  306.     }
  307.  
  308.     update_serial $section $link
  309. }
  310.  
  311. #
  312. # Update exported key in detail table
  313. #
  314.  
  315. proc upd_in_detail {section link} {
  316.     $section pushIndent
  317.     set columns [$link columnSet]
  318.     expand_text $section {
  319.     UPDATE ~[[$link detail] getUniqueName]
  320.     SET ~[gen_comparec $section $columns "" "" "p_" "" ",\n"]
  321.     WHERE ~[gen_comparec $section $columns "" "" "p_" "" " AND\n"];
  322.     }
  323.     $section popIndent
  324. }
  325.  
  326.  
  327. # Nullify exported key in detail table
  328. #
  329. proc nullify_detail {section link} {
  330.     set columns [$link columnSet]
  331.     expand_text $section {
  332.     UPDATE ~[[$link detail] getUniqueName]
  333.     SET ~[gen_col_listl $section [$link friendLink] "" " = NULL"]
  334.     WHERE ~[gen_compare_cl $section $link "" "" "p_" "" " AND\n"];
  335.     }
  336. }
  337.  
  338. # Check which procedures cannot be implemented.
  339. # Put those procedures in the array 'not_possible', indexed by operation
  340. # and table-handle.
  341. #
  342. proc check_not_implemented {model np} {
  343.     upvar $np not_possible
  344.     set operations {ins del upd}
  345.     foreach table [$model tableSet] {
  346.     foreach link [$table importSet] {
  347.         foreach oper $operations {
  348.         if [get imp_not_implemented($oper,[get_${oper}_type $link]) 0] {
  349.             set not_possible($oper,$table) 1
  350.             m4_warning $W_NOT_IMPLEMENTED p${oper}_[$table getUniqueName]
  351.         }
  352.         }
  353.     }
  354.     foreach link [$table exportSet] {
  355.         foreach oper $operations {
  356.         if [get exp_not_implemented($oper,[get_${oper}_type $link]) 0] {
  357.             set not_possible($oper,$table) 1
  358.             m4_warning $W_NOT_IMPLEMENTED p${oper}_[$table getUniqueName]
  359.         }
  360.         }
  361.     }
  362.     }
  363. }
  364.  
  365. # this procedure does the real work for gen_rules_{before,after}
  366. #
  367. proc gen_ri_rules {section table when oper} {
  368.     foreach link [$table importSet] {
  369.     set procrule [get imp_ri_rules_${when}($oper,[get_${oper}_type $link])]
  370.     if {$procrule != ""} {
  371.         $procrule $section $link
  372.     }
  373.     }
  374.     foreach link [$table exportSet] {
  375.     set procrule [get exp_ri_rules_${when}($oper,[get_${oper}_type $link])]
  376.     if {$procrule != ""} {
  377.         $procrule $section $link
  378.     }
  379.     }
  380. }
  381.  
  382. # Generate R.I. rules to check before the operation is executed
  383. #
  384. proc gen_rules_before {section table oper} {
  385.     gen_ri_rules $section $table before $oper
  386. }
  387.  
  388. # Generate R.I. rules to check after the operation has executed and has failed
  389. #
  390. proc gen_rules_after {section table oper query} {
  391.     set tmp [TextSection new]
  392.     gen_ri_rules $tmp $table after $oper
  393.     if {[$tmp lineNr] > 1} {
  394.     # something is generated: create an ON EXCEPTION block
  395.     $section pushIndent
  396.     expand_text $section {
  397.         ON EXCEPTION
  398.             ~[$section pushIndent
  399.             $section appendSect $tmp
  400.             expand_text $section $query currtab $table
  401.             $section popIndent]
  402.         END EXCEPTION;
  403.     }
  404.     $section popIndent
  405.     }
  406. }
  407.  
  408.  
  409. # Generate procedure body
  410. #
  411. proc gen_inf_proc_body {section table oper query} {
  412.     $section pushIndent
  413.     case $oper in {
  414.     {del} {
  415.     gen_get_tuple_for_del $section $table
  416.     }
  417.     {upd} {
  418.     gen_get_tuple_for_upd $section $table
  419.     }}
  420.  
  421. #
  422. #    ON EXCEPTION must be first statement in block,
  423. #    so we just create another block ...
  424. #
  425.  
  426.     expand_text $section {
  427.     BEGIN
  428.         ~[$section pushIndent
  429.         gen_rules_after $section $table $oper $query
  430.         gen_rules_before $section $table $oper
  431.         expand_text $section $query currtab $table
  432.         $section popIndent]
  433.     END
  434.     }
  435. #
  436. #   Special case: del_in_master must be generated AFTER the query, but NOT
  437. #   in the exception block
  438. #
  439.     if {$oper == "del"} {
  440.     gen_del_in_master $section $table
  441.     }
  442.     $section popIndent
  443. }
  444.  
  445.  
  446. # Generate code for "del_in_master", if needed.
  447. #
  448. proc gen_del_in_master {section table} {
  449.     foreach link [$table importSet] {
  450.     if {[$link getDelType] == "del_in_master"} {
  451.         expand_text $section {
  452.         CALL pdel_~[[$link master] getUniqueName](~[
  453.             gen_col_listc $section [$link columnSet] p_]);
  454.         }
  455.     }
  456.     }
  457. }
  458.  
  459.  
  460. # Generate code to get the complete "old" tuple that is updated, if needed.
  461. #
  462. proc gen_get_tuple_for_upd {section table} {
  463.     if {![tuple_needed_for_upd $table]} {
  464.     return
  465.     }
  466.     expand_text $section {
  467.         ~[gen_simple_data_decl_4gl $section $table ALL "DEFINE o_" "" ";\n"]
  468.         SELECT *
  469.         INTO ~[gen_col_list $section $table ALL "o_"]
  470.         FROM ~[$table getUniqueName]
  471.         WHERE ~[gen_compare $section $table KEYS "" "" "p_" "" " AND\n"];
  472.     }
  473. }
  474.  
  475. # Is old tuple needed for an update?
  476. # This is only the case when the old values are needed to check the R.I, i.e.
  477. # if an import rule "rej_exist" exists.
  478. #
  479. proc tuple_needed_for_upd {table} {
  480.     foreach link [$table importSet] {
  481.         if {[$link getUpdType] == "rej_exist"} {
  482.         return 1
  483.     }
  484.     }
  485.     return 0
  486. }
  487.