#!/usr/local/bin/tcl
#
#
# Prototype tcl script to get table defs out of Sybase and make rough
# format definitions at the same time!
# format files are of the form:
# 4.0
# 7
# 1       SYBCHAR 0       13      "\t"    1 acfusu
# 2       SYBCHAR 0       8       "\t"    2 funds
# 3       SYBCHAR 0       0       "\t"    3 funds_stamp
# 4       SYBCHAR 0       8       "\t"    4 lien
# 5       SYBCHAR 0       0       "\t"    5 lien_stamp
# 6       SYBCHAR 0       8       "\t"    6 expend
# 7       SYBCHAR 0       0       "\n"    7 expend_stamp
#
#			^^^^ but this length is the either/or length for
#			reading field data in:  either read N chars or
#			stop at the terminator.  so when you are sure
#			that your terminator is there, you can just make
#			this large -- so we do
#
#	then we want bcp command lines of the form
#	bcp $base.$owner.$table out Filename -f formatfile -e errorfile -Uuser
#		-P password
#	in another file, heavily protected.
#
#	we're expanding this gradually to do more things, like get the prot
#	codes for each table
#
#	TODO:   get the primary keys
#		get the text of triggers and procs, and their protections too
#
loadlibindex /usr/local/tcl/local/ucosyb.tlib
#
#
#------------------------------MAIN--------------------------------------
#
	set base "[lindex $argv 0]"

	echo "Please tell me your Sybase username:"
	gets stdin user
	echo "Please enter your Sybase password:"
	gets stdin pass

	global sybmsg
	global dbpipe1
	global init
	global debugs
	
	set debugs {}

	set tables ""
	set server "YOURSERVER"

	set dbpipe1 [sybOpen $base $user $pass $server]
	set dbpipe2 [sybOpen $base $user $pass $server]

#	Output file names
	set extfile $base/Extract.$base.csh
	set impfile $base/Reload.$base.csh
	set tblfile $base/$base.tbl
	set prifile $base/$base.priv
	set trgfile $base/$base.trg
	set profile $base/$base.pro
	set vuwfile $base/$base.vuw
	set rulfile $base/$base.rul
	set deffile $base/$base.def
	set keyfile $base/$base.keys
#
	set tblf [open $tblfile w]
	set bcpof [open $extfile w]
	set bcpif [open $impfile w]
	set privf [open $prifile w]
#
	set trigf [open $trgfile w]
	set procf [open $profile w]
	set viewf [open $vuwfile w]
	set rulef [open $rulfile w]
	set dfltf [open $deffile w]
	set keyf  [open $keyfile w]

#	put the preamble in your output files first 

	set preamble "#!/bin/csh\n#\n# BCP script for extracting data from Sybase database $base"
	puts $bcpof $preamble
	set preamble "#!/bin/csh\n#\n# BCP script for reloading data into Sybase database $base"
	puts $bcpif $preamble
#
	set preamble "use $base\ngo\n\n"
	puts $tblf $preamble
	puts $privf $preamble
	puts $trigf $preamble
	puts $procf $preamble
	puts $viewf $preamble
	puts $rulef $preamble
	puts $dfltf $preamble
#
#	First deal with the tables

	echo "dealing with the user tables..."

