jpayne@68
|
1 # tdbcmysql.tcl --
|
jpayne@68
|
2 #
|
jpayne@68
|
3 # Class definitions and Tcl-level methods for the tdbc::mysql bridge.
|
jpayne@68
|
4 #
|
jpayne@68
|
5 # Copyright (c) 2008 by Kevin B. Kenny
|
jpayne@68
|
6 # See the file "license.terms" for information on usage and redistribution
|
jpayne@68
|
7 # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
jpayne@68
|
8 #
|
jpayne@68
|
9 # RCS: @(#) $Id: tdbcmysql.tcl,v 1.47 2008/02/27 02:08:27 kennykb Exp $
|
jpayne@68
|
10 #
|
jpayne@68
|
11 #------------------------------------------------------------------------------
|
jpayne@68
|
12
|
jpayne@68
|
13 package require tdbc
|
jpayne@68
|
14
|
jpayne@68
|
15 ::namespace eval ::tdbc::mysql {
|
jpayne@68
|
16
|
jpayne@68
|
17 namespace export connection datasources drivers
|
jpayne@68
|
18
|
jpayne@68
|
19 }
|
jpayne@68
|
20
|
jpayne@68
|
21 #------------------------------------------------------------------------------
|
jpayne@68
|
22 #
|
jpayne@68
|
23 # tdbc::mysql::connection --
|
jpayne@68
|
24 #
|
jpayne@68
|
25 # Class representing a connection to a database through MYSQL.
|
jpayne@68
|
26 #
|
jpayne@68
|
27 #-------------------------------------------------------------------------------
|
jpayne@68
|
28
|
jpayne@68
|
29 ::oo::class create ::tdbc::mysql::connection {
|
jpayne@68
|
30
|
jpayne@68
|
31 superclass ::tdbc::connection
|
jpayne@68
|
32
|
jpayne@68
|
33 # The constructor is written in C. It takes alternating keywords
|
jpayne@68
|
34 # and values pairs as its argumenta. (See the manual page for the
|
jpayne@68
|
35 # available options.)
|
jpayne@68
|
36
|
jpayne@68
|
37 variable foreignKeysStatement
|
jpayne@68
|
38
|
jpayne@68
|
39 # The 'statementCreate' method delegates to the constructor of the
|
jpayne@68
|
40 # statement class
|
jpayne@68
|
41
|
jpayne@68
|
42 forward statementCreate ::tdbc::mysql::statement create
|
jpayne@68
|
43
|
jpayne@68
|
44 # The 'columns' method returns a dictionary describing the tables
|
jpayne@68
|
45 # in the database
|
jpayne@68
|
46
|
jpayne@68
|
47 method columns {table {pattern %}} {
|
jpayne@68
|
48
|
jpayne@68
|
49 # To return correct lengths of CHARACTER and BINARY columns,
|
jpayne@68
|
50 # we need to know the maximum lengths of characters in each
|
jpayne@68
|
51 # collation. We cache this information only once, on the first
|
jpayne@68
|
52 # call to 'columns'.
|
jpayne@68
|
53
|
jpayne@68
|
54 if {[my NeedCollationInfo]} {
|
jpayne@68
|
55 my SetCollationInfo {*}[my allrows -as lists {
|
jpayne@68
|
56 SELECT coll.id, cs.maxlen
|
jpayne@68
|
57 FROM INFORMATION_SCHEMA.COLLATIONS coll,
|
jpayne@68
|
58 INFORMATION_SCHEMA.CHARACTER_SETS cs
|
jpayne@68
|
59 WHERE cs.CHARACTER_SET_NAME = coll.CHARACTER_SET_NAME
|
jpayne@68
|
60 ORDER BY coll.id DESC
|
jpayne@68
|
61 }]
|
jpayne@68
|
62 }
|
jpayne@68
|
63
|
jpayne@68
|
64 return [my Columns $table $pattern]
|
jpayne@68
|
65 }
|
jpayne@68
|
66
|
jpayne@68
|
67 # The 'preparecall' method gives a portable interface to prepare
|
jpayne@68
|
68 # calls to stored procedures. It delegates to 'prepare' to do the
|
jpayne@68
|
69 # actual work.
|
jpayne@68
|
70
|
jpayne@68
|
71 method preparecall {call} {
|
jpayne@68
|
72 regexp {^[[:space:]]*(?:([A-Za-z_][A-Za-z_0-9]*)[[:space:]]*=)?(.*)} \
|
jpayne@68
|
73 $call -> varName rest
|
jpayne@68
|
74 if {$varName eq {}} {
|
jpayne@68
|
75 my prepare "CALL $rest"
|
jpayne@68
|
76 } else {
|
jpayne@68
|
77 my prepare \\{:$varName=$rest\\}
|
jpayne@68
|
78 }
|
jpayne@68
|
79 }
|
jpayne@68
|
80
|
jpayne@68
|
81 # The 'init', 'begintransaction', 'commit, 'rollback', 'tables'
|
jpayne@68
|
82 # 'NeedCollationInfo', 'SetCollationInfo', and 'Columns' methods
|
jpayne@68
|
83 # are implemented in C.
|
jpayne@68
|
84
|
jpayne@68
|
85 # The 'BuildForeignKeysStatements' method builds a SQL statement to
|
jpayne@68
|
86 # retrieve the foreign keys from a database. (It executes once the
|
jpayne@68
|
87 # first time the 'foreignKeys' method is executed, and retains the
|
jpayne@68
|
88 # prepared statements for reuse.) It is slightly nonstandard because
|
jpayne@68
|
89 # MYSQL doesn't name the PRIMARY constraints uniquely.
|
jpayne@68
|
90
|
jpayne@68
|
91 method BuildForeignKeysStatement {} {
|
jpayne@68
|
92
|
jpayne@68
|
93 foreach {exists1 clause1} {
|
jpayne@68
|
94 0 {}
|
jpayne@68
|
95 1 { AND fkc.REFERENCED_TABLE_NAME = :primary}
|
jpayne@68
|
96 } {
|
jpayne@68
|
97 foreach {exists2 clause2} {
|
jpayne@68
|
98 0 {}
|
jpayne@68
|
99 1 { AND fkc.TABLE_NAME = :foreign}
|
jpayne@68
|
100 } {
|
jpayne@68
|
101 set stmt [my prepare "
|
jpayne@68
|
102 SELECT rc.CONSTRAINT_SCHEMA AS \"foreignConstraintSchema\",
|
jpayne@68
|
103 rc.CONSTRAINT_NAME AS \"foreignConstraintName\",
|
jpayne@68
|
104 rc.UPDATE_RULE AS \"updateAction\",
|
jpayne@68
|
105 rc.DELETE_RULE AS \"deleteAction\",
|
jpayne@68
|
106 fkc.REFERENCED_TABLE_SCHEMA AS \"primarySchema\",
|
jpayne@68
|
107 fkc.REFERENCED_TABLE_NAME AS \"primaryTable\",
|
jpayne@68
|
108 fkc.REFERENCED_COLUMN_NAME AS \"primaryColumn\",
|
jpayne@68
|
109 fkc.TABLE_SCHEMA AS \"foreignSchema\",
|
jpayne@68
|
110 fkc.TABLE_NAME AS \"foreignTable\",
|
jpayne@68
|
111 fkc.COLUMN_NAME AS \"foreignColumn\",
|
jpayne@68
|
112 fkc.ORDINAL_POSITION AS \"ordinalPosition\"
|
jpayne@68
|
113 FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc
|
jpayne@68
|
114 INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE fkc
|
jpayne@68
|
115 ON fkc.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
|
jpayne@68
|
116 AND fkc.CONSTRAINT_SCHEMA = rc.CONSTRAINT_SCHEMA
|
jpayne@68
|
117 WHERE 1=1
|
jpayne@68
|
118 $clause1
|
jpayne@68
|
119 $clause2
|
jpayne@68
|
120 "]
|
jpayne@68
|
121 dict set foreignKeysStatement $exists1 $exists2 $stmt
|
jpayne@68
|
122 }
|
jpayne@68
|
123 }
|
jpayne@68
|
124 }
|
jpayne@68
|
125 }
|
jpayne@68
|
126
|
jpayne@68
|
127 #------------------------------------------------------------------------------
|
jpayne@68
|
128 #
|
jpayne@68
|
129 # tdbc::mysql::statement --
|
jpayne@68
|
130 #
|
jpayne@68
|
131 # The class 'tdbc::mysql::statement' models one statement against a
|
jpayne@68
|
132 # database accessed through an MYSQL connection
|
jpayne@68
|
133 #
|
jpayne@68
|
134 #------------------------------------------------------------------------------
|
jpayne@68
|
135
|
jpayne@68
|
136 ::oo::class create ::tdbc::mysql::statement {
|
jpayne@68
|
137
|
jpayne@68
|
138 superclass ::tdbc::statement
|
jpayne@68
|
139
|
jpayne@68
|
140 # The 'resultSetCreate' method forwards to the constructor of the
|
jpayne@68
|
141 # result set.
|
jpayne@68
|
142
|
jpayne@68
|
143 forward resultSetCreate ::tdbc::mysql::resultset create
|
jpayne@68
|
144
|
jpayne@68
|
145 # Methods implemented in C:
|
jpayne@68
|
146 #
|
jpayne@68
|
147 # constructor connection SQLCode
|
jpayne@68
|
148 # The constructor accepts the handle to the connection and the SQL code
|
jpayne@68
|
149 # for the statement to prepare. It creates a subordinate namespace to
|
jpayne@68
|
150 # hold the statement's active result sets, and then delegates to the
|
jpayne@68
|
151 # 'init' method, written in C, to do the actual work of preparing the
|
jpayne@68
|
152 # statement.
|
jpayne@68
|
153 # params
|
jpayne@68
|
154 # Returns descriptions of the parameters of a statement.
|
jpayne@68
|
155 # paramtype paramname ?direction? type ?precision ?scale??
|
jpayne@68
|
156 # Declares the type of a parameter in the statement
|
jpayne@68
|
157
|
jpayne@68
|
158 }
|
jpayne@68
|
159
|
jpayne@68
|
160 #------------------------------------------------------------------------------
|
jpayne@68
|
161 #
|
jpayne@68
|
162 # tdbc::mysql::resultset --
|
jpayne@68
|
163 #
|
jpayne@68
|
164 # The class 'tdbc::mysql::resultset' models the result set that is
|
jpayne@68
|
165 # produced by executing a statement against an MYSQL database.
|
jpayne@68
|
166 #
|
jpayne@68
|
167 #------------------------------------------------------------------------------
|
jpayne@68
|
168
|
jpayne@68
|
169 ::oo::class create ::tdbc::mysql::resultset {
|
jpayne@68
|
170
|
jpayne@68
|
171 superclass ::tdbc::resultset
|
jpayne@68
|
172
|
jpayne@68
|
173 # Methods implemented in C include:
|
jpayne@68
|
174
|
jpayne@68
|
175 # constructor statement ?dictionary?
|
jpayne@68
|
176 # -- Executes the statement against the database, optionally providing
|
jpayne@68
|
177 # a dictionary of substituted parameters (default is to get params
|
jpayne@68
|
178 # from variables in the caller's scope).
|
jpayne@68
|
179 # columns
|
jpayne@68
|
180 # -- Returns a list of the names of the columns in the result.
|
jpayne@68
|
181 # nextdict
|
jpayne@68
|
182 # -- Stores the next row of the result set in the given variable in
|
jpayne@68
|
183 # the caller's scope as a dictionary whose keys are
|
jpayne@68
|
184 # column names and whose values are column values, or else
|
jpayne@68
|
185 # as a list of cells.
|
jpayne@68
|
186 # nextlist
|
jpayne@68
|
187 # -- Stores the next row of the result set in the given variable in
|
jpayne@68
|
188 # the caller's scope as a list of cells.
|
jpayne@68
|
189 # rowcount
|
jpayne@68
|
190 # -- Returns a count of rows affected by the statement, or -1
|
jpayne@68
|
191 # if the count of rows has not been determined.
|
jpayne@68
|
192
|
jpayne@68
|
193 }
|