1
# pgin.tcl - PostgreSQL Tcl Interface direct to protocol v3 backend
2
# $Id: pgin.tcl,v 3.34 2006-08-14 01:52:04+00 lbayuk Exp $
3
# This version encodes/decodes UNICODE data to/from PostgreSQL.
5
# Copyright 1998-2006 by ljb (lbayuk@mindspring.com)
6
# May be freely distributed with or without modification; must retain this
7
# notice; provided with no warranties.
8
# See the file COPYING for complete information on usage and redistribution
9
# of this file, and for a disclaimer of all warranties.
11
# See the file INTERNALS in the source distribution for more information
12
# about how this thing works, including namespace variables.
15
# md5.tcl - Compute MD5 Checksum
17
package require Tcl 8.3
19
# === Definition of the pgtcl namespace ===
21
namespace eval pgtcl {
25
# Version number, also used in package provide at the bottom of this file:
26
variable version 3.0.1
28
# Counter for making uniquely named result structures:
31
# Array mapping error field names to protocol codes:
32
# Secondary values (without prefix before '_') have been added for
33
# compatibility with Gborg pgtcl (sigh...)
56
# === Internal Low-level I/O procedures for v3 protocol ===
58
# Internal procedure to send a packet to the backend with type and length.
59
# Type can be empty - this is used for the startup packet.
60
# The default is to flush the channel, since almost all messages generated
61
# by pgin.tcl need to wait for a response. The exception is prepared queries.
62
proc pgtcl::sendmsg {sock type data {noflush ""}} {
63
puts -nonewline $sock \
64
$type[binary format I [expr {[string length $data]+4}]]$data
70
# Read a message and return the message type byte:
71
# This initializes the per-connection buffer too.
72
# This has a special check for a v2 error message, which is needed at
73
# startup in case of talking to v2 server. It assumes we will not
74
# get a V3 error message longer than 0x20000000 bytes, which is pretty safe.
75
# It fakes up a V3 error with severity ERROR, code (5 spaces), and the message.
76
proc pgtcl::readmsg {sock} {
77
upvar #0 pgtcl::buf_$sock buf pgtcl::bufi_$sock bufi pgtcl::bufn_$sock bufn
79
if {[binary scan [read $sock 5] aI type len] != 2} {
80
set err "pgtcl: Unable to read message from database"
82
append err " - server closed connection"
86
if {$type == "E" && $len >= 0x20000000} {
87
if {$pgtcl::debug} { puts "Warning: V2 error message received!" }
88
# Build the start of the V3 error, including the 4 misread bytes in $len:
89
set buf [binary format {a a*x a a*x a I} S ERROR C " " M $len]
90
while {[set c [read $sock 1]] != ""} {
92
if {$c == "\000"} break
94
# This is 'code=0' to mark no more error options.
96
set bufn [string length $buf]
98
set bufn [expr {$len - 4}]
99
set buf [read $sock $bufn]
104
# Return the next byte from the buffer:
105
proc pgtcl::get_byte {db} {
106
upvar #0 pgtcl::buf_$db buf pgtcl::bufi_$db bufi
107
set result [string index $buf $bufi]
112
# Return the next $n bytes from the buffer:
113
proc pgtcl::get_bytes {db n} {
114
upvar #0 pgtcl::buf_$db buf pgtcl::bufi_$db bufi
117
return [string range $buf $obufi [expr {$obufi + $n - 1}]]
120
# Return the rest of the buffer.
121
proc pgtcl::get_rest {db} {
122
upvar #0 pgtcl::buf_$db buf pgtcl::bufi_$db bufi pgtcl::bufn_$db bufn
125
return [string range $buf $obufi end]
128
# Skip next $n bytes in the buffer.
129
proc pgtcl::skip {db n} {
130
upvar #0 pgtcl::bufi_$db bufi
134
# Return next int32 from the buffer:
135
proc pgtcl::get_int32 {db} {
136
upvar #0 pgtcl::buf_$db buf pgtcl::bufi_$db bufi
137
if {[binary scan $buf "x$bufi I" i] != 1} {
144
# Return next signed int16 from the buffer:
145
proc pgtcl::get_int16 {db} {
146
upvar #0 pgtcl::buf_$db buf pgtcl::bufi_$db bufi
147
if {[binary scan $buf "x$bufi S" i] != 1} {
154
# Return next unsigned int16 from the buffer:
155
proc pgtcl::get_uint16 {db} {
156
upvar #0 pgtcl::buf_$db buf pgtcl::bufi_$db bufi
157
if {[binary scan $buf "x$bufi S" i] != 1} {
161
return [expr {$i & 0xffff}]
164
# Return next signed int8 from the buffer:
165
# (This is only used in 1 place in the protocol...)
166
proc pgtcl::get_int8 {db} {
167
upvar #0 pgtcl::buf_$db buf pgtcl::bufi_$db bufi
168
if {[binary scan $buf "x$bufi c" i] != 1} {
175
# Return the next null-terminated string from the buffer:
176
# This decodes the UNICODE data. It is used for people-readable text like
177
# messages, not query result data.
178
proc pgtcl::get_string {db} {
179
upvar #0 pgtcl::buf_$db buf pgtcl::bufi_$db bufi
180
set end [string first "\000" $buf $bufi]
185
set bufi [expr {$end + 1}]
186
return [encoding convertfrom identity \
187
[string range $buf $obufi [expr {$end - 1}]]]
190
# === Internal Mid-level I/O procedures for v3 protocol ===
192
# Parse a backend ErrorResponse or NoticeResponse message. The Severity
193
# and Message parts are returned together with a trailing newline, like v2
194
# protocol did. If optional result_name is supplied, it is the name of
195
# a result structure to store all error parts in, indexed as (error,$code).
196
proc pgtcl::get_response {db {result_name ""}} {
197
if {$result_name != ""} {
198
upvar $result_name result
200
array set result {error,S ERROR error,M {}}
201
while {[set c [pgtcl::get_byte $db]] != "\000" && $c != ""} {
202
set result(error,$c) [pgtcl::get_string $db]
204
return "$result(error,S): $result(error,M)\n"
207
# Handle ParameterStatus and remember the name and value:
208
proc pgtcl::get_parameter_status {db} {
209
upvar #0 pgtcl::param_$db param
210
set name [pgtcl::get_string $db]
211
set param($name) [pgtcl::get_string $db]
212
if {$pgtcl::debug} { puts "+server param $name=$param($name)" }
215
# Handle a notification ('A') message.
216
# The notifying backend pid and more_info are read but ignored.
217
proc pgtcl::get_notification_response {db} {
218
set notify_pid [pgtcl::get_int32 $db]
219
set notify_rel [pgtcl::get_string $db]
220
set more_info [pgtcl::get_string $db]
221
if {$pgtcl::debug} { puts "+pgtcl got notify from $notify_pid: $notify_rel" }
222
if {[info exists pgtcl::notify($db,$notify_rel)]} {
223
after idle $pgtcl::notify($db,$notify_rel)
227
# Handle a notice ('N') message. If no handler is defined, or the handler is
228
# empty, do nothing, otherwise, call the handler with the message argument
229
# appended. For backward compatibility with v2 protocol, the message is
230
# assumed to end in a newline.
231
proc pgtcl::get_notice {db} {
232
set msg [pgtcl::get_response $db]
233
if {[info exists pgtcl::notice($db)] && [set cmd $pgtcl::notice($db)] != ""} {
234
eval $cmd [list $msg]
238
# Internal procedure to read a tuple (row) from the backend.
239
# Column count is redundant, but check it anyway.
240
# Format code (text/binary) is used to do Unicode decoding on Text only.
241
proc pgtcl::gettuple {db result_name} {
242
upvar $result_name result
243
if {$result(nattr) == 0} {
245
error "Protocol error, data before descriptor"
247
set irow $result(ntuple)
248
set nattr [pgtcl::get_uint16 $db]
249
if {$nattr != $result(nattr)} {
251
error "Expecting $result(nattr) columns, but data row has $nattr"
254
foreach format $result(formats) {
255
set col_len [pgtcl::get_int32 $db]
258
set result($irow,$icol) [pgtcl::get_bytes $db $col_len]
260
set result($irow,$icol) [encoding convertfrom identity \
261
[pgtcl::get_bytes $db $col_len]]
264
set result($irow,$icol) ""
266
set result(null,$irow,$icol) ""
274
# Internal procedure to handle common backend utility message types:
275
# C : Completion status E : Error
276
# N : Notice message A : Notification
277
# S : ParameterStatus
278
# This can be given any message type. If it handles the message,
279
# it returns 1. If it doesn't handle the message, it returns 0.
281
proc pgtcl::common_message {msgchar db result_name} {
282
upvar $result_name result
284
A { pgtcl::get_notification_response $db }
285
C { set result(complete) [pgtcl::get_string $db] }
286
N { pgtcl::get_notice $db }
287
S { pgtcl::get_parameter_status $db }
289
set result(status) PGRES_FATAL_ERROR
290
set result(error) [pgtcl::get_response $db result]
297
# === Other internal support procedures ===
299
# Internal procedure to set a default value from the environment:
300
proc pgtcl::default {default args} {
303
if {[info exists env($a)]} {
310
# Internal procedure to parse a connection info string.
311
# This has to handle quoting and escaping. See the PostgreSQL Programmer's
312
# Guide, Client Interfaces, Libpq, Database Connection Functions.
313
# The definitive reference is the PostgreSQL source code in:
314
# interface/libpq/fe-connect.c:conninfo_parse()
315
# One quirk to note: backslash escapes work in quoted values, and also in
316
# unquoted values, but you cannot use backslash-space in an unquoted value,
317
# because the space ends the value regardless of the backslash.
319
# Stores the results in an array $result(paramname)=value. It will not
320
# create a new index in the array; if paramname does not already exist,
321
# it means a bad parameter was given (one not defined by pg_conndefaults).
322
# Returns an error message on error, else an empty string if OK.
323
proc pgtcl::parse_conninfo {conninfo result_name} {
324
upvar $result_name result
325
while {[regexp {^ *([^=]*)= *(.+)} $conninfo unused name conninfo]} {
326
set name [string trim $name]
327
if {[regexp {^'(.*)} $conninfo unused conninfo]} {
329
set n [string length $conninfo]
330
for {set i 0} {$i < $n} {incr i} {
331
if {[set c [string index $conninfo $i]] == "\\"} {
332
set c [string index $conninfo [incr i]]
333
} elseif {$c == "'"} break
337
return "unterminated quoted string in connection info string"
339
set conninfo [string range $conninfo [incr i] end]
341
regexp {^([^ ]*)(.*)} $conninfo unused value conninfo
342
regsub -all {\\(.)} $value {\1} value
344
if {$pgtcl::debug} { puts "+parse_conninfo name=$name value=$value" }
345
if {![info exists result($name)]} {
346
return "invalid connection option \"$name\""
348
set result($name) $value
350
if {[string trim $conninfo] != ""} {
351
return "syntax error in connection info string '...$conninfo'"
356
# Internal procedure to check for valid result handle. This returns
357
# the fully qualified name of the result array.
358
# Usage: upvar #0 [pgtcl::checkres $res] result
359
proc pgtcl::checkres {res} {
360
if {![info exists pgtcl::result$res]} {
361
error "Invalid result handle\n$res is not a valid query result"
363
return "pgtcl::result$res"
366
# === Public procedures : Connecting and Disconnecting ===
368
# Return connection defaults as {optname label dispchar dispsize value}...
369
proc pg_conndefaults {} {
370
set user [pgtcl::default user PGUSER USER LOGNAME USERNAME]
372
[list user Database-User {} 20 $user] \
373
[list password Database-Password * 20 [pgtcl::default {} PGPASSWORD]] \
374
[list host Database-Host {} 40 [pgtcl::default localhost PGHOST]] \
375
{hostaddr Database-Host-IP-Address {} 45 {}} \
376
[list port Database-Port {} 6 [pgtcl::default 5432 PGPORT]] \
377
[list dbname Database-Name {} 20 [pgtcl::default $user PGDATABASE]] \
378
[list tty Backend-Debug-TTY D 40 [pgtcl::default {} PGTTY]] \
379
[list options Backend-Debug-Options D 40 [pgtcl::default {} PGOPTIONS]] \
381
if {$pgtcl::debug} { puts "+pg_conndefaults: $result" }
385
# Connect to database. Only the new form, with -conninfo, is recognized.
386
# We speak backend protocol v3, and only handle clear-text password and
387
# MD5 authentication (messages R 3, and R 5).
388
# A parameter is added to set client_encoding to UNICODE. This is due to
389
# Tcl's way of representing strings.
390
proc pg_connect {args} {
392
if {[llength $args] != 2 || [lindex $args 0] != "-conninfo"} {
393
error "Connection to database failed\nMust use pg_connect -conninfo form"
396
# Get connection defaults into an array opt(), then merge caller params:
397
foreach o [pg_conndefaults] {
398
set opt([lindex $o 0]) [lindex $o 4]
400
if {[set msg [pgtcl::parse_conninfo [lindex $args 1] opt]] != ""} {
401
error "Connection to database failed\n$msg"
404
# Hostaddr overrides host, per documentation, and we need host below.
405
if {$opt(hostaddr) != ""} {
406
set opt(host) $opt(hostaddr)
410
puts "+pg_connect to $opt(dbname)@$opt(host):$opt(port) as $opt(user)"
413
if {[catch {socket $opt(host) $opt(port)} sock]} {
414
error "Connection to database failed\n$sock"
416
# Note: full buffering, socket must be flushed after write!
417
fconfigure $sock -buffering full -translation binary
420
pgtcl::sendmsg $sock {} [binary format "I a*x a*x a*x a*x a*x a*x a*x a*x x" \
422
user $opt(user) database $opt(dbname) \
423
client_encoding UNICODE options $opt(options)]
426
while {[set c [pgtcl::readmsg $sock]] != "Z"} {
429
set msg [pgtcl::get_response $sock]
433
set n [pgtcl::get_int32 $sock]
435
pgtcl::sendmsg $sock p "$opt(password)\000"
437
set salt [pgtcl::get_bytes $sock 4]
438
# This is from PostgreSQL source backend/libpq/crypt.c:
440
"md5[md5::digest [md5::digest $opt(password)$opt(user)]$salt]"
441
if {$pgtcl::debug} { puts "+pg_connect MD5 sending: $md5_response" }
442
pgtcl::sendmsg $sock p "$md5_response\000"
444
set msg "Unknown database authentication request($n)"
449
set pid [pgtcl::get_int32 $sock]
450
set key [pgtcl::get_int32 $sock]
451
if {$pgtcl::debug} { puts "+server pid=$pid key=$key" }
454
pgtcl::get_parameter_status $sock
457
set msg "Unexpected reply from database: $c"
464
error "Connection to database failed\n$msg"
466
# Initialize transaction status; should be get_byte but it better be I:
467
set pgtcl::xstate($sock) I
468
# Initialize action for NOTICE messages (see get_notice):
469
set pgtcl::notice($sock) {puts -nonewline stderr}
474
# Disconnect from the database. Free all result structures which are
475
# associated with this connection, and other data for this connection,
476
# including the buffer.
477
# Note: This does not use {array unset} (Tcl 8.3) nor {unset -nocomplain}
478
# (Tcl 8.4), but is coded to be compatible with earlier versions.
479
proc pg_disconnect {db} {
480
if {$pgtcl::debug} { puts "+Disconnecting $db from database" }
481
pgtcl::sendmsg $db X {}
483
foreach v [info vars pgtcl::result*] {
485
if {$result(conn) == $db} {
486
if {$pgtcl::debug} { puts "+Freeing left-over result structure $v" }
490
if {[array exists pgtcl::notify]} {
491
foreach v [array names pgtcl::notify $db,*] {
492
unset pgtcl::notify($v)
495
catch { unset pgtcl::param_$db }
496
catch { unset pgtcl::xstate($db) pgtcl::notice($db) }
497
catch { unset pgtcl::buf_$db pgtcl::bufi_$db pgtcl::bufn_$db }
500
# === Internal procedures: Query Result and supporting functions ===
502
# Read the backend reply to a query (simple or extended) and build a
503
# result structure. For extended query mode, the client already sent
504
# the Bind, DescribePortal, Execute, and Sync.
505
# This implements most of the backend query response protocol. The important
507
# T : RowDescription describes the attributes (columns) of each data row.
508
# D : DataRow has data for 1 tuple.
509
# Z : ReadyForQuery, update transaction status.
510
# H : Ready for Copy Out
511
# G : Ready for Copy In
512
# Plus the messages handled by pgtcl::common_message.
513
# If the optional parameter $extq == 1, the result handle is from an extended
514
# mode query (see pg_exec_prepared) and these messages are allowed and ignored:
516
# 1 : ParseComplete (used only for exec_params)
519
# Returns a result handle (the number pgtcl::rn), or throws an error.
521
proc pgtcl::getresult {db {extq 0}} {
522
upvar #0 pgtcl::result[incr pgtcl::rn] result
526
attrs {} types {} sizes {} modifs {} formats {}
527
error {} tbloids {} tblcols {}
529
status PGRES_COMMAND_OK
533
set c [pgtcl::readmsg $db]
536
pgtcl::gettuple $db result
539
if {$result(nattr) != 0} {
541
error "Protocol failure, multiple descriptors"
543
set result(status) PGRES_TUPLES_OK
544
set nattr [pgtcl::get_uint16 $db]
545
set result(nattr) $nattr
546
for {set icol 0} {$icol < $nattr} {incr icol} {
547
lappend result(attrs) [pgtcl::get_string $db]
548
lappend result(tbloids) [pgtcl::get_int32 $db]
549
lappend result(tblcols) [pgtcl::get_uint16 $db]
550
lappend result(types) [pgtcl::get_int32 $db]
551
lappend result(sizes) [pgtcl::get_int16 $db]
552
lappend result(modifs) [pgtcl::get_int32 $db]
553
lappend result(formats) [pgtcl::get_int16 $db]
557
set result(status) PGRES_EMPTY_QUERY
560
pgtcl::begincopy result OUT
564
pgtcl::begincopy result IN
568
set pgtcl::xstate($db) [pgtcl::get_byte $db]
572
if {(!$extq || ($c != "2" && $c != "n" && $c != "1")) && \
573
![pgtcl::common_message $c $db result]} {
575
error "Unexpected reply from database: $c"
580
if {$pgtcl::debug > 1} {
581
puts "+pgtcl::getresult $pgtcl::rn = "
587
# Process format code information for pg_exec_prepared.
588
# fclist A list of BINARY (or B*) or TEXT (or T*) format code words.
589
# ncodes_name The name of a variable to get the number of format codes.
590
# codes_name The name of a variable to get a list of format codes in
591
# the PostgreSQL syntax: 0=text 1=binary.
592
proc pgtcl::crunch_fcodes {fclist ncodes_name codes_name} {
593
upvar $ncodes_name ncodes $codes_name codes
594
set ncodes [llength $fclist]
597
if {[string match B* $k]} {
605
# Return an error code field value for pg_result -error?Field? code.
606
# For field names, it accepts either the libpq name (without PG_DIAG_) or the
607
# single-letter protocol code.
608
# For compatibility with changes made to Gborg pgtcl after this feature was
609
# added here, it also accepts names without prefixes, and $code is case
611
# If an unknown field name is used, or the field isn't part of the error
612
# message, an empty string is substituted.
614
proc pgtcl::error_fields {result_name code} {
615
upvar $result_name result
617
set code [string toupper $code]
618
if {[info exists errnames($code)]} {
619
set code $errnames($code)
621
if {[info exists result(error,$code)]} {
622
return $result(error,$code)
627
# === Public procedures : Query and Result ===
629
# Execute SQL and return a result handle.
630
# If parameters are supplied, use pg_exec_params in all-text arg mode.
631
# (Let pg_exec_params encode the query in that case.)
633
proc pg_exec {db query args} {
634
if {$pgtcl::debug} { puts "+pg_exec $query {$args}" }
635
if {[llength $args] == 0} {
636
pgtcl::sendmsg $db Q "[encoding convertto identity $query]\000"
637
return [pgtcl::getresult $db]
639
return [eval pg_exec_params {$db} {$query} {{}} {{}} {{}} $args]
642
# Extract data from a pg_exec result structure.
643
# -cmdTuples, -list, and -llist are extensions to the baseline libpgtcl which
644
# have appeared or will appear in beta or future versions.
645
# -errorField, -lxAttributes and -getNull are proposed new for 7.4.
646
# -cmdStatus is new with pgintcl-2.0.1
648
proc pg_result {res option args} {
649
upvar #0 [pgtcl::checkres $res] result
650
set argc [llength $args]
651
set ntuple $result(ntuple)
652
set nattr $result(nattr)
654
-status { return $result(status) }
655
-conn { return $result(conn) }
657
if {[regexp {^INSERT +([0-9]*)} $result(complete) unused oid]} {
663
if {[regexp {^INSERT +[0-9]* +([0-9]*)} $result(complete) x num] \
664
|| [regexp {^(UPDATE|DELETE) +([0-9]*)} $result(complete) x y num]} {
669
-cmdStatus { return $result(complete) }
670
-numTuples { return $ntuple }
671
-numAttrs { return $nattr }
674
error "-assign option must be followed by a variable name"
678
foreach attr $result(attrs) {
679
for {set irow 0} {$irow < $ntuple} {incr irow} {
680
set a($irow,$attr) $result($irow,$icol)
686
if {$argc != 1 && $argc != 2} {
687
error "-assignbyidxoption requires an array name and optionally an\
690
upvar [lindex $args 0] a
692
set suffix [lindex $args 1]
696
set attr_first [lindex $result(attrs) 0]
697
set attr_rest [lrange $result(attrs) 1 end]
698
for {set irow 0} {$irow < $ntuple} {incr irow} {
699
set val_first $result($irow,0)
701
foreach attr $attr_rest {
702
set a($val_first,$attr$suffix) $result($irow,$icol)
709
error "-getTuple option must be followed by a tuple number"
712
if {$irow < 0 || $irow >= $ntuple} {
713
error "argument to getTuple cannot exceed number of tuples - 1"
716
for {set icol 0} {$icol < $nattr} {incr icol} {
717
lappend list $result($irow,$icol)
723
error "-getNull option must be followed by a tuple number"
726
if {$irow < 0 || $irow >= $ntuple} {
727
error "argument to getNull cannot exceed number of tuples - 1"
730
for {set icol 0} {$icol < $nattr} {incr icol} {
731
lappend list [info exists result(null,$irow,$icol)]
737
error "-tupleArray option must be followed by a tuple number and\
740
set irow [lindex $args 0]
741
if {$irow < 0 || $irow >= $ntuple} {
742
error "argument to tupleArray cannot exceed number of tuples - 1"
744
upvar [lindex $args 1] a
746
foreach attr $result(attrs) {
747
set a($attr) $result($irow,$icol)
753
for {set irow 0} {$irow < $ntuple} {incr irow} {
754
for {set icol 0} {$icol < $nattr} {incr icol} {
755
lappend list $result($irow,$icol)
762
for {set irow 0} {$irow < $ntuple} {incr irow} {
764
for {set icol 0} {$icol < $nattr} {incr icol} {
765
lappend sublist $result($irow,$icol)
767
lappend list $sublist
772
return $result(attrs)
776
foreach attr $result(attrs) type $result(types) size $result(sizes) {
777
lappend list [list $attr $type $size]
783
foreach attr $result(attrs) type $result(types) size $result(sizes) \
784
modif $result(modifs) format $result(formats) \
785
tbloid $result(tbloids) tblcol $result(tblcols) {
786
lappend list [list $attr $type $size $modif $format $tbloid $tblcol]
796
return $result(error)
798
return [pgtcl::error_fields result $args]
800
default { error "Invalid option to pg_result: $option" }
804
# Run a select query and iterate over the results. Uses pg_exec to run the
805
# query and build the result structure, but we cheat and directly use the
806
# result array rather than calling pg_result.
807
# Each returned tuple is stored into the caller's array, then the caller's
809
# If the caller's proc does "break", "return", or gets an error, get out
810
# of the processing loop. Tcl codes: 0=OK 1=error 2=return 3=break 4=continue
811
proc pg_select {db query var_name proc} {
813
global errorCode errorInfo
814
set res [pg_exec $db $query]
815
upvar #0 pgtcl::result$res result
816
if {$result(status) != "PGRES_TUPLES_OK"} {
817
set msg $result(error)
822
set var(.headers) $result(attrs)
823
set var(.numcols) $result(nattr)
824
set ntuple $result(ntuple)
825
for {set irow 0} {$irow < $ntuple} {incr irow} {
826
set var(.tupno) $irow
828
foreach attr $result(attrs) {
829
set var($attr) $result($irow,$icol)
832
set code [catch {uplevel 1 $proc} s]
833
if {$code != 0 && $code != 4} break
837
return -code error -errorinfo $errorInfo -errorcode $errorCode $s
838
} elseif {$code == 2 || $code > 4} {
839
return -code $code $s
844
# Register a listener for backend notification, or cancel a listener.
845
proc pg_listen {db name {proc ""}} {
847
set pgtcl::notify($db,$name) $proc
848
set r [pg_exec $db "listen $name"]
850
} elseif {[info exists pgtcl::notify($db,$name)]} {
851
unset pgtcl::notify($db,$name)
852
set r [pg_exec $db "unlisten $name"]
857
# pg_execute: Execute a query, optionally iterating over the results.
859
# Returns the number of tuples selected or affected by the query.
860
# Usage: pg_execute ?options? connection query ?proc?
861
# Options: -array ArrayVar
863
# If -array is not given with a SELECT, the data is put in variables
864
# named by the fields. This is generally a bad idea and could be dangerous.
866
# If there is no proc body and the query return 1 or more rows, the first
867
# row is stored in the array or variables and we return (as does libpgtcl).
869
# Notes: Handles proc return codes of:
870
# 0(OK) 1(error) 2(return) 3(break) 4(continue)
871
# Uses pg_exec and pg_result, but also makes direct access to the
872
# structures used by them.
874
proc pg_execute {args} {
875
global errorCode errorInfo
877
set usage "pg_execute ?-array arrayname?\
878
?-oid varname? connection queryString ?loop_body?"
880
# Set defaults and parse command arguments:
884
set last_option_arg {}
885
set n_nonswitch_args 0
890
if {$last_option_arg != ""} {
891
if {$last_option_arg == "-array"} {
894
} elseif {$last_option_arg == "-oid"} {
898
error "Unknown option $last_option_arg\n$usage"
900
set last_option_arg {}
901
} elseif {[regexp ^- $arg]} {
902
set last_option_arg $arg
904
if {[incr n_nonswitch_args] == 1} {
906
} elseif {$n_nonswitch_args == 2} {
908
} elseif {$n_nonswitch_args == 3} {
912
error "Wrong # of arguments\n$usage"
916
if {$last_option_arg != "" || $n_nonswitch_args < 2} {
917
error "Bad arguments\n$usage"
920
set res [pg_exec $conn $query]
921
upvar #0 pgtcl::result$res result
923
# For non-SELECT query, just process oid and return value.
924
# Let pg_result do the decoding.
925
if {[regexp {^PGRES_(COMMAND_OK|COPY|EMPTY_QUERY)} $result(status)]} {
927
set oid [pg_result $res -oid]
929
set ntuple [pg_result $res -cmdTuples]
930
pg_result $res -clear
934
if {$result(status) != "PGRES_TUPLES_OK"} {
935
set status [list $result(status) $result(error)]
936
pg_result $res -clear
940
# Handle a SELECT query. This is like pg_select, except the proc is optional,
941
# and the fields can go in an array or variables.
942
# With no proc, store the first row only.
945
foreach attr $result(attrs) {
946
upvar $attr data_$attr
949
set ntuple $result(ntuple)
950
for {set irow 0} {$irow < $ntuple} {incr irow} {
953
foreach attr $result(attrs) {
954
set data($attr) $result($irow,$icol)
958
foreach attr $result(attrs) {
959
set data_$attr $result($irow,$icol)
964
set code [catch {uplevel 1 $proc} s]
965
if {$code != 0 && $code != 4} break
967
pg_result $res -clear
969
return -code error -errorinfo $errorInfo -errorcode $errorCode $s
970
} elseif {$code == 2 || $code > 4} {
971
return -code $code $s
976
# Extended query protocol: Bind parameters and execute prepared statement.
977
# This is modelled on libpq PQexecPrepared. Use pg_exec to send a PREPARE
978
# first; when called externally it does not handle unnamed statements.
979
# This is also used internally by pg_exec_params, with an unnamed statement.
981
# db Connection handle
982
# stmt Name of the prepared SQL statement to execute
983
# res_formats A list describing results: B* => Binary, else Text.
984
# arg_formats A list describing args: B* => Binary, else Text.
985
# args Variable number of arguments to bind to the query params.
986
proc pg_exec_prepared {db stmt res_formats arg_formats args} {
987
set nargs [llength $args]
989
if {$pgtcl::debug} { puts "+pg_exec_prepared stmt=$stmt nargs=$nargs" }
990
# Calculate argument format information:
991
pgtcl::crunch_fcodes $arg_formats nfcodes fcodes
993
# Build the first part of the Bind message:
994
set out [binary format {x a*x S S* S} \
995
[encoding convertto identity $stmt] $nfcodes $fcodes $nargs]
997
# Expand fcodes so there is a text/binary flag for each argument:
999
set all_fcodes [string repeat "0 " $nargs]
1000
} elseif {$nfcodes == 1} {
1001
set all_fcodes [string repeat "$fcodes " $nargs]
1003
set all_fcodes $fcodes
1006
# Append parameter values as { int32 length or 0 or -1 for NULL; data}
1007
# Note: There is no support for NULLs as parameters.
1008
# Encode all text parameters, leave binary parameters alone.
1009
foreach arg $args fcode $all_fcodes {
1011
append out [binary format I [string length $arg]] $arg
1013
append out [binary format I [string length $arg]] \
1014
[encoding convertto identity $arg]
1018
# Append result parameter format information:
1019
pgtcl::crunch_fcodes $res_formats nrfcodes rfcodes
1020
append out [binary format {S S*} $nrfcodes $rfcodes]
1022
# Send it off. Don't wait for BindComplete or Error, because the protocol
1023
# says the BE will discard until Sync anyway.
1024
pgtcl::sendmsg $db B $out -noflush
1026
# Send DescribePortal for the unnamed portal:
1027
pgtcl::sendmsg $db D "P\0" -noflush
1028
# Send Execute, unnamed portal, unlimited rows:
1029
pgtcl::sendmsg $db E "\0\0\0\0\0" -noflush
1031
pgtcl::sendmsg $db S {}
1033
# Fetch query result and return result handle:
1034
return [pgtcl::getresult $db 1]
1037
# Extended query protocol: Parse, Bind and execute statement. This is similar
1038
# to pg_exec_prepared, but doesn't use a pre-prepared statement, and if you
1039
# want to pass binary parameters you must also provide the type OIDs.
1040
# This is modelled on libpq PQexecParams.
1042
# db Connection handle
1043
# query Query to execute, may contain parameters $1, $2, ...
1044
# res_formats A list describing results: B* => binary, else text
1045
# arg_formats A list describing args: B* => Binary, else Text.
1046
# arg_types A list of type OIDs for each argument (if Binary).
1047
# args Variable number of arguments to bind to the query params.
1049
# Protocol note: Perhaps the right way to do this is to send Parse,
1050
# then Flush, and check for ParseComplete or ErrorResponse. But then
1051
# if there is an error, you need to send Sync and build a result structure.
1052
# Since the backend will ignore everything after error until Sync, this
1053
# is coded the easier way: Just send everything and let the lower-level code
1054
# report the errors, whether on Parse or Bind or Execute.
1056
proc pg_exec_params {db query res_formats arg_formats arg_types args} {
1057
if {$pgtcl::debug} { puts "+pg_exec_params query=$query" }
1058
# Build and send Parse message with the SQL command and list of arg types:
1059
set out [binary format {x a*x S} [encoding convertto identity $query] \
1060
[llength $arg_types]]
1061
foreach type $arg_types {
1062
append out [binary format I $type]
1064
pgtcl::sendmsg $db P $out -noflush
1065
# See note above regarding not checking for ParseComplete here.
1066
# Proceed as with pg_exec_prepared, but with an unnamed statement:
1067
return [eval pg_exec_prepared {$db} {""} {$res_formats} {$arg_formats} $args]
1070
# === Public procedures : Miscellaneous ===
1072
# pg_notice_handler: Set/get handler command for Notice/Warning
1073
# Usage: pg_notice_handler connection ?command?
1075
# command If supplied, the new handler command. The notice text
1076
# will be appended as a list element.
1077
# If supplied but empty, ignore notice/warnings.
1078
# If not supplied, just return the current value.
1079
# Returns the previous handler command.
1080
proc pg_notice_handler {db args} {
1081
set return_value $pgtcl::notice($db)
1082
if {[set nargs [llength $args]] == 1} {
1083
set pgtcl::notice($db) [lindex $args 0]
1084
} elseif {$nargs != 0} {
1085
error "Wrong # args: should be \"pg_notice_handler connection ?command?\""
1087
return $return_value
1090
# pg_configure: Configure options for PostgreSQL connections
1091
# This is provided only for backward compatibility with earlier versions.
1093
proc pg_configure {db option args} {
1094
if {[set nargs [llength $args]] > 1} {
1095
error "Wrong # args: should be \"pg_configure connection option ?value?\""
1098
debug { upvar pgtcl::debug var }
1099
notice { upvar pgtcl::notice($db) var }
1101
error "Bad option \"$option\": must be one of notice, debug"
1104
set return_value $var
1106
set var [lindex $args 0]
1108
return $return_value
1111
# pg_escape_string: Escape a string for use as a quoted SQL string
1112
# Returns the escaped string. This was added to PostgreSQL after 7.3.2
1113
# and to libpgtcl after 1.4b3.
1114
# Note: string map requires Tcl >= 8.1 but is faster than regsub here.
1115
proc pg_escape_string {s} {
1116
return [string map {' '' \\ \\\\} $s]
1119
# pg_quote: Same as pg_escape_string but returns the quotes around the
1120
# argument too. Found this in gborg pgtcl cvs logs, not sure why.
1123
return "'[string map {' '' \\ \\\\} $s]'"
1126
# pg_escape_bytea: Escape a binary string for use as a quoted SQL string.
1127
# Returns the escaped string, which is safe for use inside single quotes
1128
# in an SQL statement. Note back-slashes are doubled due to double parsing
1129
# in the backend. Emulates libpq PQescapeBytea()
1130
# See also pg_unescape_bytea, but note that these functions are not inverses.
1131
# (I tried many versions to improve speed and this was fastest, although still
1132
# slow. The numeric constants 92=\ and 39=` were part of that optimization.)
1133
proc pg_escape_bytea {binstr} {
1135
binary scan $binstr c* val_list
1136
foreach c [split $binstr {}] val $val_list {
1138
append result {\\\\}
1139
} elseif {$val == 39} {
1141
} elseif {$val < 32 || 126 < $val} {
1142
append result [format {\\%03o} [expr {$val & 255}]]
1150
# pg_unescape_bytea: Unescape a string returned from PostgreSQL as an
1151
# escaped bytea object and return a binary string.
1152
# Emulates libpq PQunescapeBytea().
1153
# See also pg_escape_bytea, but note that these functions are not inverses.
1154
# Implementation note: Iterative implementations perform very poorly.
1155
# This method is from Benny Riefenstahl via Jerry Levan. It works much
1156
# faster, and returns the correct data on any value produced by the
1157
# PostgreSQL backend from converting a bytea data type to text (byteaout).
1158
# But it does NOT work the same as PQunescapeBytea() for all values.
1159
# For example, passing \a here returns 0x07, but PQunescapeBytea returns 'a'.
1160
proc pg_unescape_bytea {str} {
1161
return [subst -nocommands -novariables $str]
1164
# pg_parameter_status: Return the value of a backend parameter value.
1165
# These are generally supplied by the backend during startup.
1166
proc pg_parameter_status {db name} {
1167
upvar #0 pgtcl::param_$db param
1168
if {[info exists param($name)]} {
1169
return $param($name)
1174
# pg_transaction_status: Return the current transaction status.
1175
# Returns a string: IDLE INTRANS INERROR or UNKNOWN.
1176
proc pg_transaction_status {db} {
1177
if {[info exists pgtcl::xstate($db)]} {
1178
switch -- $pgtcl::xstate($db) {
1180
T { return INTRANS }
1181
E { return INERROR }
1187
# === Internal Procedure to support COPY ===
1189
# Handle a CopyInResponse or CopyOutResponse message:
1190
proc pgtcl::begincopy {result_name direction} {
1191
upvar $result_name result
1192
set db $result(conn)
1193
if {[pgtcl::get_int8 $db]} {
1194
error "pg_exec: COPY BINARY is not supported"
1196
set result(status) PGRES_COPY_$direction
1197
# Column count and per-column formats are ignored.
1198
set ncol [pgtcl::get_int16 $db]
1199
pgtcl::skip $db [expr {2*$ncol}]
1200
if {$pgtcl::debug} { puts "+pg_exec begin copy $direction" }
1203
# === Public procedures: COPY ===
1205
# I/O procedures to support COPY. No longer able to just read/write the
1206
# channel, due to the message procotol.
1208
# Read line from COPY TO. Returns the copy line if OK, else "" on end.
1209
# Note: The returned line does not end in a newline, so you can split it
1210
# on tab and get a list of column values.
1211
# At end of COPY, it takes the CopyDone only. pg_endcopy must be called to
1212
# get the CommandComplete and ReadyForQuery messages.
1213
proc pg_copy_read {res} {
1214
upvar #0 [pgtcl::checkres $res] result
1215
set db $result(conn)
1216
if {$result(status) != "PGRES_COPY_OUT"} {
1217
error "pg_copy_read called but connection is not doing a COPY OUT"
1219
# Notice/Notify etc are not allowed during copy, so no loop needed.
1220
set c [pgtcl::readmsg $db]
1221
if {$pgtcl::debug} { puts "+pg_copy_read msg $c" }
1223
return [string trimright \
1224
[encoding convertfrom identity [pgtcl::get_rest $db]] "\n\r"]
1229
# Error or invalid response.
1231
set result(status) PGRES_FATAL_ERROR
1232
set result(error) [pgtcl::get_response $db result]
1235
error "pg_copy_read: procotol violation, unexpected $c in copy out"
1238
# Write line for COPY FROM. This must represent a single record (tuple) with
1239
# values separated by tabs. Do not add a newline; pg_copy_write does this.
1240
proc pg_copy_write {res line} {
1241
upvar #0 [pgtcl::checkres $res] result
1242
pgtcl::sendmsg $result(conn) d "[encoding convertto identity $line]\n"
1245
# End a COPY TO/FROM. This is needed to finish up the protocol after
1246
# reading or writing. On COPY TO, this needs to be called after
1247
# pg_copy_read returns an empty string. On COPY FROM, this needs to
1248
# be called after writing the last record with pg_copy_write.
1249
# Note: Do not write or expect to read "\." anymore.
1250
# When it returns, the result structure (res) will be updated.
1251
proc pg_endcopy {res} {
1252
upvar #0 [pgtcl::checkres $res] result
1253
set db $result(conn)
1254
if {$pgtcl::debug} { puts "+pg_endcopy end $result(status)" }
1256
# An error might have been sent during a COPY TO, so the result
1257
# status will already be FATAL and should not be disturbed.
1258
if {$result(status) != "PGRES_FATAL_ERROR"} {
1259
if {$result(status) == "PGRES_COPY_IN"} {
1261
pgtcl::sendmsg $db c {}
1262
} elseif {$result(status) != "PGRES_COPY_OUT"} {
1263
error "pg_endcopy called but connection is not doing a COPY"
1265
set result(status) PGRES_COMMAND_OK
1268
# We're looking for CommandComplete and ReadyForQuery here, but other
1269
# things can happen too.
1270
while {[set c [pgtcl::readmsg $db]] != "Z"} {
1271
if {![pgtcl::common_message $c $db result]} {
1272
error "Unexpected reply from database: $c"
1275
set pgtcl::xstate($db) [pgtcl::get_byte $db]
1276
if {$pgtcl::debug} { puts "+pg_endcopy returns, st=$result(status)" }
1279
# === Internal producedures for Function Call (used by Large Object) ===
1281
# Internal procedure to lookup, cache, and return a PostgreSQL function OID.
1282
# This assumes all connections have the same function OIDs, which might not be
1283
# true if you connect to servers running different versions of PostgreSQL.
1284
# Throws an error if the OID is not found by PostgreSQL.
1285
# To call overloaded functions, argument types must be specified in parentheses
1286
# after the function name, in the the exact same format as psql "\df".
1287
# This is a list of types separated by a comma and one space.
1288
# For example: fname="like(text, text)".
1289
# The return type cannot be specified. I don't think there are any functions
1290
# distinguished only by return type.
1291
proc pgtcl::getfnoid {db fname} {
1294
if {![info exists fnoids($fname)]} {
1296
# Separate the function name from the (arg type list):
1297
if {[regexp {^([^(]*)\(([^)]*)\)$} $fname unused fcn arglist]} {
1298
set amatch " and oidvectortypes(proargtypes)='$arglist'"
1303
pg_select $db "select oid from pg_proc where proname='$fcn' $amatch" d {
1304
set fnoids($fname) $d(oid)
1306
if {![info exists fnoids($fname)]} {
1307
error "Unable to get OID of database function $fname"
1310
return $fnoids($fname)
1313
# Internal procedure to implement PostgreSQL "fast-path" function calls.
1314
# $fn_oid is the OID of the PostgreSQL function. See pgtcl::getfnoid.
1315
# $result_name is the name of the variable to store the backend function
1317
# $arginfo is a list of argument descriptors, each is I or S or a number.
1318
# I means the argument is an integer32.
1319
# S means the argument is a string, and its actual length is used.
1320
# A number means send exactly that many bytes (null-pad if needed) from
1322
# (Argument type S is passed in Ascii format code, others as Binary.)
1323
# $arglist is a list of arguments to the PostgreSQL function. (This
1324
# is actually a pass-through argument 'args' from the wrappers.)
1325
# Throws Tcl error on error, otherwise returns size of the result
1326
# stored into the $result_name variable.
1328
proc pgtcl::callfn {db fn_oid result_name arginfo arglist} {
1329
upvar $result_name result
1331
set nargs [llength $arginfo]
1332
if {$pgtcl::debug} {
1333
puts "+callfn oid=$fn_oid nargs=$nargs info=$arginfo args=$arglist"
1336
# Function call: oid nfcodes fcodes... nargs {arglen arg}... resultfcode
1338
foreach k $arginfo {
1345
set out [binary format {I S S* S} $fn_oid $nargs $fcodes $nargs]
1346
# Append each argument and its length:
1347
foreach k $arginfo arg $arglist {
1349
append out [binary format II 4 $arg]
1350
} elseif {$k == "S"} {
1351
append out [binary format I [string length $arg]] $arg
1353
append out [binary format Ia$k $k $arg]
1356
# Append format code for binary result:
1357
append out [binary format S 1]
1358
pgtcl::sendmsg $db F $out
1362
# Fake up a partial result structure for pgtcl::common_message :
1365
# FunctionCall response. Also handles common messages (notify, notice).
1366
while {[set c [pgtcl::readmsg $db]] != "Z"} {
1368
set result_size [pgtcl::get_int32 $db]
1369
if {$result_size > 0} {
1370
set result [pgtcl::get_bytes $db $result_size]
1374
} elseif {![pgtcl::common_message $c $db res]} {
1375
error "Unexpected reply from database: $c"
1378
set pgtcl::xstate($db) [pgtcl::get_byte $db]
1379
if {$res(error) != ""} {
1385
# === Public prodedures: Function Call ===
1387
# Public interface to pgtcl::callfn.
1388
proc pg_callfn {db fname result_name arginfo args} {
1389
upvar $result_name result
1390
return [pgtcl::callfn $db [pgtcl::getfnoid $db $fname] result $arginfo $args]
1393
# Public, simplified interface to pgtcl::callfn when an int32 return value is
1394
# expected. Returns the backend function return value.
1395
proc pg_callfn_int {db fname arginfo args} {
1396
set n [pgtcl::callfn $db [pgtcl::getfnoid $db $fname] result $arginfo $args]
1398
error "Unexpected response size ($result_size) to pg function call $fname"
1400
binary scan $result I val
1404
# === Internal procedure to support Large Object ===
1406
# Convert a LO mode string into the value of the constants used by libpq.
1407
# Note: libpgtcl uses a mode like INV_READ|INV_WRITE for lo_creat, but
1408
# r, w, or rw for lo_open (which it translates to INV_READ|INV_WRITE).
1409
# This seems like a mistake. The code here accepts either form for either.
1410
proc pgtcl::lomode {mode} {
1412
if {[string match -nocase *INV_* $mode]} {
1413
if {[string match -nocase *INV_READ* $mode]} {
1416
if {[string match -nocase *INV_WRITE* $mode]} {
1417
set imode [expr {$imode + 0x20000}]
1420
if {[string match -nocase *r* $mode]} {
1423
if {[string match -nocase *w* $mode]} {
1424
set imode [expr {$imode + 0x20000}]
1428
error "Invalid large object mode $mode"
1433
# === Public prodedures: Large Object ===
1435
# Create large object and return OID.
1436
# See note regarding mode above at pgtcl::lomode.
1437
proc pg_lo_creat {db mode} {
1438
if {[catch {pg_callfn_int $db lo_creat I [pgtcl::lomode $mode]} result]} {
1439
error "Large Object create failed\n$result"
1441
if {$result == -1} {
1442
error "Large Object create failed"
1447
# Open large object and return large object file descriptor.
1448
# See note regarding mode above at pgtcl::lomode.
1449
proc pg_lo_open {db loid mode} {
1450
if {[catch {pg_callfn_int $db lo_open "I I" $loid [pgtcl::lomode $mode]} \
1452
error "Large Object open failed\n$result"
1454
if {$result == -1} {
1455
error "Large Object open failed"
1460
# Close large object file descriptor.
1461
proc pg_lo_close {db lofd} {
1462
if {[catch {pg_callfn_int $db lo_close I $lofd} result]} {
1463
error "Large Object close failed\n$result"
1468
# Delete large object:
1469
proc pg_lo_unlink {db loid} {
1470
if {[catch {pg_callfn_int $db lo_unlink I $loid} result]} {
1471
error "Large Object unlink failed\n$result"
1476
# Read from large object.
1477
# Note: The original PostgreSQL documentation says it returns -1 on error,
1478
# which is a bad idea since you can't get to the error message. But it's
1479
# probably too late to change it, so we remain bug compatible.
1480
proc pg_lo_read {db lofd buf_name maxlen} {
1482
if {[catch {pg_callfn $db loread buf "I I" $lofd $maxlen} result]} {
1488
# Write to large object. At most $len bytes are written.
1489
# See note above on pg_lo_read error return.
1490
proc pg_lo_write {db lofd buf len} {
1491
if {[set buflen [string length $buf]] < $len} {
1494
if {[catch {pg_callfn_int $db lowrite "I $len" $lofd $buf} result]} {
1500
# Seek to offset inside large object:
1501
proc pg_lo_lseek {db lofd offset whence} {
1503
SEEK_SET { set iwhence 0 }
1504
SEEK_CUR { set iwhence 1 }
1505
SEEK_END { set iwhence 2 }
1506
default { error "'whence' must be SEEK_SET, SEEK_CUR, or SEEK_END" }
1508
if {[catch {pg_callfn_int $db lo_lseek "I I I" $lofd $offset $iwhence} \
1510
error "Large Object seek failed\n$result"
1515
# Return location of file offset in large object:
1516
proc pg_lo_tell {db lofd} {
1517
if {[catch {pg_callfn_int $db lo_tell I $lofd} result]} {
1518
error "Large Object tell offset failed\n$result"
1523
# Import large object. Wrapper for lo_creat, lo_open, lo_write.
1524
# Returns Large Object OID, which should be stored in a table somewhere.
1525
proc pg_lo_import {db filename} {
1526
if {[catch {open $filename} f]} {
1527
error "Large object import of $filename failed\n$f"
1529
fconfigure $f -translation binary
1530
set loid [pg_lo_creat $db INV_READ|INV_WRITE]
1531
set lofd [pg_lo_open $db $loid w]
1533
set buf [read $f 32768]
1534
if {[set len [string length $buf]] == 0} break
1535
if {[pg_lo_write $db $lofd $buf $len] != $len} {
1536
error "Large Object import failed to write $len bytes"
1539
pg_lo_close $db $lofd
1544
# Export large object. Wrapper for lo_open, lo_read.
1545
proc pg_lo_export {db loid filename} {
1546
if {[catch {open $filename w} f]} {
1547
error "Large object export to $filename failed\n$f"
1549
fconfigure $f -translation binary
1550
if {[catch {pg_lo_open $db $loid r} lofd]} {
1551
error "Large Object export to $filename failed\n$lofd"
1553
while {[set len [pg_lo_read $db $lofd buf 32768]] > 0} {
1554
puts -nonewline $f $buf
1556
pg_lo_close $db $lofd
1560
# === MD5 Checksum procedures for password authentication ===
1562
# Coded in Tcl by ljb <lbayuk@mindspring.com>, using these sources:
1564
# PostgreSQL: src/backend/libpq/md5.c
1565
# If you want a better/faster MD5 implementation, see tcllib.
1567
namespace eval md5 { }
1569
# Round 1 helper, e.g.:
1570
# a = b + ROT_LEFT((a + F(b, c, d) + X[0] + 0xd76aa478), 7)
1571
# p1 p2 p1 p3 p4 p5 p6 p7
1572
# Where F(x,y,z) = (x & y) | (~x & z)
1574
proc md5::round1 {p1 p2 p3 p4 p5 p6 p7} {
1575
set r [expr {$p2 + ($p1 & $p3 | ~$p1 & $p4) + $p5 + $p6}]
1576
return [expr {$p1 + ($r << $p7 | (($r >> (32 - $p7)) & ((1 << $p7) - 1)))}]
1579
# Round 2 helper, e.g.:
1580
# a = b + ROT_LEFT((a + G(b, c, d) + X[1] + 0xf61e2562), 5)
1581
# p1 p2 p1 p3 p4 p5 p6 p7
1582
# Where G(x,y,z) = (x & z) | (y & ~z)
1584
proc md5::round2 {p1 p2 p3 p4 p5 p6 p7} {
1585
set r [expr {$p2 + ($p1 & $p4 | $p3 & ~$p4) + $p5 + $p6}]
1586
return [expr {$p1 + ($r << $p7 | (($r >> (32 - $p7)) & ((1 << $p7) - 1)))}]
1589
# Round 3 helper, e.g.:
1590
# a = b + ROT_LEFT((a + H(b, c, d) + X[5] + 0xfffa3942), 4)
1591
# p1 p2 p1 p3 p4 p5 p6 p7
1592
# Where H(x, y, z) = x ^ y ^ z
1594
proc md5::round3 {p1 p2 p3 p4 p5 p6 p7} {
1595
set r [expr {$p2 + ($p1 ^ $p3 ^ $p4) + $p5 + $p6}]
1596
return [expr {$p1 + ($r << $p7 | (($r >> (32 - $p7)) & ((1 << $p7) - 1)))}]
1599
# Round 4 helper, e.g.:
1600
# a = b + ROT_LEFT((a + I(b, c, d) + X[0] + 0xf4292244), 6)
1601
# p1 p2 p1 p3 p4 p5 p6 p7
1602
# Where I(x, y, z) = y ^ (x | ~z)
1604
proc md5::round4 {p1 p2 p3 p4 p5 p6 p7} {
1605
set r [expr {$p2 + ($p3 ^ ($p1 | ~$p4)) + $p5 + $p6}]
1606
return [expr {$p1 + ($r << $p7 | (($r >> (32 - $p7)) & ((1 << $p7) - 1)))}]
1609
# Do one set of rounds. Updates $state(0:3) with results from $x(0:16).
1610
proc md5::round {x_name state_name} {
1611
upvar $x_name x $state_name state
1617
# Round 1, steps 1-16
1618
set a [round1 $b $a $c $d $x(0) 0xd76aa478 7]
1619
set d [round1 $a $d $b $c $x(1) 0xe8c7b756 12]
1620
set c [round1 $d $c $a $b $x(2) 0x242070db 17]
1621
set b [round1 $c $b $d $a $x(3) 0xc1bdceee 22]
1622
set a [round1 $b $a $c $d $x(4) 0xf57c0faf 7]
1623
set d [round1 $a $d $b $c $x(5) 0x4787c62a 12]
1624
set c [round1 $d $c $a $b $x(6) 0xa8304613 17]
1625
set b [round1 $c $b $d $a $x(7) 0xfd469501 22]
1626
set a [round1 $b $a $c $d $x(8) 0x698098d8 7]
1627
set d [round1 $a $d $b $c $x(9) 0x8b44f7af 12]
1628
set c [round1 $d $c $a $b $x(10) 0xffff5bb1 17]
1629
set b [round1 $c $b $d $a $x(11) 0x895cd7be 22]
1630
set a [round1 $b $a $c $d $x(12) 0x6b901122 7]
1631
set d [round1 $a $d $b $c $x(13) 0xfd987193 12]
1632
set c [round1 $d $c $a $b $x(14) 0xa679438e 17]
1633
set b [round1 $c $b $d $a $x(15) 0x49b40821 22]
1635
# Round 2, steps 17-32
1636
set a [round2 $b $a $c $d $x(1) 0xf61e2562 5]
1637
set d [round2 $a $d $b $c $x(6) 0xc040b340 9]
1638
set c [round2 $d $c $a $b $x(11) 0x265e5a51 14]
1639
set b [round2 $c $b $d $a $x(0) 0xe9b6c7aa 20]
1640
set a [round2 $b $a $c $d $x(5) 0xd62f105d 5]
1641
set d [round2 $a $d $b $c $x(10) 0x02441453 9]
1642
set c [round2 $d $c $a $b $x(15) 0xd8a1e681 14]
1643
set b [round2 $c $b $d $a $x(4) 0xe7d3fbc8 20]
1644
set a [round2 $b $a $c $d $x(9) 0x21e1cde6 5]
1645
set d [round2 $a $d $b $c $x(14) 0xc33707d6 9]
1646
set c [round2 $d $c $a $b $x(3) 0xf4d50d87 14]
1647
set b [round2 $c $b $d $a $x(8) 0x455a14ed 20]
1648
set a [round2 $b $a $c $d $x(13) 0xa9e3e905 5]
1649
set d [round2 $a $d $b $c $x(2) 0xfcefa3f8 9]
1650
set c [round2 $d $c $a $b $x(7) 0x676f02d9 14]
1651
set b [round2 $c $b $d $a $x(12) 0x8d2a4c8a 20]
1653
# Round 3, steps 33-48
1654
set a [round3 $b $a $c $d $x(5) 0xfffa3942 4]
1655
set d [round3 $a $d $b $c $x(8) 0x8771f681 11]
1656
set c [round3 $d $c $a $b $x(11) 0x6d9d6122 16]
1657
set b [round3 $c $b $d $a $x(14) 0xfde5380c 23]
1658
set a [round3 $b $a $c $d $x(1) 0xa4beea44 4]
1659
set d [round3 $a $d $b $c $x(4) 0x4bdecfa9 11]
1660
set c [round3 $d $c $a $b $x(7) 0xf6bb4b60 16]
1661
set b [round3 $c $b $d $a $x(10) 0xbebfbc70 23]
1662
set a [round3 $b $a $c $d $x(13) 0x289b7ec6 4]
1663
set d [round3 $a $d $b $c $x(0) 0xeaa127fa 11]
1664
set c [round3 $d $c $a $b $x(3) 0xd4ef3085 16]
1665
set b [round3 $c $b $d $a $x(6) 0x04881d05 23]
1666
set a [round3 $b $a $c $d $x(9) 0xd9d4d039 4]
1667
set d [round3 $a $d $b $c $x(12) 0xe6db99e5 11]
1668
set c [round3 $d $c $a $b $x(15) 0x1fa27cf8 16]
1669
set b [round3 $c $b $d $a $x(2) 0xc4ac5665 23]
1671
# Round 4, steps 49-64
1672
set a [round4 $b $a $c $d $x(0) 0xf4292244 6]
1673
set d [round4 $a $d $b $c $x(7) 0x432aff97 10]
1674
set c [round4 $d $c $a $b $x(14) 0xab9423a7 15]
1675
set b [round4 $c $b $d $a $x(5) 0xfc93a039 21]
1676
set a [round4 $b $a $c $d $x(12) 0x655b59c3 6]
1677
set d [round4 $a $d $b $c $x(3) 0x8f0ccc92 10]
1678
set c [round4 $d $c $a $b $x(10) 0xffeff47d 15]
1679
set b [round4 $c $b $d $a $x(1) 0x85845dd1 21]
1680
set a [round4 $b $a $c $d $x(8) 0x6fa87e4f 6]
1681
set d [round4 $a $d $b $c $x(15) 0xfe2ce6e0 10]
1682
set c [round4 $d $c $a $b $x(6) 0xa3014314 15]
1683
set b [round4 $c $b $d $a $x(13) 0x4e0811a1 21]
1684
set a [round4 $b $a $c $d $x(4) 0xf7537e82 6]
1685
set d [round4 $a $d $b $c $x(11) 0xbd3af235 10]
1686
set c [round4 $d $c $a $b $x(2) 0x2ad7d2bb 15]
1687
set b [round4 $c $b $d $a $x(9) 0xeb86d391 21]
1695
# Pad out buffer per MD5 spec:
1696
proc md5::pad {buf_name} {
1700
set len [string length $buf]
1701
# Length in bits as 2 32 bit words:
1702
set len64hi [expr {$len >> 29 & 7}]
1703
set len64lo [expr {$len << 3}]
1705
# Append 1 special byte, then append 0 or more 0 bytes until
1706
# (length in bytes % 64) == 56
1707
set pad [expr {64 - ($len + 8) % 64}]
1708
append buf [binary format a$pad "\x80"]
1710
# Append the length in bits as a 64 bit value, low bytes first.
1711
append buf [binary format i1i1 $len64lo $len64hi]
1715
# Calculate MD5 Digest over a string, return as 32 hex digit string.
1716
proc md5::digest {buf} {
1717
# This is 0123456789abcdeffedcba9876543210 in byte-swapped order:
1718
set state(0) 0x67452301
1719
set state(1) 0xEFCDAB89
1720
set state(2) 0x98BADCFE
1721
set state(3) 0x10325476
1723
# Pad buffer per RFC to exact multiple of 64 bytes.
1726
# Calculate digest in 64 byte chunks:
1730
binary scan $buf c* bytes
1731
# Unclear, but the data seems to get byte swapped here.
1733
set word [expr {$c << 24 | ($word >> 8 & 0xffffff) }]
1734
if {[incr nbytes] == 4} {
1736
set x($nwords) $word
1738
if {[incr nwords] == 16} {
1745
# Result is state(0:3), but each word is taken low byte first.
1747
for {set i 0} {$i <= 3} {incr i} {
1749
append result [format %02x%02x%02x%02x \
1751
[expr {$w >> 8 & 255}] \
1752
[expr {$w >> 16 & 255}] \
1753
[expr {$w >> 24 & 255}]]
1757
package provide pgintcl $pgtcl::version