#	get 2 fields:  owner name and table name from sysobjects
	set sqlcmd "select name,user_name(uid),id from sysobjects where type = 'U' order by name,user_name(uid)"
	set table sysobjects
	doSQL 1
	while {1 == 1} {

		set cnames {}
		set line [sybNext 1]
#		echo "INPUT	$line"
		if {$line == ""} {break}

		set owner [lindex $line 1]
		set tname [lindex $line 0]
		set objid [lindex $line 2]
		set fname [format "%s.%s" $owner $tname]
		set ofile [open $base/Make.$fname w]
		puts $ofile $preamble

		set sqlcmd "sp_help '$fname'"
		set table $tname

		if {[lsearch $tables $tname] >= 0} {
			set comment "/* WARNING, WARNING		*/\n/*   This table name is already in use in this database!*/"
			puts $tblf $comment
		}
		lappend tables $tname

		doSQL 2

		set comment "/* table was $base.$fname 			*/"
		puts $tblf $comment
		puts $ofile $comment
		set start "drop table $tname\ngo\ncreate table $tname ("
		puts $tblf $start
		puts $ofile $start

		set fmtf [open $base/$fname.fmt w]

	while {3 == 3} {

		set line [sybNext 2]
#		echo "	INPUT2	$line"
		if {$sybmsg(nextrow) == "NO_MORE_RESULTS"} {break}

#	If the first word is name then toss a line and take the table name
#	from the first word of the 2nd line

#	If the first word is Column_name then eat one line and start
#	looking for columns:  the first line that does not start with
#	a space is the end of this column list so terminate the table
#	definition.  Put the columns and their characteristics in 
#	an array and we'll write it out at the end.
		if {[lindex $line 0] == "default"} {
			set line [sybNext 2]
			set i 1
			while {4 == 4} {
				set line [sybNext 2]
				if {$line == ""} {break}
				set cname [lindex $line 0]
				lappend cnames $cname
				set ctype [lindex $line 1]
				set csize [lindex $line 2]
				set cnull [lindex $line 3]
				if {$cnull == 0} {
				set cnull "NOT NULL"
				} else {
				set cnull "NULL"
				}
				set fields($i,name) $cname
				set fields($i,type) $ctype
				set fields($i,size) $csize
				set fields($i,null) $cnull
				incr i
			}
			set record "4.0\n[expr {$i - 1}]"
			puts $fmtf $record
			loop j 1 $i {
				case $fields($j,type) in {
				{char varchar} {
				set record "  $fields($j,name)	$fields($j,type)($fields($j,size))  $fields($j,null)"
				}
				{default} {
				set record "  $fields($j,name)	$fields($j,type)	$fields($j,null)"
				}
				}
				puts $tblf $record nonewline
				puts $ofile $record nonewline
				if {$j < [expr {$i - 1}]} {
					puts $tblf ","
					puts $ofile ","
					set record "$j	SYBCHAR 0	255	\"~\" 	$j $fields($j,name)	"
				} else {
					puts $tblf "\n)\ngo\n"
					puts $ofile "\n)\ngo\n"
					set record "$j	SYBCHAR 0	255	\"\\n\" 	$j $fields($j,name)	"
				}
				puts $fmtf $record
			}

		}
			
	}
	close $fmtf
#
#	Having got the format info, let's find out all about the protections
#
	set pritypes(205) grant
	set pritypes(206) revoke
	set prep(205) to
	set prep(206) from
	set acts(193) select
	set acts(195) insert
	set acts(196) delete
	set acts(197) update

	set sqlcmd "select user_name(uid),action,protecttype,columns from sysprotects where id = $objid"
	set table "sysprotects"
	doSQL 2

	while {4 == 4} {

		set line [sybNext 2]
		if {$line == ""} {break}

		assign_fields $line uname action ptype cols

#		echo "$uname has $acts($action) on $tname $pritypes($ptype) cols $cols"

#		if cols = "01" then this means all columns.  otherwise it
#		gets really nasty and we have to go find which cols.
#		which we won't worry about just now

		if {($cols == "01") || ($cols == "")} {
#		all is well
		puts $privf "$pritypes($ptype) $acts($action) on $tname $prep($ptype) $uname\ngo\n"
		puts $ofile "$pritypes($ptype) $acts($action) on $tname $prep($ptype) $uname\ngo\n"
		} else {
#		all is not well
		echo "columns code for $tname for $user  $pritypes($ptype) $acts($action):  $cols"
		echo "I don't yet know what to do about that."
		}
	}

#	And knowing about the protections, let's find out about the
#	Primary Key(s):

	set sqlcmd "select key1,key2,key3,key4,key5,key6,key7,key8 from syskeys where id = $objid"
	set table syskeys
	doSQL 2

	set keys {}
	while {5 == 5} {

		set line [sybNext 2]
		if {$line == ""} {break}

#	This gets us column numbers, which we have to translate into names

		puts $keyf "sp_primarykey $tname, " nonewline
		puts $ofile "sp_primarykey $tname, " nonewline

		foreach k $line {
			if {$k} {
			set key [lindex $cnames [expr {$k - 1}]]
			set keys [format "%s %s," $keys $key]
			}
		}
		set keys [string trimright $keys ,]

		echo "P keys for table $tname are $keys"

		puts $keyf "$keys\ngo\n"
		puts $ofile "$keys\ngo\n"
		
		close $ofile

	}
#	and   keep doing it.
#	bcp $base.$owner.$table out Filename -f $fname.fmt -e errorfile -Uuser
#		-P password
	puts $bcpof "echo \"get $base.$fname\" "
	set record "bcp $base.$fname out $fname.FLAT -f $fname.fmt -e $fname.err -U$user -P$pass"
	puts $bcpof $record
	puts $bcpif "echo \"restore $base.$fname\" "
	set record "bcp $base.$fname in $fname.FLAT  -f $fname.fmt -e $fname.err -U$user -P$pass"
	puts $bcpif $record
	}
	close $bcpif
	close $bcpof
	close $tblf
	exec chmod 700 $extfile
	exec chmod 700 $impfile
