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