ironruby-dbi 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,65 @@
1
+ module DBI
2
+ # Exceptions (borrowed by Python API 2.0)
3
+
4
+ # Base class of all other error exceptions. Use this to catch all DBI
5
+ # errors.
6
+ class Error < RuntimeError
7
+ end
8
+
9
+ # For important warnings like data truncation, etc.
10
+ class Warning < RuntimeError
11
+ end
12
+
13
+ # Exception for errors related to the DBI interface rather than the
14
+ # database itself.
15
+ class InterfaceError < Error
16
+ end
17
+
18
+ # Exception raised if the DBD driver has not specified a mandatory method.
19
+ class NotImplementedError < InterfaceError
20
+ end
21
+
22
+ # Exception for errors related to the database.
23
+ class DatabaseError < Error
24
+ attr_reader :err, :errstr, :state
25
+
26
+ def initialize(errstr="", err=nil, state=nil)
27
+ super(errstr)
28
+ @err, @errstr, @state = err, errstr, state
29
+ end
30
+ end
31
+
32
+ # Exception for errors due to problems with the processed
33
+ # data such as division by zero, numeric value out of range, etc.
34
+ class DataError < DatabaseError
35
+ end
36
+
37
+ # Exception for errors related to the database's operation which are not
38
+ # necessarily under the control of the programmer. This includes such
39
+ # things as unexpected disconnect, datasource name not found, transaction
40
+ # could not be processed, a memory allocation error occured during
41
+ # processing, etc.
42
+ class OperationalError < DatabaseError
43
+ end
44
+
45
+ # Exception raised when the relational integrity of the database
46
+ # is affected, e.g. a foreign key check fails.
47
+ class IntegrityError < DatabaseError
48
+ end
49
+
50
+ # Exception raised when the database encounters an internal error,
51
+ # e.g. the cursor is not valid anymore, the transaction is out of sync.
52
+ class InternalError < DatabaseError
53
+ end
54
+
55
+ # Exception raised for programming errors, e.g. table not found
56
+ # or already exists, syntax error in SQL statement, wrong number
57
+ # of parameters specified, etc.
58
+ class ProgrammingError < DatabaseError
59
+ end
60
+
61
+ # Exception raised if e.g. commit() is called for a database which do not
62
+ # support transactions.
63
+ class NotSupportedError < DatabaseError
64
+ end
65
+ end
@@ -0,0 +1,49 @@
1
+ #
2
+ # Dispatch classes (Handle, DriverHandle, DatabaseHandle and StatementHandle)
3
+ #
4
+
5
+ module DBI
6
+ #
7
+ # Base class for all handles.
8
+ #
9
+ class Handle
10
+ attr_reader :trace_mode, :trace_output
11
+ attr_reader :handle
12
+ attr :convert_types, true
13
+
14
+ def initialize(handle, convert_types=true)
15
+ @handle = handle
16
+ @trace_mode = @trace_output = nil
17
+ @convert_types = convert_types
18
+ end
19
+
20
+ # Please seee DBI.trace.
21
+ def trace(mode=nil, output=nil)
22
+ # FIXME trace
23
+ raise InterfaceError, "the trace module has been removed until it actually works."
24
+ @trace_mode = mode || @trace_mode || DBI::DEFAULT_TRACE_MODE
25
+ @trace_output = output || @trace_output || DBI::DEFAULT_TRACE_OUTPUT
26
+ end
27
+
28
+ #
29
+ # Leverage a driver-specific method. The method name will have "__"
30
+ # prepended to them before calling, and the DBD must define them as
31
+ # such for them to work.
32
+ #
33
+ def func(function, *values)
34
+ if @handle.respond_to?("__" + function.to_s) then
35
+ @handle.send("__" + function.to_s, *values)
36
+ else
37
+ raise InterfaceError, "Driver specific function <#{function}> not available."
38
+ end
39
+ rescue ArgumentError
40
+ raise InterfaceError, "Wrong # of arguments for driver specific function"
41
+ end
42
+
43
+ # error functions?
44
+ end
45
+ end
46
+
47
+ require 'dbi/handles/driver'
48
+ require 'dbi/handles/database'
49
+ require 'dbi/handles/statement'
@@ -0,0 +1,229 @@
1
+ module DBI
2
+ # DatabaseHandle is the interface the consumer sees after connecting to the
3
+ # database via DBI.connect.
4
+ #
5
+ # It is strongly discouraged that DBDs inherit from this class directly;
6
+ # please inherit from the DBI::BaseDatabase instead.
7
+ #
8
+ # Note: almost all methods in this class will raise InterfaceError if the
9
+ # database is not connected.
10
+ class DatabaseHandle < Handle
11
+
12
+ attr_accessor :raise_error
13
+
14
+ # This is the driver name as supplied by the DBD's driver_name method.
15
+ # Its primary utility is in DBI::TypeUtil#convert.
16
+ def driver_name
17
+ return @driver_name.dup if @driver_name
18
+ return nil
19
+ end
20
+
21
+ # Assign the driver name. This can be leveraged to create custom type
22
+ # management via DBI::TypeUtil#convert.
23
+ def driver_name=(name)
24
+ @driver_name = name
25
+ @driver_name.freeze
26
+ end
27
+
28
+ #
29
+ # Boolean if we are still connected to the database. See #ping.
30
+ #
31
+ def connected?
32
+ not @handle.nil?
33
+ end
34
+
35
+ #
36
+ # Disconnect from the database. Will raise InterfaceError if this was
37
+ # already done prior.
38
+ #
39
+ def disconnect
40
+ sanity_check
41
+ @handle.disconnect
42
+ @handle = nil
43
+ end
44
+
45
+ #
46
+ # Prepare a StatementHandle and return it. If given a block, it will
47
+ # supply that StatementHandle as the first argument to the block, and
48
+ # BaseStatement#finish it when the block is done executing.
49
+ #
50
+ def prepare(stmt)
51
+ sanity_check(stmt)
52
+ sth = StatementHandle.new(@handle.prepare(stmt), false, true, @convert_types)
53
+ # FIXME trace sth.trace(@trace_mode, @trace_output)
54
+ sth.dbh = self
55
+ sth.raise_error = raise_error
56
+
57
+ if block_given?
58
+ begin
59
+ yield sth
60
+ ensure
61
+ sth.finish unless sth.finished?
62
+ end
63
+ else
64
+ return sth
65
+ end
66
+ end
67
+
68
+ #
69
+ # Prepare and execute a statement. It has block semantics equivalent to #prepare.
70
+ #
71
+ def execute(stmt, bindvars={})
72
+ sanity_check(stmt)
73
+
74
+ if @convert_types
75
+ bindvars = DBI::Utils::ConvParam.conv_param(driver_name, bindvars)
76
+ end
77
+
78
+ sth = StatementHandle.new(@handle.execute(stmt, bindvars), true, true, @convert_types, true)
79
+ # FIXME trace sth.trace(@trace_mode, @trace_output)
80
+ sth.dbh = self
81
+ sth.raise_error = raise_error
82
+
83
+ if block_given?
84
+ begin
85
+ yield sth
86
+ ensure
87
+ sth.finish unless sth.finished?
88
+ end
89
+ else
90
+ return sth
91
+ end
92
+ end
93
+
94
+ #
95
+ # Perform a statement. This goes straight to the DBD's implementation
96
+ # of #do (and consequently, BaseDatabase#do), and does not work like
97
+ # #execute and #prepare. Should return a row modified count.
98
+ #
99
+ def do(stmt, bindvars={})
100
+ sanity_check(stmt)
101
+ @handle.do(stmt, bindvars)
102
+ #@handle.do(stmt, DBI::Utils::ConvParam.conv_param(driver_name, bindvars))
103
+ end
104
+
105
+ #
106
+ # Executes a statement and returns the first row from the result.
107
+ #
108
+ def select_one(stmt, bindvars={})
109
+ sanity_check(stmt)
110
+ row = nil
111
+ execute(stmt, bindvars) do |sth|
112
+ row = sth.fetch
113
+ end
114
+ row
115
+ end
116
+
117
+ #
118
+ # Executes a statement and returns all rows from the result. If a block
119
+ # is given, it is executed for each row.
120
+ #
121
+ def select_all(stmt, bindvars={}, &p)
122
+ sanity_check(stmt)
123
+ rows = nil
124
+ execute(stmt, bindvars) do |sth|
125
+ if block_given?
126
+ sth.each(&p)
127
+ else
128
+ rows = sth.fetch_all
129
+ end
130
+ end
131
+ return rows
132
+ end
133
+
134
+ #
135
+ # Return the tables available to this DatabaseHandle as an array of strings.
136
+ #
137
+ def tables
138
+ sanity_check
139
+ @handle.tables
140
+ end
141
+
142
+ #
143
+ # Returns the columns of the provided table as an array of ColumnInfo
144
+ # objects. See BaseDatabase#columns for the minimum parameters that
145
+ # this method must provide.
146
+ #
147
+ def columns( table )
148
+ sanity_check
149
+ @handle.columns( table ).collect {|col| ColumnInfo.new(col) }
150
+ end
151
+
152
+ #
153
+ # Attempt to establish if the database is still connected. While
154
+ # #connected? returns the state the DatabaseHandle thinks is true, this
155
+ # is an active operation that will contact the database.
156
+ #
157
+ def ping
158
+ sanity_check
159
+ @handle.ping
160
+ end
161
+
162
+ #
163
+ # Attempt to escape the value, rendering it suitable for inclusion in a SQL statement.
164
+ #
165
+ def quote(value)
166
+ sanity_check
167
+ @handle.quote(value)
168
+ end
169
+
170
+ #
171
+ # Force a commit to the database immediately.
172
+ #
173
+ def commit
174
+ sanity_check
175
+ @handle.commit
176
+ end
177
+
178
+ #
179
+ # Force a rollback to the database immediately.
180
+ #
181
+ def rollback
182
+ sanity_check
183
+ @handle.rollback
184
+ end
185
+
186
+ #
187
+ # Commits, runs the block provided, yielding the DatabaseHandle as it's
188
+ # argument. If an exception is raised through the block, rollback occurs.
189
+ # Otherwise, commit occurs.
190
+ #
191
+ def transaction
192
+ sanity_check
193
+ raise InterfaceError, "No block given" unless block_given?
194
+
195
+ commit
196
+ begin
197
+ yield self
198
+ commit
199
+ rescue Exception
200
+ rollback
201
+ raise
202
+ end
203
+ end
204
+
205
+ # Get an attribute from the DatabaseHandle.
206
+ def [] (attr)
207
+ sanity_check
208
+ @handle[attr]
209
+ end
210
+
211
+ # Set an attribute on the DatabaseHandle.
212
+ def []= (attr, val)
213
+ sanity_check
214
+ @handle[attr] = val
215
+ end
216
+
217
+ protected
218
+
219
+ def sanity_check(stmt=nil)
220
+ raise InterfaceError, "Database connection was already closed!" if @handle.nil?
221
+ check_statement(stmt) if stmt
222
+ end
223
+
224
+ # basic sanity checks for statements
225
+ def check_statement(stmt)
226
+ raise InterfaceError, "Statement is empty, or contains nothing but whitespace" if stmt !~ /\S/
227
+ end
228
+ end
229
+ end
@@ -0,0 +1,60 @@
1
+ module DBI
2
+ # DriverHandles, while not directly exposed, are essentially the backend
3
+ # for the facade that many DBI root-level methods communicate with.
4
+ class DriverHandle < Handle
5
+
6
+ attr_writer :driver_name
7
+
8
+ # Connect to the database. The DSN will have been parsed at this point
9
+ # and the named parameters should need no explanation.
10
+ #
11
+ # If a block is provided to DBI.connect, the connected DatabaseHandle
12
+ # will be provided as the first argument to the block, and the
13
+ # DatabaseHandle will be disconnected upon block exit.
14
+ #
15
+ def connect(db_args, user, auth, params)
16
+
17
+ user = @handle.default_user[0] if user.nil?
18
+ auth = @handle.default_user[1] if auth.nil?
19
+
20
+ # TODO: what if only one of them is nil?
21
+ #if user.nil? and auth.nil? then
22
+ # user, auth = @handle.default_user
23
+ #end
24
+
25
+ params ||= {}
26
+ new_params = @handle.default_attributes
27
+ params.each {|k,v| new_params[k] = v}
28
+
29
+ if params.has_key?(:_convert_types)
30
+ @convert_types = params[:_convert_types]
31
+ end
32
+
33
+ db = @handle.connect(db_args, user, auth, new_params)
34
+ dbh = DatabaseHandle.new(db, @convert_types)
35
+ # FIXME trace
36
+ # dbh.trace(@trace_mode, @trace_output)
37
+ dbh.driver_name = @driver_name
38
+
39
+ if block_given?
40
+ begin
41
+ yield dbh
42
+ ensure
43
+ dbh.disconnect if dbh.connected?
44
+ end
45
+ else
46
+ return dbh
47
+ end
48
+ end
49
+
50
+ # See BaseDriver#data_sources.
51
+ def data_sources
52
+ @handle.data_sources
53
+ end
54
+
55
+ # See BaseDriver#disconnect_all.
56
+ def disconnect_all
57
+ @handle.disconnect_all
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,408 @@
1
+ module DBI
2
+ #
3
+ # StatementHandle is the interface the consumer sees after successfully
4
+ # issuing a DatabaseHandle#prepare. They may also be exposed through other
5
+ # methods that send statements to the database.
6
+ #
7
+ # Almost all methods in this class will raise InterfaceError if the
8
+ # statement is already finished.
9
+ #
10
+ class StatementHandle < Handle
11
+
12
+ include Enumerable
13
+
14
+ attr_accessor :dbh
15
+ attr_accessor :raise_error
16
+
17
+ def initialize(handle, fetchable=false, prepared=true, convert_types=true, executed=false)
18
+ super(handle)
19
+ @fetchable = fetchable
20
+ @prepared = prepared # only false if immediate execute was used
21
+ @executed = executed # only true if the statement was already executed.
22
+ @cols = nil
23
+ @coltypes = nil
24
+ @convert_types = convert_types
25
+
26
+ if @fetchable
27
+ @row = DBI::Row.new(column_names, column_types, nil, @convert_types)
28
+ else
29
+ @row = nil
30
+ end
31
+ end
32
+
33
+ # Returns true if the StatementHandle has had #finish called on it,
34
+ # explicitly or otherwise.
35
+ def finished?
36
+ @handle.nil?
37
+ end
38
+
39
+ # Returns true if the statement is believed to return data upon #fetch.
40
+ #
41
+ # The current reliability of this (and the concept in general) is
42
+ # suspect.
43
+ def fetchable?
44
+ @fetchable
45
+ end
46
+
47
+ #
48
+ # Instruct successive calls to #fetch to cast the type returned into
49
+ # `type`, for row position `pos`. Like all bind_* calls, `pos` indexes
50
+ # starting at 1.
51
+ #
52
+ # `type` is an object with the DBI::Type calling convention.
53
+ #
54
+ # This call must be called after #execute has successfully ran,
55
+ # otherwise it will raise InterfaceError.
56
+ #
57
+ # Example:
58
+ # # `foo` is an integer and this statement will return two rows.
59
+ # sth = dbh.prepare("select foo from bar")
60
+ # # would raise InterfaceError if called here
61
+ # sth.execute
62
+ #
63
+ # sth.bind_coltype(1, DBI::Type::Varchar)
64
+ # # would normally use DBI::Type::Integer and return a Fixnum. We'll make it a string.
65
+ # sth.fetch => ["1"]
66
+ #
67
+ # # Here we coerce it to Float.
68
+ # sth.bind_coltype(1, DBI::Type::Float)
69
+ # sth.fetch => [1.0]
70
+ # sth.finish
71
+ #
72
+ def bind_coltype(pos, type)
73
+ sanity_check({:prepared => true, :executed => true})
74
+
75
+ coltypes = column_types
76
+
77
+ if (pos - 1) < 1
78
+ raise InterfaceError, "bind positions index starting at 1"
79
+ end
80
+
81
+ coltypes[pos-1] = type
82
+ @row = DBI::Row.new(column_names, coltypes, nil, @convert_types)
83
+ end
84
+
85
+ #
86
+ # Just like BaseStatement#bind_param, but will attempt to convert the
87
+ # type if it's supposed to, adhering to the DBD's current ruleset.
88
+ #
89
+ def bind_param(param, value, attribs=nil)
90
+ sanity_check({ :prepared => true })
91
+
92
+ if @convert_types
93
+ value = DBI::Utils::ConvParam.conv_param(dbh.driver_name, value)[0]
94
+ end
95
+
96
+ @handle.bind_param(param, value, attribs)
97
+ end
98
+
99
+
100
+ # Execute the statement.
101
+ #
102
+ # This generally means that the statement will be sent to the database
103
+ # and some form of result cursor will be obtained, but is ultimately
104
+ # driver-dependent.
105
+ #
106
+ # If arguments are supplied, these are fed to #bind_param.
107
+ def execute(bindvars={})
108
+ cancel # cancel before
109
+ sanity_check({:prepared => true })
110
+
111
+ if @convert_types
112
+ bindvars = DBI::Utils::ConvParam.conv_param(dbh.driver_name, bindvars)
113
+ end
114
+
115
+ @handle.bind_params(bindvars)
116
+ @handle.execute
117
+ @fetchable = true
118
+ @executed = true
119
+
120
+ # TODO:?
121
+ #if @row.nil?
122
+ @row = DBI::Row.new(column_names, column_types, nil, @convert_types)
123
+ #end
124
+ return nil
125
+ end
126
+
127
+ #
128
+ # Finish the statement, causing the database to release all assets
129
+ # related to it (any result cursors, normally).
130
+ #
131
+ # StatementHandles that have already been finished will normally be
132
+ # inoperable and unavailable for further use.
133
+ #
134
+ def finish
135
+ sanity_check
136
+ @handle.finish
137
+ @handle = nil
138
+ end
139
+
140
+ #
141
+ # Cancel the query, closing any open result cursors and truncating any result sets.
142
+ #
143
+ # The difference between this and #finish is that cancelled statements
144
+ # may be re-executed.
145
+ #
146
+ def cancel
147
+ sanity_check
148
+ @handle.cancel if @fetchable
149
+ @fetchable = false
150
+ end
151
+
152
+ #
153
+ # Obtains the column names for this query as an array.
154
+ #
155
+ def column_names
156
+ sanity_check
157
+ return @cols unless @cols.nil?
158
+ @cols = @handle.column_info.collect {|col| col['name'] }
159
+ end
160
+
161
+ #
162
+ # Obtain the type mappings for the columns in this query based on
163
+ # ColumnInfo data on the query.
164
+ #
165
+ # The result will be a position-dependent array of objects that conform
166
+ # to the DBI::Type calling syntax.
167
+ #
168
+ def column_types
169
+ sanity_check
170
+ return @coltypes unless @coltypes.nil?
171
+ @coltypes = @handle.column_info.collect do |col|
172
+ if col['dbi_type']
173
+ col['dbi_type']
174
+ else
175
+ DBI::TypeUtil.type_name_to_module(col['type_name'])
176
+ end
177
+ end
178
+ end
179
+
180
+ #
181
+ # See BaseStatement#column_info.
182
+ #
183
+ def column_info
184
+ sanity_check
185
+ @handle.column_info.collect {|col| ColumnInfo.new(col) }
186
+ end
187
+
188
+ #
189
+ # Should return the row modified count as the result of statement execution.
190
+ #
191
+ # However, some low-level drivers do not supply this information or
192
+ # supply misleading information (> 0 rows for read-only select
193
+ # statements, f.e.)
194
+ #
195
+ def rows
196
+ sanity_check
197
+ @handle.rows
198
+ end
199
+
200
+
201
+ #
202
+ # See BaseStatement#fetch.
203
+ #
204
+ # fetch can also take a block which will be applied to each row in a
205
+ # similar fashion to Enumerable#collect. See #each.
206
+ #
207
+ def fetch(&p)
208
+ sanity_check({ :fetchable => true, :prepared => true, :executed => true })
209
+
210
+ if block_given?
211
+ while (res = @handle.fetch) != nil
212
+ @row = @row.dup
213
+ @row.set_values(res)
214
+ yield @row
215
+ end
216
+ @handle.cancel
217
+ @fetchable = false
218
+ return nil
219
+ else
220
+ res = @handle.fetch
221
+ if res.nil?
222
+ @handle.cancel
223
+ @fetchable = false
224
+ else
225
+ @row = @row.dup
226
+ @row.set_values(res)
227
+ res = @row
228
+ end
229
+ return res
230
+ end
231
+ end
232
+
233
+ #
234
+ # Synonym for #fetch with a block.
235
+ #
236
+ def each(&p)
237
+ sanity_check({:fetchable => true, :prepared => true, :executed => true})
238
+ raise InterfaceError, "No block given" unless block_given?
239
+
240
+ fetch(&p)
241
+ end
242
+
243
+ #
244
+ # Similar to #fetch, but returns Array of Array instead of Array of
245
+ # DBI::Row objects (and therefore does not perform type mapping). This
246
+ # is basically a way to get the raw data from the DBD.
247
+ #
248
+ def fetch_array
249
+ sanity_check({:fetchable => true, :prepared => true, :executed => true})
250
+
251
+ if block_given?
252
+ while (res = @handle.fetch) != nil
253
+ yield res
254
+ end
255
+ @handle.cancel
256
+ @fetchable = false
257
+ return nil
258
+ else
259
+ res = @handle.fetch
260
+ if res.nil?
261
+ @handle.cancel
262
+ @fetchable = false
263
+ end
264
+ return res
265
+ end
266
+ end
267
+
268
+ #
269
+ # Map the columns and results into an Array of Hash resultset.
270
+ #
271
+ # No type conversion is performed here. Expect this to change in 0.6.0.
272
+ #
273
+ def fetch_hash
274
+ sanity_check({:fetchable => true, :prepared => true, :executed => true})
275
+
276
+ cols = column_names
277
+
278
+ if block_given?
279
+ while (row = @handle.fetch) != nil
280
+ hash = {}
281
+ row.each_with_index {|v, i| hash[cols[i]] = v}
282
+ yield hash
283
+ end
284
+ @handle.cancel
285
+ @fetchable = false
286
+ return nil
287
+ else
288
+ row = @handle.fetch
289
+ if row.nil?
290
+ @handle.cancel
291
+ @fetchable = false
292
+ return nil
293
+ else
294
+ hash = {}
295
+ row.each_with_index {|v, i| hash[cols[i]] = v}
296
+ return hash
297
+ end
298
+ end
299
+ end
300
+
301
+ #
302
+ # Fetch `cnt` rows. Result is array of DBI::Row
303
+ #
304
+ def fetch_many(cnt)
305
+ sanity_check({:fetchable => true, :prepared => true, :executed => true})
306
+
307
+ cols = column_names
308
+ rows = @handle.fetch_many(cnt)
309
+ if rows.nil? or rows.empty?
310
+ @handle.cancel
311
+ @fetchable = false
312
+ return []
313
+ else
314
+ return rows.collect{|r| tmp = @row.dup; tmp.set_values(r); tmp }
315
+ end
316
+ end
317
+
318
+ #
319
+ # Fetch the entire result set. Result is array of DBI::Row.
320
+ #
321
+ def fetch_all
322
+ sanity_check({:fetchable => true, :prepared => true, :executed => true})
323
+
324
+ cols = column_names
325
+ fetched_rows = []
326
+
327
+ begin
328
+ while row = fetch do
329
+ fetched_rows.push(row)
330
+ end
331
+ rescue Exception
332
+ end
333
+
334
+ @handle.cancel
335
+ @fetchable = false
336
+
337
+ return fetched_rows
338
+ end
339
+
340
+ #
341
+ # See BaseStatement#fetch_scroll.
342
+ #
343
+ def fetch_scroll(direction, offset=1)
344
+ sanity_check({:fetchable => true, :prepared => true, :executed => true})
345
+
346
+ row = @handle.fetch_scroll(direction, offset)
347
+ if row.nil?
348
+ #@handle.cancel
349
+ #@fetchable = false
350
+ return nil
351
+ else
352
+ @row.set_values(row)
353
+ return @row
354
+ end
355
+ end
356
+
357
+ # Get an attribute from the StatementHandle object.
358
+ def [] (attr)
359
+ sanity_check
360
+ @handle[attr]
361
+ end
362
+
363
+ # Set an attribute on the StatementHandle object.
364
+ def []= (attr, val)
365
+ sanity_check
366
+ @handle[attr] = val
367
+ end
368
+
369
+ protected
370
+
371
+ def sanity_check(params={})
372
+ raise InterfaceError, "Statement was already closed!" if @handle.nil?
373
+
374
+ params.each_key do |key|
375
+ case key
376
+ when :fetchable
377
+ check_fetchable
378
+ when :executed
379
+ check_executed
380
+ when :prepared
381
+ check_prepared
382
+ when :statement
383
+ check_statement(params[:statement])
384
+ end
385
+ end
386
+ end
387
+
388
+ def check_prepared
389
+ raise InterfaceError, "Statement wasn't prepared before." unless @prepared
390
+ end
391
+
392
+ def check_fetchable
393
+ if !@fetchable and @raise_error
394
+ raise InterfaceError, "Statement does not have any data for fetching."
395
+ end
396
+ end
397
+
398
+ def check_executed
399
+ raise InterfaceError, "Statement hasn't been executed yet." unless @executed
400
+ end
401
+
402
+ # basic sanity checks for statements
403
+ def check_statement(stmt)
404
+ raise InterfaceError, "Statement is empty, or contains nothing but whitespace" if stmt !~ /\S/
405
+ end
406
+
407
+ end # class StatementHandle
408
+ end