1#! /bin/sh 2# Start tclsh \ 3exec @TCLSH@ "$0" "$@" 4 5# 6# Code still has to be documented 7# 8 9#load /usr/local/pgsql/lib/libpgtcl.so 10package require Pgtcl 11 12 13# 14# Check for minimum arguments 15# 16if {$argc < 2} { 17 puts stderr "" 18 puts stderr "usage: pltcl_loadmod dbname \[options\] file \[...\]" 19 puts stderr "" 20 puts stderr "options:" 21 puts stderr " -host hostname" 22 puts stderr " -port portnumber" 23 puts stderr "" 24 exit 1 25} 26 27# 28# Remember database name and initialize options 29# 30set dbname [lindex $argv 0] 31set options "" 32set errors 0 33set opt "" 34set val "" 35 36set i 1 37while {$i < $argc} { 38 if {[string compare [string index [lindex $argv $i] 0] "-"] != 0} { 39 break; 40 } 41 42 set opt [lindex $argv $i] 43 incr i 44 if {$i >= $argc} { 45 puts stderr "no value given for option $opt" 46 incr errors 47 continue 48 } 49 set val [lindex $argv $i] 50 incr i 51 52 switch -- $opt { 53 -host { 54 append options "-host \"$val\" " 55 } 56 -port { 57 append options "-port $val " 58 } 59 default { 60 puts stderr "unknown option '$opt'" 61 incr errors 62 } 63 } 64} 65 66# 67# Final syntax check 68# 69if {$i >= $argc || $errors > 0} { 70 puts stderr "" 71 puts stderr "usage: pltcl_loadmod dbname \[options\] file \[...\]" 72 puts stderr "" 73 puts stderr "options:" 74 puts stderr " -host hostname" 75 puts stderr " -port portnumber" 76 puts stderr "" 77 exit 1 78} 79 80 81proc __PLTcl_loadmod_check_table {conn tabname expnames exptypes} { 82 set attrs [expr [llength $expnames] - 1] 83 set error 0 84 set found 0 85 86 pg_select $conn "select C.relname, A.attname, A.attnum, T.typname \ 87 from pg_catalog.pg_class C, pg_catalog.pg_attribute A, pg_catalog.pg_type T \ 88 where C.relname = '$tabname' \ 89 and A.attrelid = C.oid \ 90 and A.attnum > 0 \ 91 and T.oid = A.atttypid \ 92 order by attnum" tup { 93 94 incr found 95 set i $tup(attnum) 96 97 if {$i > $attrs} { 98 puts stderr "Table $tabname has extra field '$tup(attname)'" 99 incr error 100 continue 101 } 102 103 set xname [lindex $expnames $i] 104 set xtype [lindex $exptypes $i] 105 106 if {[string compare $tup(attname) $xname] != 0} { 107 puts stderr "Attribute $i of $tabname has wrong name" 108 puts stderr " got '$tup(attname)' expected '$xname'" 109 incr error 110 } 111 if {[string compare $tup(typname) $xtype] != 0} { 112 puts stderr "Attribute $i of $tabname has wrong type" 113 puts stderr " got '$tup(typname)' expected '$xtype'" 114 incr error 115 } 116 } 117 118 if {$found == 0} { 119 return 0 120 } 121 122 if {$found < $attrs} { 123 incr found 124 set miss [lrange $expnames $found end] 125 puts "Table $tabname doesn't have field(s) $miss" 126 incr error 127 } 128 129 if {$error > 0} { 130 return 2 131 } 132 133 return 1 134} 135 136 137proc __PLTcl_loadmod_check_tables {conn} { 138 upvar #0 __PLTcl_loadmod_status status 139 140 set error 0 141 142 set names {{} modname modseq modsrc} 143 set types {{} name int2 text} 144 145 switch [__PLTcl_loadmod_check_table $conn pltcl_modules $names $types] { 146 0 { 147 set status(create_table_modules) 1 148 } 149 1 { 150 set status(create_table_modules) 0 151 } 152 2 { 153 puts "Error(s) in table pltcl_modules" 154 incr error 155 } 156 } 157 158 set names {{} funcname modname} 159 set types {{} name name} 160 161 switch [__PLTcl_loadmod_check_table $conn pltcl_modfuncs $names $types] { 162 0 { 163 set status(create_table_modfuncs) 1 164 } 165 1 { 166 set status(create_table_modfuncs) 0 167 } 168 2 { 169 puts "Error(s) in table pltcl_modfuncs" 170 incr error 171 } 172 } 173 174 if {$status(create_table_modfuncs) && !$status(create_table_modules)} { 175 puts stderr "Table pltcl_modfuncs doesn't exist but pltcl_modules does" 176 puts stderr "Either both tables must be present or none." 177 incr error 178 } 179 180 if {$status(create_table_modules) && !$status(create_table_modfuncs)} { 181 puts stderr "Table pltcl_modules doesn't exist but pltcl_modfuncs does" 182 puts stderr "Either both tables must be present or none." 183 incr error 184 } 185 186 if {$error} { 187 puts stderr "" 188 puts stderr "Abort" 189 exit 1 190 } 191 192 if {!$status(create_table_modules)} { 193 __PLTcl_loadmod_read_current $conn 194 } 195} 196 197 198proc __PLTcl_loadmod_read_current {conn} { 199 upvar #0 __PLTcl_loadmod_status status 200 upvar #0 __PLTcl_loadmod_modsrc modsrc 201 upvar #0 __PLTcl_loadmod_funclist funcs 202 upvar #0 __PLTcl_loadmod_globlist globs 203 204 set errors 0 205 206 set curmodlist "" 207 pg_select $conn "select distinct modname from pltcl_modules" mtup { 208 set mname $mtup(modname); 209 lappend curmodlist $mname 210 } 211 212 foreach mname $curmodlist { 213 set srctext "" 214 pg_select $conn "select * from pltcl_modules \ 215 where modname = '$mname' \ 216 order by modseq" tup { 217 append srctext $tup(modsrc) 218 } 219 220 if {[catch { 221 __PLTcl_loadmod_analyze \ 222 "Current $mname" \ 223 $mname \ 224 $srctext new_globals new_functions 225 }]} { 226 incr errors 227 } 228 set modsrc($mname) $srctext 229 set funcs($mname) $new_functions 230 set globs($mname) $new_globals 231 } 232 233 if {$errors} { 234 puts stderr "" 235 puts stderr "Abort" 236 exit 1 237 } 238} 239 240 241proc __PLTcl_loadmod_analyze {modinfo modname srctext v_globals v_functions} { 242 upvar 1 $v_globals new_g 243 upvar 1 $v_functions new_f 244 upvar #0 __PLTcl_loadmod_allfuncs allfuncs 245 upvar #0 __PLTcl_loadmod_allglobs allglobs 246 247 set errors 0 248 249 set old_g [info globals] 250 set old_f [info procs] 251 set new_g "" 252 set new_f "" 253 254 if {[catch { 255 uplevel #0 "$srctext" 256 } msg]} { 257 puts "$modinfo: $msg" 258 incr errors 259 } 260 261 set cur_g [info globals] 262 set cur_f [info procs] 263 264 foreach glob $cur_g { 265 if {[lsearch -exact $old_g $glob] >= 0} { 266 continue 267 } 268 if {[info exists allglobs($glob)]} { 269 puts stderr "$modinfo: Global $glob previously used in module $allglobs($glob)" 270 incr errors 271 } else { 272 set allglobs($glob) $modname 273 } 274 lappend new_g $glob 275 uplevel #0 unset $glob 276 } 277 foreach func $cur_f { 278 if {[lsearch -exact $old_f $func] >= 0} { 279 continue 280 } 281 if {[info exists allfuncs($func)]} { 282 puts stderr "$modinfo: Function $func previously defined in module $allfuncs($func)" 283 incr errors 284 } else { 285 set allfuncs($func) $modname 286 } 287 lappend new_f $func 288 rename $func {} 289 } 290 291 if {$errors} { 292 return -code error 293 } 294 #puts "globs in $modname: $new_g" 295 #puts "funcs in $modname: $new_f" 296} 297 298 299proc __PLTcl_loadmod_create_tables {conn} { 300 upvar #0 __PLTcl_loadmod_status status 301 302 if {$status(create_table_modules)} { 303 if {[catch { 304 set res [pg_exec $conn \ 305 "create table pltcl_modules ( \ 306 modname name, \ 307 modseq int2, \ 308 modsrc text);"] 309 } msg]} { 310 puts stderr "Error creating table pltcl_modules" 311 puts stderr " $msg" 312 exit 1 313 } 314 if {[catch { 315 set res [pg_exec $conn \ 316 "create index pltcl_modules_i \ 317 on pltcl_modules using btree \ 318 (modname name_ops);"] 319 } msg]} { 320 puts stderr "Error creating index pltcl_modules_i" 321 puts stderr " $msg" 322 exit 1 323 } 324 puts "Table pltcl_modules created" 325 pg_result $res -clear 326 } 327 328 if {$status(create_table_modfuncs)} { 329 if {[catch { 330 set res [pg_exec $conn \ 331 "create table pltcl_modfuncs ( \ 332 funcname name, \ 333 modname name);"] 334 } msg]} { 335 puts stderr "Error creating table pltcl_modfuncs" 336 puts stderr " $msg" 337 exit 1 338 } 339 if {[catch { 340 set res [pg_exec $conn \ 341 "create index pltcl_modfuncs_i \ 342 on pltcl_modfuncs using hash \ 343 (funcname name_ops);"] 344 } msg]} { 345 puts stderr "Error creating index pltcl_modfuncs_i" 346 puts stderr " $msg" 347 exit 1 348 } 349 puts "Table pltcl_modfuncs created" 350 pg_result $res -clear 351 } 352} 353 354 355proc __PLTcl_loadmod_read_new {conn} { 356 upvar #0 __PLTcl_loadmod_status status 357 upvar #0 __PLTcl_loadmod_modsrc modsrc 358 upvar #0 __PLTcl_loadmod_funclist funcs 359 upvar #0 __PLTcl_loadmod_globlist globs 360 upvar #0 __PLTcl_loadmod_allfuncs allfuncs 361 upvar #0 __PLTcl_loadmod_allglobs allglobs 362 upvar #0 __PLTcl_loadmod_modlist modlist 363 364 set errors 0 365 366 set new_modlist "" 367 foreach modfile $modlist { 368 set modname [file rootname [file tail $modfile]] 369 if {[catch { 370 set fid [open $modfile "r"] 371 } msg]} { 372 puts stderr $msg 373 incr errors 374 continue 375 } 376 set srctext [read $fid] 377 close $fid 378 379 if {[info exists modsrc($modname)]} { 380 if {[string compare $modsrc($modname) $srctext] == 0} { 381 puts "Module $modname unchanged - ignored" 382 continue 383 } 384 foreach func $funcs($modname) { 385 unset allfuncs($func) 386 } 387 foreach glob $globs($modname) { 388 unset allglobs($glob) 389 } 390 unset funcs($modname) 391 unset globs($modname) 392 set modsrc($modname) $srctext 393 lappend new_modlist $modname 394 } else { 395 set modsrc($modname) $srctext 396 lappend new_modlist $modname 397 } 398 399 if {[catch { 400 __PLTcl_loadmod_analyze "New/updated $modname" \ 401 $modname $srctext new_globals new_funcs 402 }]} { 403 incr errors 404 } 405 406 set funcs($modname) $new_funcs 407 set globs($modname) $new_globals 408 } 409 410 if {$errors} { 411 puts stderr "" 412 puts stderr "Abort" 413 exit 1 414 } 415 416 set modlist $new_modlist 417} 418 419 420proc __PLTcl_loadmod_load_modules {conn} { 421 upvar #0 __PLTcl_loadmod_modsrc modsrc 422 upvar #0 __PLTcl_loadmod_funclist funcs 423 upvar #0 __PLTcl_loadmod_modlist modlist 424 425 set errors 0 426 427 foreach modname $modlist { 428 set xname [__PLTcl_loadmod_quote $modname] 429 430 pg_result [pg_exec $conn "begin;"] -clear 431 432 pg_result [pg_exec $conn \ 433 "delete from pltcl_modules where modname = '$xname'"] -clear 434 pg_result [pg_exec $conn \ 435 "delete from pltcl_modfuncs where modname = '$xname'"] -clear 436 437 foreach func $funcs($modname) { 438 set xfunc [__PLTcl_loadmod_quote $func] 439 pg_result [ \ 440 pg_exec $conn "insert into pltcl_modfuncs values ( \ 441 '$xfunc', '$xname')" \ 442 ] -clear 443 } 444 set i 0 445 set srctext $modsrc($modname) 446 while {[string compare $srctext ""] != 0} { 447 set xpart [string range $srctext 0 3999] 448 set xpart [__PLTcl_loadmod_quote $xpart] 449 set srctext [string range $srctext 4000 end] 450 451 pg_result [ \ 452 pg_exec $conn "insert into pltcl_modules values ( \ 453 '$xname', $i, '$xpart')" \ 454 ] -clear 455 incr i 456 } 457 458 pg_result [pg_exec $conn "commit;"] -clear 459 460 puts "Successfully loaded/updated module $modname" 461 } 462} 463 464 465proc __PLTcl_loadmod_quote {s} { 466 regsub -all {\\} $s {\\\\} s 467 regsub -all {'} $s {''} s 468 return $s 469} 470 471 472set __PLTcl_loadmod_modlist [lrange $argv $i end] 473set __PLTcl_loadmod_modsrc(dummy) "" 474set __PLTcl_loadmod_funclist(dummy) "" 475set __PLTcl_loadmod_globlist(dummy) "" 476set __PLTcl_loadmod_allfuncs(dummy) "" 477set __PLTcl_loadmod_allglobs(dummy) "" 478 479unset __PLTcl_loadmod_modsrc(dummy) 480unset __PLTcl_loadmod_funclist(dummy) 481unset __PLTcl_loadmod_globlist(dummy) 482unset __PLTcl_loadmod_allfuncs(dummy) 483unset __PLTcl_loadmod_allglobs(dummy) 484 485 486puts "" 487 488set __PLTcl_loadmod_conn [eval pg_connect $dbname $options] 489 490unset i dbname options errors opt val 491 492__PLTcl_loadmod_check_tables $__PLTcl_loadmod_conn 493 494__PLTcl_loadmod_read_new $__PLTcl_loadmod_conn 495 496__PLTcl_loadmod_create_tables $__PLTcl_loadmod_conn 497__PLTcl_loadmod_load_modules $__PLTcl_loadmod_conn 498 499pg_disconnect $__PLTcl_loadmod_conn 500 501puts "" 502