📄 pgin.tcl
字号:
} # Append format code for binary result: append out [binary format S 1] pgtcl::sendmsg $db F $out set result {} set result_size 0 # Fake up a partial result structure for pgtcl::common_message : set res(error) "" # FunctionCall response. Also handles common messages (notify, notice). while {[set c [pgtcl::readmsg $db]] != "Z"} { if {$c == "V"} { set result_size [pgtcl::get_int32 $db] if {$result_size > 0} { set result [pgtcl::get_bytes $db $result_size] } else { set result "" } } elseif {![pgtcl::common_message $c $db res]} { error "Unexpected reply from database: $c" } } set pgtcl::xstate($db) [pgtcl::get_byte $db] if {$res(error) != ""} { error $res(error) } return $result_size}# === Public prodedures: Function Call ===# Public interface to pgtcl::callfn.proc pg_callfn {db fname result_name arginfo args} { upvar $result_name result return [pgtcl::callfn $db [pgtcl::getfnoid $db $fname] result $arginfo $args]}# Public, simplified interface to pgtcl::callfn when an int32 return value is# expected. Returns the backend function return value.proc pg_callfn_int {db fname arginfo args} { set n [pgtcl::callfn $db [pgtcl::getfnoid $db $fname] result $arginfo $args] if {$n != 4} { error "Unexpected response size ($result_size) to pg function call $fname" } binary scan $result I val return $val}# === Internal procedure to support Large Object ===# Convert a LO mode string into the value of the constants used by libpq.# Note: libpgtcl uses a mode like INV_READ|INV_WRITE for lo_creat, but# r, w, or rw for lo_open (which it translates to INV_READ|INV_WRITE).# This seems like a mistake. The code here accepts either form for either.proc pgtcl::lomode {mode} { set imode 0 if {[string match -nocase *INV_* $mode]} { if {[string match -nocase *INV_READ* $mode]} { set imode 0x40000 } if {[string match -nocase *INV_WRITE* $mode]} { set imode [expr {$imode + 0x20000}] } } else { if {[string match -nocase *r* $mode]} { set imode 0x40000 } if {[string match -nocase *w* $mode]} { set imode [expr {$imode + 0x20000}] } } if {$imode == 0} { error "Invalid large object mode $mode" } return $imode}# === Public prodedures: Large Object ===# Create large object and return OID.# See note regarding mode above at pgtcl::lomode.proc pg_lo_creat {db mode} { if {[catch {pg_callfn_int $db lo_creat I [pgtcl::lomode $mode]} result]} { error "Large Object create failed\n$result" } if {$result == -1} { error "Large Object create failed" } return $result}# Open large object and return large object file descriptor.# See note regarding mode above at pgtcl::lomode.proc pg_lo_open {db loid mode} { if {[catch {pg_callfn_int $db lo_open "I I" $loid [pgtcl::lomode $mode]} \ result]} { error "Large Object open failed\n$result" } if {$result == -1} { error "Large Object open failed" } return $result}# Close large object file descriptor.proc pg_lo_close {db lofd} { if {[catch {pg_callfn_int $db lo_close I $lofd} result]} { error "Large Object close failed\n$result" } return $result}# Delete large object:proc pg_lo_unlink {db loid} { if {[catch {pg_callfn_int $db lo_unlink I $loid} result]} { error "Large Object unlink failed\n$result" } return $result}# Read from large object.# Note: The original PostgreSQL documentation says it returns -1 on error,# which is a bad idea since you can't get to the error message. But it's# probably too late to change it, so we remain bug compatible.proc pg_lo_read {db lofd buf_name maxlen} { upvar $buf_name buf if {[catch {pg_callfn $db loread buf "I I" $lofd $maxlen} result]} { return -1 } return $result}# Write to large object. At most $len bytes are written.# See note above on pg_lo_read error return.proc pg_lo_write {db lofd buf len} { if {[set buflen [string length $buf]] < $len} { set len $buflen } if {[catch {pg_callfn_int $db lowrite "I $len" $lofd $buf} result]} { return -1 } return $result}# Seek to offset inside large object:proc pg_lo_lseek {db lofd offset whence} { switch $whence { SEEK_SET { set iwhence 0 } SEEK_CUR { set iwhence 1 } SEEK_END { set iwhence 2 } default { error "'whence' must be SEEK_SET, SEEK_CUR, or SEEK_END" } } if {[catch {pg_callfn_int $db lo_lseek "I I I" $lofd $offset $iwhence} \ result]} { error "Large Object seek failed\n$result" } return $result}# Return location of file offset in large object:proc pg_lo_tell {db lofd} { if {[catch {pg_callfn_int $db lo_tell I $lofd} result]} { error "Large Object tell offset failed\n$result" } return $result}# Import large object. Wrapper for lo_creat, lo_open, lo_write.# Returns Large Object OID, which should be stored in a table somewhere.proc pg_lo_import {db filename} { if {[catch {open $filename} f]} { error "Large object import of $filename failed\n$f" } fconfigure $f -translation binary set loid [pg_lo_creat $db INV_READ|INV_WRITE] set lofd [pg_lo_open $db $loid w] while {1} { set buf [read $f 32768] if {[set len [string length $buf]] == 0} break if {[pg_lo_write $db $lofd $buf $len] != $len} { error "Large Object import failed to write $len bytes" } } pg_lo_close $db $lofd close $f return $loid}# Export large object. Wrapper for lo_open, lo_read.proc pg_lo_export {db loid filename} { if {[catch {open $filename w} f]} { error "Large object export to $filename failed\n$f" } fconfigure $f -translation binary if {[catch {pg_lo_open $db $loid r} lofd]} { error "Large Object export to $filename failed\n$lofd" } while {[set len [pg_lo_read $db $lofd buf 32768]] > 0} { puts -nonewline $f $buf } pg_lo_close $db $lofd close $f}# === MD5 Checksum procedures for password authentication ===# Coded in Tcl by ljb <lbayuk@mindspring.com>, using these sources:# RFC1321# PostgreSQL: src/backend/libpq/md5.c# If you want a better/faster MD5 implementation, see tcllib.namespace eval md5 { }# Round 1 helper, e.g.:# a = b + ROT_LEFT((a + F(b, c, d) + X[0] + 0xd76aa478), 7)# p1 p2 p1 p3 p4 p5 p6 p7# Where F(x,y,z) = (x & y) | (~x & z)#proc md5::round1 {p1 p2 p3 p4 p5 p6 p7} { set r [expr {$p2 + ($p1 & $p3 | ~$p1 & $p4) + $p5 + $p6}] return [expr {$p1 + ($r << $p7 | (($r >> (32 - $p7)) & ((1 << $p7) - 1)))}]}# Round 2 helper, e.g.:# a = b + ROT_LEFT((a + G(b, c, d) + X[1] + 0xf61e2562), 5)# p1 p2 p1 p3 p4 p5 p6 p7# Where G(x,y,z) = (x & z) | (y & ~z)#proc md5::round2 {p1 p2 p3 p4 p5 p6 p7} { set r [expr {$p2 + ($p1 & $p4 | $p3 & ~$p4) + $p5 + $p6}] return [expr {$p1 + ($r << $p7 | (($r >> (32 - $p7)) & ((1 << $p7) - 1)))}]}# Round 3 helper, e.g.:# a = b + ROT_LEFT((a + H(b, c, d) + X[5] + 0xfffa3942), 4)# p1 p2 p1 p3 p4 p5 p6 p7# Where H(x, y, z) = x ^ y ^ z#proc md5::round3 {p1 p2 p3 p4 p5 p6 p7} { set r [expr {$p2 + ($p1 ^ $p3 ^ $p4) + $p5 + $p6}] return [expr {$p1 + ($r << $p7 | (($r >> (32 - $p7)) & ((1 << $p7) - 1)))}]}# Round 4 helper, e.g.:# a = b + ROT_LEFT((a + I(b, c, d) + X[0] + 0xf4292244), 6)# p1 p2 p1 p3 p4 p5 p6 p7# Where I(x, y, z) = y ^ (x | ~z)#proc md5::round4 {p1 p2 p3 p4 p5 p6 p7} { set r [expr {$p2 + ($p3 ^ ($p1 | ~$p4)) + $p5 + $p6}] return [expr {$p1 + ($r << $p7 | (($r >> (32 - $p7)) & ((1 << $p7) - 1)))}]}# Do one set of rounds. Updates $state(0:3) with results from $x(0:16).proc md5::round {x_name state_name} { upvar $x_name x $state_name state set a $state(0) set b $state(1) set c $state(2) set d $state(3) # Round 1, steps 1-16 set a [round1 $b $a $c $d $x(0) 0xd76aa478 7] set d [round1 $a $d $b $c $x(1) 0xe8c7b756 12] set c [round1 $d $c $a $b $x(2) 0x242070db 17] set b [round1 $c $b $d $a $x(3) 0xc1bdceee 22] set a [round1 $b $a $c $d $x(4) 0xf57c0faf 7] set d [round1 $a $d $b $c $x(5) 0x4787c62a 12] set c [round1 $d $c $a $b $x(6) 0xa8304613 17] set b [round1 $c $b $d $a $x(7) 0xfd469501 22] set a [round1 $b $a $c $d $x(8) 0x698098d8 7] set d [round1 $a $d $b $c $x(9) 0x8b44f7af 12] set c [round1 $d $c $a $b $x(10) 0xffff5bb1 17] set b [round1 $c $b $d $a $x(11) 0x895cd7be 22] set a [round1 $b $a $c $d $x(12) 0x6b901122 7] set d [round1 $a $d $b $c $x(13) 0xfd987193 12] set c [round1 $d $c $a $b $x(14) 0xa679438e 17] set b [round1 $c $b $d $a $x(15) 0x49b40821 22] # Round 2, steps 17-32 set a [round2 $b $a $c $d $x(1) 0xf61e2562 5] set d [round2 $a $d $b $c $x(6) 0xc040b340 9] set c [round2 $d $c $a $b $x(11) 0x265e5a51 14] set b [round2 $c $b $d $a $x(0) 0xe9b6c7aa 20] set a [round2 $b $a $c $d $x(5) 0xd62f105d 5] set d [round2 $a $d $b $c $x(10) 0x02441453 9] set c [round2 $d $c $a $b $x(15) 0xd8a1e681 14] set b [round2 $c $b $d $a $x(4) 0xe7d3fbc8 20] set a [round2 $b $a $c $d $x(9) 0x21e1cde6 5] set d [round2 $a $d $b $c $x(14) 0xc33707d6 9] set c [round2 $d $c $a $b $x(3) 0xf4d50d87 14] set b [round2 $c $b $d $a $x(8) 0x455a14ed 20] set a [round2 $b $a $c $d $x(13) 0xa9e3e905 5] set d [round2 $a $d $b $c $x(2) 0xfcefa3f8 9] set c [round2 $d $c $a $b $x(7) 0x676f02d9 14] set b [round2 $c $b $d $a $x(12) 0x8d2a4c8a 20] # Round 3, steps 33-48 set a [round3 $b $a $c $d $x(5) 0xfffa3942 4] set d [round3 $a $d $b $c $x(8) 0x8771f681 11] set c [round3 $d $c $a $b $x(11) 0x6d9d6122 16] set b [round3 $c $b $d $a $x(14) 0xfde5380c 23] set a [round3 $b $a $c $d $x(1) 0xa4beea44 4] set d [round3 $a $d $b $c $x(4) 0x4bdecfa9 11] set c [round3 $d $c $a $b $x(7) 0xf6bb4b60 16] set b [round3 $c $b $d $a $x(10) 0xbebfbc70 23] set a [round3 $b $a $c $d $x(13) 0x289b7ec6 4] set d [round3 $a $d $b $c $x(0) 0xeaa127fa 11] set c [round3 $d $c $a $b $x(3) 0xd4ef3085 16] set b [round3 $c $b $d $a $x(6) 0x04881d05 23] set a [round3 $b $a $c $d $x(9) 0xd9d4d039 4] set d [round3 $a $d $b $c $x(12) 0xe6db99e5 11] set c [round3 $d $c $a $b $x(15) 0x1fa27cf8 16] set b [round3 $c $b $d $a $x(2) 0xc4ac5665 23] # Round 4, steps 49-64 set a [round4 $b $a $c $d $x(0) 0xf4292244 6] set d [round4 $a $d $b $c $x(7) 0x432aff97 10] set c [round4 $d $c $a $b $x(14) 0xab9423a7 15] set b [round4 $c $b $d $a $x(5) 0xfc93a039 21] set a [round4 $b $a $c $d $x(12) 0x655b59c3 6] set d [round4 $a $d $b $c $x(3) 0x8f0ccc92 10] set c [round4 $d $c $a $b $x(10) 0xffeff47d 15] set b [round4 $c $b $d $a $x(1) 0x85845dd1 21] set a [round4 $b $a $c $d $x(8) 0x6fa87e4f 6] set d [round4 $a $d $b $c $x(15) 0xfe2ce6e0 10] set c [round4 $d $c $a $b $x(6) 0xa3014314 15] set b [round4 $c $b $d $a $x(13) 0x4e0811a1 21] set a [round4 $b $a $c $d $x(4) 0xf7537e82 6] set d [round4 $a $d $b $c $x(11) 0xbd3af235 10] set c [round4 $d $c $a $b $x(2) 0x2ad7d2bb 15] set b [round4 $c $b $d $a $x(9) 0xeb86d391 21] incr state(0) $a incr state(1) $b incr state(2) $c incr state(3) $d}# Pad out buffer per MD5 spec:proc md5::pad {buf_name} { upvar $buf_name buf # Length in bytes: set len [string length $buf] # Length in bits as 2 32 bit words: set len64hi [expr {$len >> 29 & 7}] set len64lo [expr {$len << 3}] # Append 1 special byte, then append 0 or more 0 bytes until # (length in bytes % 64) == 56 set pad [expr {64 - ($len + 8) % 64}] append buf [binary format a$pad "\x80"] # Append the length in bits as a 64 bit value, low bytes first. append buf [binary format i1i1 $len64lo $len64hi]}# Calculate MD5 Digest over a string, return as 32 hex digit string.proc md5::digest {buf} { # This is 0123456789abcdeffedcba9876543210 in byte-swapped order: set state(0) 0x67452301 set state(1) 0xEFCDAB89 set state(2) 0x98BADCFE set state(3) 0x10325476 # Pad buffer per RFC to exact multiple of 64 bytes. pad buf # Calculate digest in 64 byte chunks: set nwords 0 set nbytes 0 set word 0 binary scan $buf c* bytes # Unclear, but the data seems to get byte swapped here. foreach c $bytes { set word [expr {$c << 24 | ($word >> 8 & 0xffffff) }] if {[incr nbytes] == 4} { set nbytes 0 set x($nwords) $word set word 0 if {[incr nwords] == 16} { round x state set nwords 0 } } } # Result is state(0:3), but each word is taken low byte first. set result {} for {set i 0} {$i <= 3} {incr i} { set w $state($i) append result [format %02x%02x%02x%02x \ [expr {$w & 255}] \ [expr {$w >> 8 & 255}] \ [expr {$w >> 16 & 255}] \ [expr {$w >> 24 & 255}]] } return $result}package provide pgintcl $pgtcl::version
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -