Child: [025c79] (diff)

Download this file

sqlite3tcsq.tcl    295 lines (268 with data), 9.0 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
# @(#$Id$ (C) 2013 J.F. Dockes
#
# Permission to use, copy, modify, distribute, and sell this software
# and its documentation for any purpose is hereby granted without fee,
# provided that the above copyright notice and this permission notice
# appear in all copies of the software and related documentation.
#
# THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
# EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
# WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
#
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
# INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT
# ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY,
# ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
# SOFTWARE.
#
# The low level access layer for tcsq when using sqlite3.
# The SQLDBTYPE should be set to SQLITE3 for this to be used.
package provide SQLITE3tcsq 0.1
package require sqlite3
#set tcsqdebuglevel 2
### SQLITE3 CODE############################################
# Pseudo cursor management
array set _SQLITE3stmts {}
array set _SQLITE3cursors {}
# Open a connection. This is normally used to connect and identify to
# a database server, without mention of a specific database. This has
# no meaning for SQLITE3
set _sql3hdlnum 0
proc SQLITE3connect {{host ""} {user ""} {passwd ""}} {
global _sql3hdlnum
set _sql3hdlnum [expr {$_sql3hdlnum + 1}]
return "sql3hdl$_sql3hdlnum"
}
# Connect to a database.
proc SQLITE3use {hdl database} {
tcsqdebug "SQLITE3use: '$hdl', '$database'"
set cmd [list sqlite3 $hdl $database]
tcsqdebug "SQLITE3use: '$cmd'" 2
eval $cmd
return $hdl
}
# Open a select statement and return a cursor. Sqlite really has no
# notion of this, it returns the whole result set in return from
# statement evaluation. So we have to store the result and the current pointer.
proc SQLITE3opensel {hdl stmt} {
global _SQLITE3stmts _SQLITE3cursors
tcsqdebug "SQLITE3opensel: hdl '$hdl', stmt '$stmt'"
set ok 0
for {set i 1} {$i < 100000} {incr i} {
set qry "qry$i"
if {![info exists _SQLITE3stmts($qry)]} {
set _SQLITE3stmts($qry) []
set _SQLITE3cursors($qry) -1
set ok 1
break
}
}
if {$ok == 0} {
return -code error "SQLITE3opensel: can't allocate statement handle"
}
$hdl eval $stmt resarray {
# resarray[*] has the column names in appropriate order
set row []
foreach col $resarray(*) {
lappend row $resarray($col)
}
lappend _SQLITE3stmts($qry) $row
}
return $qry
}
proc SQLITE3next {qry} {
global _SQLITE3stmts _SQLITE3cursors
incr _SQLITE3cursors($qry)
set res [lindex $_SQLITE3stmts($qry) $_SQLITE3cursors($qry)]
return $res
}
proc SQLITE3rew {qry} {
global _SQLITE3cursors
set _SQLITE3cursors($qry) -1
}
proc SQLITE3closel {qry} {
tcsqdebug "SQLITE3closel: qry '$qry'"
global _SQLITE3stmts _SQLITE3cursors
unset _SQLITE3stmts($qry)
unset _SQLITE3cursors($qry)
}
proc SQLITE3exec {hdl stmt} {
tcsqdebug "SQLITE3exec: hdl '$hdl', stmt '$stmt'"
$hdl eval $stmt
set res [$hdl changes]
tcsqdebug "SQLITE3exec: returning: $res"
return $res
}
# Retrieve auto_increment value for the last insert.
# Don't know how portable this, probably not much. Works with MySQL
proc SQLITE3insertid {hdl} {
return $hdl last_insert_rowid
}
# Retrieve unique id for the specified table. This should not ever be called
proc SQLITE3uniqueid {hdl tbl} {
return ""
}
proc SQLITE3discon {hdl} {
tcsqdebug "SQLITE3discon: hdl '$hdl'"
$hdl close
}
proc SQLITE3tabinfo {hdl} {
return [$hdl eval "select name from sqlite_master where type='table'"]
}
# Extract type and length information from sqlite3 pragma table_info "type"
# column
# typ[(len)]
proc SQLITE3gettyplen {typlen typnm lennm} {
upvar $typnm typ
upvar $lennm len
tcsqdebug "SQLITE3gettyplen: '$typlen'"
set parO [string first {(} $typlen]
if {$parO < 0} {
set typ [string tolower [lindex $typlen 0]]
set ll {}
} else {
set typ [string tolower [string range $typlen 0 [expr {$parO -1}]]]
set parC [string first {)} $typlen]
incr parO
incr parC -1
set ll [split [string range $typlen $parO $parC] ,]
}
#puts "SQLITE3gettyplen: typlen '$typlen', typ '$typ', ll '$ll'"
switch $typ {
date {set len 10}
time {set len 8}
datetime {set len 19}
timestamp {set len 10}
enum -
default {set len [lindex $ll 0]}
}
# Catch weird cases like blob, etc... and unexpected ones... Set
# arbitrary length.
if {$len == ""} {
set len 13
}
tcsqdebug "col typlen '$typlen', typ '$typ', len '$len'"
}
# One thing that we'd like to do, but don't know how is retrieve the
# "auto_increment attribute.
proc SQLITE3colinfo {hdl tbl arnm} {
tcsqdebug "SQLITE3colinfo hdl '$hdl' tbl '$tbl' arnm '$arnm'"
upvar $arnm ar
global sqlsc_def_maxlen env
set primkeycols {}
# Retrieve column info. List elements:
# 0 col# (cid) / 1 name / 2 type / 3 notnull / 4 dflt_value / 5 pk
$hdl eval "pragma table_info($tbl)" myar {
tcsqdebug "table_info row"
set nm [string tolower $myar(name)]
lappend allcols $nm
SQLITE3gettyplen $myar(type) typ($nm) len($nm)
if {$myar(pk)} {
lappend primkeycols $nm
if {$typ($nm) == "int"} {
set ar(tabcolserial) $nm
}
}
}
# Build the column list for the screen:
if {![info exists ar(columns)]} {
# If it was not specified, we use all columns
set ar(columns) $allcols
set autocols 1
} else {
# The user-specified list may include fully qualified
# names. Compute the list of simple column names.
# scol == "" may happen because of the "\n" in the column list
foreach col $ar(columns) {
set scol [_tcsqsimplecolname $tbl $col]
if {$scol != ""} {
lappend scols [string tolower $scol]
}
}
set autocols 0
}
# Possibly build updateindex from the primary key, if not
# specified by the caller.
# If the column list was specified, but not the updateindex list,
# we set the latter only if all its components are included in the
# column list.
if {![info exists ar(updateindex)]} {
set ar(updateindex) $primkeycols
if {$autocols == 0} {
foreach col $primkeycols {
if {[lsearch -exact $scols $col] == -1} {
unset ar(updateindex)
break
}
}
}
}
catch {tcsqdebug "$tbl: updateindex: $ar(updateindex)" 1}
# Set the column types and lengths in the array
foreach col $ar(columns) {
# Get the simple name and check that it is for this table
set scol [_tcsqsimplecolname $tbl $col]
if {$scol == "" || ![info exists typ($scol)]} {
# Column probably from another table
tcsqdebug "No type info for column '$scol'"
continue
}
# In all cases, remember type and length as from db
set ar(sqlsc_${col}_dbtype) $typ($scol)
set ar(sqlsc_${col}_dblen) $len($scol)
# puts "$col: Dbtyp: $typ($scol), Dblen: $len($scol)"
set typind "sqlsc_${col}_type"
set lenind "sqlsc_${col}_len"
if {![info exists ar($lenind)]} {
set ar($lenind) $len($scol)
# puts "$col: length not preset, set to $ar($lenind)"
if {$ar($lenind) > $sqlsc_def_maxlen} {
# puts "$col: limiting width to $sqlsc_def_maxlen"
set ar($lenind) $sqlsc_def_maxlen
}
}
# SQLITE3 types ?
switch [set lowtyp [string tolower $typ($scol)]] {
char -
varchar -
longvarchar {
set ar($typind) "char"
# We don't do upshift automatically with mysql except
# in CDKIT where we need to stay compatible with
# informix
if {$autocols && [info exists env(CDKITDB)]} {
lappend ar(upshiftcols) $col
}
}
date -
datetime -
timestamp {
set ar($typind) $lowtyp
}
default {
set ar($typind) "bin"
}
}
tcsqdebug "name: $col, typ $ar($typind) len $ar($lenind)" 2
}
catch {tcsqdebug "$tbl: tabcolserial: $ar(tabcolserial)" 1}
}
proc SQLITE3quotequote {in} {
regsub -all "'" "$in" "''" out
return $out
}
# Quote bad chars in a text blob (which is a tcl string, no need to
# worry about zeros).
# note that we quote \ first, else we are going to requote those introduced
# by further operations !
# This is duplicated from the MYSQL module, not sure at all it is correct here.
proc SQLITE3quoteblob {blb} {
# puts "quoteblob: in: --$blb--"
regsub -all {\\} $blb {\\\\} blb
regsub -all {'} $blb {''} blb
regsub -all {"} $blb {\\"} blb
# puts "quoteblob: out: --$blb--"
return $blb
}
### END SQLITE3 CODE ############################################