dbi 0.4.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.
Files changed (45) hide show
  1. data/ChangeLog +3694 -0
  2. data/LICENSE +25 -0
  3. data/README +271 -0
  4. data/bin/dbi +518 -0
  5. data/build/Rakefile.dbi.rb +57 -0
  6. data/examples/test1.pl +39 -0
  7. data/examples/test1.rb +20 -0
  8. data/examples/xmltest.rb +8 -0
  9. data/lib/dbi.rb +323 -0
  10. data/lib/dbi/base_classes.rb +12 -0
  11. data/lib/dbi/base_classes/database.rb +135 -0
  12. data/lib/dbi/base_classes/driver.rb +47 -0
  13. data/lib/dbi/base_classes/statement.rb +167 -0
  14. data/lib/dbi/binary.rb +25 -0
  15. data/lib/dbi/columninfo.rb +106 -0
  16. data/lib/dbi/exceptions.rb +65 -0
  17. data/lib/dbi/handles.rb +49 -0
  18. data/lib/dbi/handles/database.rb +211 -0
  19. data/lib/dbi/handles/driver.rb +60 -0
  20. data/lib/dbi/handles/statement.rb +375 -0
  21. data/lib/dbi/row.rb +249 -0
  22. data/lib/dbi/sql.rb +23 -0
  23. data/lib/dbi/sql/preparedstatement.rb +115 -0
  24. data/lib/dbi/sql_type_constants.rb +75 -0
  25. data/lib/dbi/trace.rb +91 -0
  26. data/lib/dbi/types.rb +158 -0
  27. data/lib/dbi/typeutil.rb +108 -0
  28. data/lib/dbi/utils.rb +60 -0
  29. data/lib/dbi/utils/date.rb +59 -0
  30. data/lib/dbi/utils/tableformatter.rb +112 -0
  31. data/lib/dbi/utils/time.rb +52 -0
  32. data/lib/dbi/utils/timestamp.rb +96 -0
  33. data/lib/dbi/utils/xmlformatter.rb +73 -0
  34. data/test/dbi/tc_columninfo.rb +94 -0
  35. data/test/dbi/tc_date.rb +88 -0
  36. data/test/dbi/tc_dbi.rb +178 -0
  37. data/test/dbi/tc_row.rb +256 -0
  38. data/test/dbi/tc_sqlbind.rb +168 -0
  39. data/test/dbi/tc_statementhandle.rb +16 -0
  40. data/test/dbi/tc_time.rb +77 -0
  41. data/test/dbi/tc_timestamp.rb +142 -0
  42. data/test/dbi/tc_types.rb +220 -0
  43. data/test/dbi/trace.rb +26 -0
  44. data/test/ts_dbi.rb +15 -0
  45. metadata +108 -0
