#!/bin/sh # Find an order of loading tables with referential dependencies # Uses dbaccess, awk, sh. # Note: name of awk may have to be adjusted for your system (nawk). # Algorithm of depth first sort of directed graph # from AWK by Aho,Kernigan, W... # usage () { echo echo 'Usage: '$0 '[order|child|parent] database [table]' echo 'Example: '$0 'order project2' echo 'Example: '$0 'child project2 organization' echo 'Example: '$0 'parent project2 organization' echo '"order" lists a loading order of tables to comply with constraints.' echo '"child" lists children of a table in a loadable order.' echo '"parent" lists parents of a table in a loadable order.' echo exit 1 } # Query for finding referential dependency pairs. # Usage: $0 database_name ref_sql () { DB=$1 # remove blanks, duplicates $INFORMIXDIR/bin/dbaccess << EOF | sed '/^$/d' database $DB; output to pipe "sort -u" without headings select a.tabname, b.tabname from "informix".sysreferences r, "informix".systables a, "informix".systables b, "informix".sysconstraints c where r.ptabid = a.tabid and c.tabid = b.tabid and c.constrid = r.constrid; EOF } # Depth first sort of directed graph. # input: pairs of names representing dependency: name name # use in pipeline: ref_sql| ref_parent NAME| rtsort| reverse # output in reversed order. rtsort () { nawk ' { if (!($1 in pcnt)) pcnt[$1] = 0 pcnt[$2]++ slist[$1, ++scnt[$1]] = $2 } END { for (node in pcnt) { nodecnt++ if (pcnt[node] == 0) rtsort(node) } if (pncnt != nodecnt) { print "error: input contains a cycle" print "node: ",node } printf("\n") } function rtsort(node, i, s) { visited[node] = 1 for ( i = 1; i <= scnt[node]; i++) if (visited[s = slist[node, i]] == 0) rtsort(s) else if (visited[s] == 1) printf("error: nodes %s and %s are in a cycle\n",s,node) visited[node] = 2 printf("%s\n", node) pncnt++ }' } # Reverse order of lines input. reverse () { nawk '{ x[NR] = $0} END { for (i = NR; i > 0; i--) print x[i] }' } # input list of referential dependency pairs # usage: ref_parent # output: tree of parent dependency pairs on name # use in pipeline: ref_sql | ref_parent NAME | rtsort | reverse # Note: recursive function tree_parent, will explode # harmlessly with circular graph. # use rtsort to depth first search the dependency tree, sort # and find cycles. # Usage: ref_parent ' ref_parent () { # In BEGIN is trick to set variables. nawk 'BEGIN { start_node=ARGV[2]; ARGC = ARGC-1;} { nlist[++cnt] = $0; } END { tree_parent(start_node) } function tree_parent(newnode) { for ( node in nlist) { split(nlist[node],refs); if (newnode == refs[2]){ print nlist[node]; tree_parent(refs[1]); } } }' - $1 } # input list of referential dependency pairs # usage: ref_child # output: tree of child dependency on name # use in pipeline: ref_sql | ref_child NAME | rtsort | reverse # Note: recursive function tree_child, will explode # harmlessly with circular graph. # use rtsort to depth first search the dependency tree, sort # and find cycles. # Usage: ref_child ' ref_child () { test $# -ne 1 && usage # In BEGIN is trick to set variables. nawk 'BEGIN { start_node=ARGV[2]; ARGC = ARGC-1;} {nlist[++cnt] = $0;} END { tree_child(start_node) } function tree_child(newnode) { for ( node in nlist) { split(nlist[node],refs); if (newnode == refs[1]){ print nlist[node]; tree_child(refs[2]); } } }' - $1 } order () { ref_sql $1 | rtsort | reverse } child () { ref_sql $1 | ref_child $2 | rtsort | reverse } parent () { ref_sql $1 | ref_parent $2 | rtsort | reverse } ############################################## # Main case $# in 2|3 ) $1 $2 $3 ;; * ) usage ;; esac