dbi 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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