@@ -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,211 @@
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
+ # This is the driver name as supplied by the DBD's driver_name method.
12
+ # Its primary utility is in DBI::TypeUtil#convert.
13
+ def driver_name
14
+ return @driver_name.dup if @driver_name
15
+ return nil
16
+ end
17
+
18
+ # Assign the driver name. This can be leveraged to create custom type
19
+ # management via DBI::TypeUtil#convert.
20
+ def driver_name=(name)
21
+ @driver_name = name
22
+ @driver_name.freeze
23
+ end
24
+
25
+ #
26
+ # Boolean if we are still connected to the database. See #ping.
27
+ #
28
+ def connected?
29
+ not @handle.nil?
30
+ end
31
+
32
+ #
33
+ # Disconnect from the database. Will raise InterfaceError if this was
34
+ # already done prior.
35
+ #
36
+ def disconnect
37
+ raise InterfaceError, "Database connection was already closed!" if @handle.nil?
38
+ @handle.disconnect
39
+ @handle = nil
40
+ end
41
+
42
+ #
43
+ # Prepare a StatementHandle and return it. If given a block, it will
44
+ # supply that StatementHandle as the first argument to the block, and
45
+ # BaseStatement#finish it when the block is done executing.
46
+ #
47
+ def prepare(stmt)
48
+ raise InterfaceError, "Database connection was already closed!" if @handle.nil?
49
+ sth = StatementHandle.new(@handle.prepare(stmt), false, true, @convert_types)
50
+ # FIXME trace sth.trace(@trace_mode, @trace_output)
51
+ sth.dbh = self
52
+
53
+ if block_given?
54
+ begin
55
+ yield sth
56
+ ensure
57
+ sth.finish unless sth.finished?
58
+ end
59
+ else
60
+ return sth
61
+ end
62
+ end
63
+
64
+ #
65
+ # Prepare and execute a statement. It has block semantics equivalent to #prepare.
66
+ #
67
+ def execute(stmt, *bindvars)
68
+ raise InterfaceError, "Database connection was already closed!" if @handle.nil?
69
+
70
+ if @convert_types
71
+ bindvars = DBI::Utils::ConvParam.conv_param(driver_name, *bindvars)
72
+ end
73
+
74
+ sth = StatementHandle.new(@handle.execute(stmt, *bindvars), true, false, @convert_types)
75
+ # FIXME trace sth.trace(@trace_mode, @trace_output)
76
+ sth.dbh = self
77
+
78
+ if block_given?
79
+ begin
80
+ yield sth
81
+ ensure
82
+ sth.finish unless sth.finished?
83
+ end
84
+ else
85
+ return sth
86
+ end
87
+ end
88
+
89
+ #
90
+ # Perform a statement. This goes straight to the DBD's implementation
91
+ # of #do (and consequently, BaseDatabase#do), and does not work like
92
+ # #execute and #prepare. Should return a row modified count.
93
+ #
94
+ def do(stmt, *bindvars)
95
+ raise InterfaceError, "Database connection was already closed!" if @handle.nil?
96
+ @handle.do(stmt, *DBI::Utils::ConvParam.conv_param(driver_name, *bindvars))
97
+ end
98
+
99
+ #
100
+ # Executes a statement and returns the first row from the result.
101
+ #
102
+ def select_one(stmt, *bindvars)
103
+ raise InterfaceError, "Database connection was already closed!" if @handle.nil?
104
+ row = nil
105
+ execute(stmt, *bindvars) do |sth|
106
+ row = sth.fetch
107
+ end
108
+ row
109
+ end
110
+
111
+ #
112
+ # Executes a statement and returns all rows from the result. If a block
113
+ # is given, it is executed for each row.
114
+ #
115
+ def select_all(stmt, *bindvars, &p)
116
+ raise InterfaceError, "Database connection was already closed!" if @handle.nil?
117
+ rows = nil
118
+ execute(stmt, *bindvars) do |sth|
119
+ if block_given?
120
+ sth.each(&p)
121
+ else
122
+ rows = sth.fetch_all
123
+ end
124
+ end
125
+ return rows
126
+ end
127
+
128
+ #
129
+ # Return the tables available to this DatabaseHandle as an array of strings.
130
+ #
131
+ def tables
132
+ raise InterfaceError, "Database connection was already closed!" if @handle.nil?
133
+ @handle.tables
134
+ end
135
+
136
+ #
137
+ # Returns the columns of the provided table as an array of ColumnInfo
138
+ # objects. See BaseDatabase#columns for the minimum parameters that
139
+ # this method must provide.
140
+ #
141
+ def columns( table )
142
+ raise InterfaceError, "Database connection was already closed!" if @handle.nil?
143
+ @handle.columns( table ).collect {|col| ColumnInfo.new(col) }
144
+ end
145
+
146
+ #
147
+ # Attempt to establish if the database is still connected. While
148
+ # #connected? returns the state the DatabaseHandle thinks is true, this
149
+ # is an active operation that will contact the database.
150
+ #
151
+ def ping
152
+ raise InterfaceError, "Database connection was already closed!" if @handle.nil?
153
+ @handle.ping
154
+ end
155
+
156
+ #
157
+ # Attempt to escape the value, rendering it suitable for inclusion in a SQL statement.
158
+ #
159
+ def quote(value)
160
+ raise InterfaceError, "Database connection was already closed!" if @handle.nil?
161
+ @handle.quote(value)
162
+ end
163
+
164
+ #
165
+ # Force a commit to the database immediately.
166
+ #
167
+ def commit
168
+ raise InterfaceError, "Database connection was already closed!" if @handle.nil?
169
+ @handle.commit
170
+ end
171
+
172
+ #
173
+ # Force a rollback to the database immediately.
174
+ #
175
+ def rollback
176
+ raise InterfaceError, "Database connection was already closed!" if @handle.nil?
177
+ @handle.rollback
178
+ end
179
+
180
+ #
181
+ # Commits, runs the block provided, yielding the DatabaseHandle as it's
182
+ # argument. If an exception is raised through the block, rollback occurs.
183
+ # Otherwise, commit occurs.
184
+ #
185
+ def transaction
186
+ raise InterfaceError, "Database connection was already closed!" if @handle.nil?
187
+ raise InterfaceError, "No block given" unless block_given?
188
+
189
+ commit
190
+ begin
191
+ yield self
192
+ commit
193
+ rescue Exception
194
+ rollback
195
+ raise
196
+ end
197
+ end
198
+
199
+ # Get an attribute from the DatabaseHandle.
200
+ def [] (attr)
201
+ raise InterfaceError, "Database connection was already closed!" if @handle.nil?
202
+ @handle[attr]
203
+ end
204
+
205
+ # Set an attribute on the DatabaseHandle.
206
+ def []= (attr, val)
207
+ raise InterfaceError, "Database connection was already closed!" if @handle.nil?
208
+ @handle[attr] = val
209
+ end
210
+ end
211
+ 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,375 @@
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
+
16
+ def initialize(handle, fetchable=false, prepared=true, convert_types=true)
17
+ super(handle)
18
+ @fetchable = fetchable
19
+ @prepared = prepared # only false if immediate execute was used
20
+ @cols = nil
21
+ @coltypes = nil
22
+ @convert_types = convert_types
23
+
24
+ if @fetchable
25
+ @row = DBI::Row.new(column_names, column_types, nil, @convert_types)
26
+ else
27
+ @row = nil
28
+ end
29
+ end
30
+
31
+ # Returns true if the StatementHandle has had #finish called on it,
32
+ # explicitly or otherwise.
33
+ def finished?
34
+ @handle.nil?
35
+ end
36
+
37
+ # Returns true if the statement is believed to return data upon #fetch.
38
+ #
39
+ # The current reliability of this (and the concept in general) is
40
+ # suspect.
41
+ def fetchable?
42
+ @fetchable
43
+ end
44
+
45
+ #
46
+ # Instruct successive calls to #fetch to cast the type returned into
47
+ # `type`, for row position `pos`. Like all bind_* calls, `pos` indexes
48
+ # starting at 1.
49
+ #
50
+ # `type` is an object with the DBI::Type calling convention.
51
+ #
52
+ # This call must be called after #execute has successfully ran,
53
+ # otherwise it will raise InterfaceError.
54
+ #
55
+ # Example:
56
+ # # `foo` is an integer and this statement will return two rows.
57
+ # sth = dbh.prepare("select foo from bar")
58
+ # # would raise InterfaceError if called here
59
+ # sth.execute
60
+ #
61
+ # sth.bind_coltype(1, DBI::Type::Varchar)
62
+ # # would normally use DBI::Type::Integer and return a Fixnum. We'll make it a string.
63
+ # sth.fetch => ["1"]
64
+ #
65
+ # # Here we coerce it to Float.
66
+ # sth.bind_coltype(1, DBI::Type::Float)
67
+ # sth.fetch => [1.0]
68
+ # sth.finish
69
+ #
70
+ def bind_coltype(pos, type)
71
+ raise InterfaceError, "statement must be executed before using this command" unless @executed
72
+
73
+ coltypes = column_types
74
+
75
+ if (pos - 1) < 1
76
+ raise InterfaceError, "bind positions index starting at 1"
77
+ end
78
+
79
+ coltypes[pos-1] = type
80
+ @row = DBI::Row.new(column_names, coltypes, nil, @convert_types)
81
+ end
82
+
83
+ #
84
+ # Just like BaseStatement#bind_param, but will attempt to convert the
85
+ # type if it's supposed to, adhering to the DBD's current ruleset.
86
+ #
87
+ def bind_param(param, value, attribs=nil)
88
+ raise InterfaceError, "Statement was already closed!" if @handle.nil?
89
+ raise InterfaceError, "Statement wasn't prepared before." unless @prepared
90
+
91
+ if @convert_types
92
+ value = DBI::Utils::ConvParam.conv_param(dbh.driver_name, value)[0]
93
+ end
94
+
95
+ @handle.bind_param(param, value, attribs)
96
+ end
97
+
98
+
99
+ # Execute the statement.
100
+ #
101
+ # This generally means that the statement will be sent to the database
102
+ # and some form of result cursor will be obtained, but is ultimately
103
+ # driver-dependent.
104
+ #
105
+ # If arguments are supplied, these are fed to #bind_param.
106
+ def execute(*bindvars)
107
+ cancel # cancel before
108
+ raise InterfaceError, "Statement was already closed!" if @handle.nil?
109
+ raise InterfaceError, "Statement wasn't prepared before." unless @prepared
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
+ raise InterfaceError, "Statement was already closed!" if @handle.nil?
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
+ raise InterfaceError, "Statement was already closed!" if @handle.nil?
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
+ raise InterfaceError, "Statement was already closed!" if @handle.nil?
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
+ raise InterfaceError, "Statement was already closed!" if @handle.nil?
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
+ raise InterfaceError, "Statement was already closed!" if @handle.nil?
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
+ raise InterfaceError, "Statement was already closed!" if @handle.nil?
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
+ raise InterfaceError, "Statement was already closed!" if @handle.nil?
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
+ raise InterfaceError, "Statement was already closed!" if @handle.nil?
238
+ raise InterfaceError, "Statement must first be executed" unless @fetchable
239
+ raise InterfaceError, "No block given" unless block_given?
240
+
241
+ fetch(&p)
242
+ end
243
+
244
+ #
245
+ # Similar to #fetch, but returns Array of Array instead of Array of
246
+ # DBI::Row objects (and therefore does not perform type mapping). This
247
+ # is basically a way to get the raw data from the DBD.
248
+ #
249
+ def fetch_array
250
+ raise InterfaceError, "Statement was already closed!" if @handle.nil?
251
+ raise InterfaceError, "Statement must first be executed" unless @fetchable
252
+
253
+ if block_given?
254
+ while (res = @handle.fetch) != nil
255
+ yield res
256
+ end
257
+ @handle.cancel
258
+ @fetchable = false
259
+ return nil
260
+ else
261
+ res = @handle.fetch
262
+ if res.nil?
263
+ @handle.cancel
264
+ @fetchable = false
265
+ end
266
+ return res
267
+ end
268
+ end
269
+
270
+ #
271
+ # Map the columns and results into an Array of Hash resultset.
272
+ #
273
+ # No type conversion is performed here. Expect this to change in 0.6.0.
274
+ #
275
+ def fetch_hash
276
+ raise InterfaceError, "Statement was already closed!" if @handle.nil?
277
+ raise InterfaceError, "Statement must first be executed" unless @fetchable
278
+
279
+ cols = column_names
280
+
281
+ if block_given?
282
+ while (row = @handle.fetch) != nil
283
+ hash = {}
284
+ row.each_with_index {|v,i| hash[cols[i]] = v}
285
+ yield hash
286
+ end
287
+ @handle.cancel
288
+ @fetchable = false
289
+ return nil
290
+ else
291
+ row = @handle.fetch
292
+ if row.nil?
293
+ @handle.cancel
294
+ @fetchable = false
295
+ return nil
296
+ else
297
+ hash = {}
298
+ row.each_with_index {|v,i| hash[cols[i]] = v}
299
+ return hash
300
+ end
301
+ end
302
+ end
303
+
304
+ #
305
+ # Fetch `cnt` rows. Result is array of DBI::Row
306
+ #
307
+ def fetch_many(cnt)
308
+ raise InterfaceError, "Statement was already closed!" if @handle.nil?
309
+ raise InterfaceError, "Statement must first be executed" unless @fetchable
310
+
311
+ cols = column_names
312
+ rows = @handle.fetch_many(cnt)
313
+ if rows.nil?
314
+ @handle.cancel
315
+ @fetchable = false
316
+ return []
317
+ else
318
+ return rows.collect{|r| tmp = @row.dup; tmp.set_values(r); tmp }
319
+ end
320
+ end
321
+
322
+ #
323
+ # Fetch the entire result set. Result is array of DBI::Row.
324
+ #
325
+ def fetch_all
326
+ raise InterfaceError, "Statement was already closed!" if @handle.nil?
327
+ raise InterfaceError, "Statement must first be executed" unless @fetchable
328
+
329
+ cols = column_names
330
+ fetched_rows = []
331
+
332
+ begin
333
+ while row = fetch do
334
+ fetched_rows.push(row)
335
+ end
336
+ rescue Exception
337
+ end
338
+
339
+ @handle.cancel
340
+ @fetchable = false
341
+
342
+ return fetched_rows
343
+ end
344
+
345
+ #
346
+ # See BaseStatement#fetch_scroll.
347
+ #
348
+ def fetch_scroll(direction, offset=1)
349
+ raise InterfaceError, "Statement was already closed!" if @handle.nil?
350
+ raise InterfaceError, "Statement must first be executed" unless @fetchable
351
+
352
+ row = @handle.fetch_scroll(direction, offset)
353
+ if row.nil?
354
+ #@handle.cancel
355
+ #@fetchable = false
356
+ return nil
357
+ else
358
+ @row.set_values(row)
359
+ return @row
360
+ end
361
+ end
362
+
363
+ # Get an attribute from the StatementHandle object.
364
+ def [] (attr)
365
+ raise InterfaceError, "Statement was already closed!" if @handle.nil?
366
+ @handle[attr]
367
+ end
368
+
369
+ # Set an attribute on the StatementHandle object.
370
+ def []= (attr, val)
371
+ raise InterfaceError, "Statement was already closed!" if @handle.nil?
372
+ @handle[attr] = val
373
+ end
374
+ end # class StatementHandle
375
+ end