#! /bin/sh # Start tclsh \ exec @TCLSH@ "$0" "$@" # # Code still has to be documented # #load /usr/local/pgsql/lib/libpgtcl.so package require Pgtcl # # Check for minimum arguments # if {$argc < 2} { puts stderr "" puts stderr "usage: pltcl_loadmod dbname \[options\] file \[...\]" puts stderr "" puts stderr "options:" puts stderr " -host hostname" puts stderr " -port portnumber" puts stderr "" exit 1 } # # Remember database name and initialize options # set dbname [lindex $argv 0] set options "" set errors 0 set opt "" set val "" set i 1 while {$i < $argc} { if {[string compare [string index [lindex $argv $i] 0] "-"] != 0} { break; } set opt [lindex $argv $i] incr i if {$i >= $argc} { puts stderr "no value given for option $opt" incr errors continue } set val [lindex $argv $i] incr i switch -- $opt { -host { append options "-host \"$val\" " } -port { append options "-port $val " } default { puts stderr "unknown option '$opt'" incr errors } } } # # Final syntax check # if {$i >= $argc || $errors > 0} { puts stderr "" puts stderr "usage: pltcl_loadmod dbname \[options\] file \[...\]" puts stderr "" puts stderr "options:" puts stderr " -host hostname" puts stderr " -port portnumber" puts stderr "" exit 1 } proc __PLTcl_loadmod_check_table {conn tabname expnames exptypes} { set attrs [expr [llength $expnames] - 1] set error 0 set found 0 pg_select $conn "select C.relname, A.attname, A.attnum, T.typname \ from pg_catalog.pg_class C, pg_catalog.pg_attribute A, pg_catalog.pg_type T \ where C.relname = '$tabname' \ and A.attrelid = C.oid \ and A.attnum > 0 \ and T.oid = A.atttypid \ order by attnum" tup { incr found set i $tup(attnum) if {$i > $attrs} { puts stderr "Table $tabname has extra field '$tup(attname)'" incr error continue } set xname [lindex $expnames $i] set xtype [lindex $exptypes $i] if {[string compare $tup(attname) $xname] != 0} { puts stderr "Attribute $i of $tabname has wrong name" puts stderr " got '$tup(attname)' expected '$xname'" incr error } if {[string compare $tup(typname) $xtype] != 0} { puts stderr "Attribute $i of $tabname has wrong type" puts stderr " got '$tup(typname)' expected '$xtype'" incr error } } if {$found == 0} { return 0 } if {$found < $attrs} { incr found set miss [lrange $expnames $found end] puts "Table $tabname doesn't have field(s) $miss" incr error } if {$error > 0} { return 2 } return 1 } proc __PLTcl_loadmod_check_tables {conn} { upvar #0 __PLTcl_loadmod_status status set error 0 set names {{} modname modseq modsrc} set types {{} name int2 text} switch [__PLTcl_loadmod_check_table $conn pltcl_modules $names $types] { 0 { set status(create_table_modules) 1 } 1 { set status(create_table_modules) 0 } 2 { puts "Error(s) in table pltcl_modules" incr error } } set names {{} funcname modname} set types {{} name name} switch [__PLTcl_loadmod_check_table $conn pltcl_modfuncs $names $types] { 0 { set status(create_table_modfuncs) 1 } 1 { set status(create_table_modfuncs) 0 } 2 { puts "Error(s) in table pltcl_modfuncs" incr error } } if {$status(create_table_modfuncs) && !$status(create_table_modules)} { puts stderr "Table pltcl_modfuncs doesn't exist but pltcl_modules does" puts stderr "Either both tables must be present or none." incr error } if {$status(create_table_modules) && !$status(create_table_modfuncs)} { puts stderr "Table pltcl_modules doesn't exist but pltcl_modfuncs does" puts stderr "Either both tables must be present or none." incr error } if {$error} { puts stderr "" puts stderr "Abort" exit 1 } if {!$status(create_table_modules)} { __PLTcl_loadmod_read_current $conn } } proc __PLTcl_loadmod_read_current {conn} { upvar #0 __PLTcl_loadmod_status status upvar #0 __PLTcl_loadmod_modsrc modsrc upvar #0 __PLTcl_loadmod_funclist funcs upvar #0 __PLTcl_loadmod_globlist globs set errors 0 set curmodlist "" pg_select $conn "select distinct modname from pltcl_modules" mtup { set mname $mtup(modname); lappend curmodlist $mname } foreach mname $curmodlist { set srctext "" pg_select $conn "select * from pltcl_modules \ where modname = '$mname' \ order by modseq" tup { append srctext $tup(modsrc) } if {[catch { __PLTcl_loadmod_analyze \ "Current $mname" \ $mname \ $srctext new_globals new_functions }]} { incr errors } set modsrc($mname) $srctext set funcs($mname) $new_functions set globs($mname) $new_globals } if {$errors} { puts stderr "" puts stderr "Abort" exit 1 } } proc __PLTcl_loadmod_analyze {modinfo modname srctext v_globals v_functions} { upvar 1 $v_globals new_g upvar 1 $v_functions new_f upvar #0 __PLTcl_loadmod_allfuncs allfuncs upvar #0 __PLTcl_loadmod_allglobs allglobs set errors 0 set old_g [info globals] set old_f [info procs] set new_g "" set new_f "" if {[catch { uplevel #0 "$srctext" } msg]} { puts "$modinfo: $msg" incr errors } set cur_g [info globals] set cur_f [info procs] foreach glob $cur_g { if {[lsearch -exact $old_g $glob] >= 0} { continue } if {[info exists allglobs($glob)]} { puts stderr "$modinfo: Global $glob previously used in module $allglobs($glob)" incr errors } else { set allglobs($glob) $modname } lappend new_g $glob uplevel #0 unset $glob } foreach func $cur_f { if {[lsearch -exact $old_f $func] >= 0} { continue } if {[info exists allfuncs($func)]} { puts stderr "$modinfo: Function $func previously defined in module $allfuncs($func)" incr errors } else { set allfuncs($func) $modname } lappend new_f $func rename $func {} } if {$errors} { return -code error } #puts "globs in $modname: $new_g" #puts "funcs in $modname: $new_f" } proc __PLTcl_loadmod_create_tables {conn} { upvar #0 __PLTcl_loadmod_status status if {$status(create_table_modules)} { if {[catch { set res [pg_exec $conn \ "create table pltcl_modules ( \ modname name, \ modseq int2, \ modsrc text);"] } msg]} { puts stderr "Error creating table pltcl_modules" puts stderr " $msg" exit 1 } if {[catch { set res [pg_exec $conn \ "create index pltcl_modules_i \ on pltcl_modules using btree \ (modname name_ops);"] } msg]} { puts stderr "Error creating index pltcl_modules_i" puts stderr " $msg" exit 1 } puts "Table pltcl_modules created" pg_result $res -clear } if {$status(create_table_modfuncs)} { if {[catch { set res [pg_exec $conn \ "create table pltcl_modfuncs ( \ funcname name, \ modname name);"] } msg]} { puts stderr "Error creating table pltcl_modfuncs" puts stderr " $msg" exit 1 } if {[catch { set res [pg_exec $conn \ "create index pltcl_modfuncs_i \ on pltcl_modfuncs using hash \ (funcname name_ops);"] } msg]} { puts stderr "Error creating index pltcl_modfuncs_i" puts stderr " $msg" exit 1 } puts "Table pltcl_modfuncs created" pg_result $res -clear } } proc __PLTcl_loadmod_read_new {conn} { upvar #0 __PLTcl_loadmod_status status upvar #0 __PLTcl_loadmod_modsrc modsrc upvar #0 __PLTcl_loadmod_funclist funcs upvar #0 __PLTcl_loadmod_globlist globs upvar #0 __PLTcl_loadmod_allfuncs allfuncs upvar #0 __PLTcl_loadmod_allglobs allglobs upvar #0 __PLTcl_loadmod_modlist modlist set errors 0 set new_modlist "" foreach modfile $modlist { set modname [file rootname [file tail $modfile]] if {[catch { set fid [open $modfile "r"] } msg]} { puts stderr $msg incr errors continue } set srctext [read $fid] close $fid if {[info exists modsrc($modname)]} { if {[string compare $modsrc($modname) $srctext] == 0} { puts "Module $modname unchanged - ignored" continue } foreach func $funcs($modname) { unset allfuncs($func) } foreach glob $globs($modname) { unset allglobs($glob) } unset funcs($modname) unset globs($modname) set modsrc($modname) $srctext lappend new_modlist $modname } else { set modsrc($modname) $srctext lappend new_modlist $modname } if {[catch { __PLTcl_loadmod_analyze "New/updated $modname" \ $modname $srctext new_globals new_funcs }]} { incr errors } set funcs($modname) $new_funcs set globs($modname) $new_globals } if {$errors} { puts stderr "" puts stderr "Abort" exit 1 } set modlist $new_modlist } proc __PLTcl_loadmod_load_modules {conn} { upvar #0 __PLTcl_loadmod_modsrc modsrc upvar #0 __PLTcl_loadmod_funclist funcs upvar #0 __PLTcl_loadmod_modlist modlist set errors 0 foreach modname $modlist { set xname [__PLTcl_loadmod_quote $modname] pg_result [pg_exec $conn "begin;"] -clear pg_result [pg_exec $conn \ "delete from pltcl_modules where modname = '$xname'"] -clear pg_result [pg_exec $conn \ "delete from pltcl_modfuncs where modname = '$xname'"] -clear foreach func $funcs($modname) { set xfunc [__PLTcl_loadmod_quote $func] pg_result [ \ pg_exec $conn "insert into pltcl_modfuncs values ( \ '$xfunc', '$xname')" \ ] -clear } set i 0 set srctext $modsrc($modname) while {[string compare $srctext ""] != 0} { set xpart [string range $srctext 0 3999] set xpart [__PLTcl_loadmod_quote $xpart] set srctext [string range $srctext 4000 end] pg_result [ \ pg_exec $conn "insert into pltcl_modules values ( \ '$xname', $i, '$xpart')" \ ] -clear incr i } pg_result [pg_exec $conn "commit;"] -clear puts "Successfully loaded/updated module $modname" } } proc __PLTcl_loadmod_quote {s} { regsub -all {\\} $s {\\\\} s regsub -all {'} $s {''} s return $s } set __PLTcl_loadmod_modlist [lrange $argv $i end] set __PLTcl_loadmod_modsrc(dummy) "" set __PLTcl_loadmod_funclist(dummy) "" set __PLTcl_loadmod_globlist(dummy) "" set __PLTcl_loadmod_allfuncs(dummy) "" set __PLTcl_loadmod_allglobs(dummy) "" unset __PLTcl_loadmod_modsrc(dummy) unset __PLTcl_loadmod_funclist(dummy) unset __PLTcl_loadmod_globlist(dummy) unset __PLTcl_loadmod_allfuncs(dummy) unset __PLTcl_loadmod_allglobs(dummy) puts "" set __PLTcl_loadmod_conn [eval pg_connect $dbname $options] unset i dbname options errors opt val __PLTcl_loadmod_check_tables $__PLTcl_loadmod_conn __PLTcl_loadmod_read_new $__PLTcl_loadmod_conn __PLTcl_loadmod_create_tables $__PLTcl_loadmod_conn __PLTcl_loadmod_load_modules $__PLTcl_loadmod_conn pg_disconnect $__PLTcl_loadmod_conn puts ""