#
#
#	And now for the text objects like triggers and procs
#
	
	echo "now it's time to deal with the other types of objects..."

	set otypes(TR) trigger
	set otypes(P) procedure
	set otypes(R) rule
	set otypes(D) default
	set otypes(V) view

	set fid(TR) trigf
	set fid(P) procf
	set fid(R) rulef
	set fid(D) dfltf
	set fid(V) viewf

	set sqlcmd "select type, name,user_name(uid),id from sysobjects where type in ('TR','P','R','D','V') order by type,name,user_name(uid)"

	set table "sysobjects"
	doSQL 1

	while {1 == 1} {

		set line [sybNext 1]
		if {$line == ""} {break}

		assign_fields $line otype oname uname oid

		set fh $fid($otype)
		set type $otypes($otype)

		eval puts [set $fh] \"drop $type $uname.$oname\ngo\"

		set sqlcmd "select text from syscomments where id = $oid and texttype = 0 order by colid"
		set table syscomments
		doSQL 2

  		set plist ""
		set trunc 0
		set gluem 0
		while {2 == 2} {

			set line [lindex [sybNext 2] 0]
			if {$line == ""} {break}

#			echo "orig line is \n$line"

			set lastch [expr {[clength $line] - 1}]
			set lastnl [string last \n $line]

#			echo "last char is $lastch and last NL is $lastnl"
			if {$lastch != $lastnl} {
				set trunc 1
#				echo "truncated line at end of segment"
			} else {
				set trunc 0
			}

			foreach tline [split $line \n] {
				if {$gluem} {
					set elem [format "%s%s" [string trimright $orphan \}] [string trimleft $tline \{] ]
					lappend plist "$elem"
					set gluem 0
#					echo "truncated line glued to new line 1"
				} else {
					set elem "$tline"
					lappend plist "$elem"
				}

			}

			set lastel [expr {[llength $plist] -1}]

			if {$trunc} {
				set orphan [lrange $plist $lastel $lastel]
				set gluem 1
				set plist [lreplace $plist $lastel $lastel]
#				echo "truncated line $orphan saved..."
			}

		}

		foreach line $plist {
#		echo "line of comment text is \n$line"
		set err [regsub -all \" $line "'" temp]
#		echo "line of comment text is now \n$line"
		if {$err} {
		set out "[string trimleft [string trimright $temp \} ] \{ ]"
		} else {
		set out "[string trimleft [string trimright $line \} ] \{ ]"
		}
		eval puts [set $fh] \"$out\"

		}
		eval puts [set $fh] \"go\"
		eval puts [set $fh] \"\"
	}

	close $trigf 
	close $procf 
	close $viewf 
	close $rulef 
	close $dfltf 
	close $privf
	close $keyf
#
	echo "Warning!!! the csh script which does the extract contains your"
	echo "Sybase password.  I have protected it chmod 700 so that others"
	echo "may not read it.  Be sure to keep it protected!"
	

