sequel 0.3.1 → 0.3.2
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.
- data/CHANGELOG +28 -0
- data/Rakefile +1 -1
- data/lib/sequel/ado.rb +12 -5
- data/lib/sequel/array_keys.rb +61 -2
- data/lib/sequel/connection_pool.rb +6 -0
- data/lib/sequel/core_ext.rb +18 -14
- data/lib/sequel/database.rb +40 -4
- data/lib/sequel/dataset.rb +9 -3
- data/lib/sequel/dataset/convenience.rb +66 -19
- data/lib/sequel/dataset/sequelizer.rb +4 -4
- data/lib/sequel/dataset/sql.rb +41 -42
- data/lib/sequel/db2.rb +160 -0
- data/lib/sequel/dbi.rb +1 -1
- data/lib/sequel/error.rb +11 -0
- data/lib/sequel/model.rb +28 -22
- data/lib/sequel/model/record.rb +3 -1
- data/lib/sequel/mysql.rb +9 -9
- data/lib/sequel/odbc.rb +1 -1
- data/lib/sequel/oracle.rb +2 -2
- data/lib/sequel/postgres.rb +18 -20
- data/lib/sequel/pretty_table.rb +2 -2
- data/lib/sequel/schema/schema_sql.rb +3 -3
- data/lib/sequel/sqlite.rb +4 -0
- data/spec/adapters/mysql_spec.rb +1 -1
- data/spec/adapters/sqlite_spec.rb +8 -0
- data/spec/core_ext_spec.rb +23 -23
- data/spec/dataset_spec.rb +204 -19
- data/spec/model_spec.rb +71 -1
- data/spec/schema_generator_spec.rb +3 -3
- metadata +4 -3
- data/lib/sequel/expressions.rb +0 -76
data/lib/sequel/dataset.rb
CHANGED
@@ -97,7 +97,7 @@ module Sequel
|
|
97
97
|
new_dataset
|
98
98
|
end
|
99
99
|
|
100
|
-
def set_options(opts)
|
100
|
+
def set_options(opts) #:nodoc:
|
101
101
|
@opts = opts
|
102
102
|
@columns = nil
|
103
103
|
end
|
@@ -152,6 +152,11 @@ module Sequel
|
|
152
152
|
def <<(*args)
|
153
153
|
insert(*args)
|
154
154
|
end
|
155
|
+
|
156
|
+
# Updates the dataset with the given values.
|
157
|
+
def set(*args)
|
158
|
+
update(*args)
|
159
|
+
end
|
155
160
|
|
156
161
|
# Iterates over the records in the dataset
|
157
162
|
def each(opts = nil, &block)
|
@@ -307,6 +312,7 @@ module Sequel
|
|
307
312
|
end
|
308
313
|
end
|
309
314
|
update_each_method
|
315
|
+
self
|
310
316
|
end
|
311
317
|
|
312
318
|
# Applies the value transform for data loaded from the database.
|
@@ -383,11 +389,11 @@ module Sequel
|
|
383
389
|
|
384
390
|
@@dataset_classes = []
|
385
391
|
|
386
|
-
def self.dataset_classes
|
392
|
+
def self.dataset_classes #:nodoc:
|
387
393
|
@@dataset_classes
|
388
394
|
end
|
389
395
|
|
390
|
-
def self.inherited(c)
|
396
|
+
def self.inherited(c) #:nodoc:
|
391
397
|
@@dataset_classes << c
|
392
398
|
end
|
393
399
|
end
|
@@ -77,11 +77,11 @@ module Sequel
|
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
80
|
-
# Maps
|
80
|
+
# Maps column values for each record in the dataset (if a column name is
|
81
81
|
# given), or performs the stock mapping functionality of Enumerable.
|
82
|
-
def map(
|
83
|
-
if
|
84
|
-
super() {|r| r[
|
82
|
+
def map(column_name = nil, &block)
|
83
|
+
if column_name
|
84
|
+
super() {|r| r[column_name]}
|
85
85
|
else
|
86
86
|
super(&block)
|
87
87
|
end
|
@@ -152,24 +152,43 @@ module Sequel
|
|
152
152
|
b - a + 1
|
153
153
|
end
|
154
154
|
|
155
|
-
# Returns the minimum value for the given
|
156
|
-
def min(
|
157
|
-
single_value(:select => [
|
155
|
+
# Returns the minimum value for the given column.
|
156
|
+
def min(column)
|
157
|
+
single_value(:select => [column.MIN.AS(:v)])
|
158
158
|
end
|
159
159
|
|
160
|
-
# Returns the maximum value for the given
|
161
|
-
def max(
|
162
|
-
single_value(:select => [
|
160
|
+
# Returns the maximum value for the given column.
|
161
|
+
def max(column)
|
162
|
+
single_value(:select => [column.MAX.AS(:v)])
|
163
163
|
end
|
164
164
|
|
165
|
-
# Returns the sum for the given
|
166
|
-
def sum(
|
167
|
-
single_value(:select => [
|
165
|
+
# Returns the sum for the given column.
|
166
|
+
def sum(column)
|
167
|
+
single_value(:select => [column.SUM.AS(:v)])
|
168
168
|
end
|
169
169
|
|
170
|
-
# Returns the average value for the given
|
171
|
-
def avg(
|
172
|
-
single_value(:select => [
|
170
|
+
# Returns the average value for the given column.
|
171
|
+
def avg(column)
|
172
|
+
single_value(:select => [column.AVG.AS(:v)])
|
173
|
+
end
|
174
|
+
|
175
|
+
# Returns a dataset grouped by the given column with count by group.
|
176
|
+
def group_and_count(column)
|
177
|
+
group(column).select(column, :count[column].AS(:count)).order(:count)
|
178
|
+
end
|
179
|
+
|
180
|
+
# Returns a Range object made from the minimum and maximum values for the
|
181
|
+
# given column.
|
182
|
+
def range(column)
|
183
|
+
r = select(column.MIN.AS(:v1), column.MAX.AS(:v2)).first
|
184
|
+
r && (r[:v1]..r[:v2])
|
185
|
+
end
|
186
|
+
|
187
|
+
# Returns the interval between minimum and maximum values for the given
|
188
|
+
# column.
|
189
|
+
def interval(column)
|
190
|
+
r = select("(max(#{literal(column)}) - min(#{literal(column)})) AS v".lit).first
|
191
|
+
r && r[:v]
|
173
192
|
end
|
174
193
|
|
175
194
|
# Pretty prints the records in the dataset as plain-text table.
|
@@ -216,7 +235,7 @@ module Sequel
|
|
216
235
|
end
|
217
236
|
end
|
218
237
|
|
219
|
-
module QueryBlockCopy
|
238
|
+
module QueryBlockCopy #:nodoc:
|
220
239
|
def each(*args); raise SequelError, "#each cannot be invoked inside a query block."; end
|
221
240
|
def insert(*args); raise SequelError, "#insert cannot be invoked inside a query block."; end
|
222
241
|
def update(*args); raise SequelError, "#update cannot be invoked inside a query block."; end
|
@@ -244,7 +263,9 @@ module Sequel
|
|
244
263
|
end
|
245
264
|
|
246
265
|
MUTATION_RE = /^(.+)!$/.freeze
|
247
|
-
|
266
|
+
|
267
|
+
# Provides support for mutation methods (filter!, order!, etc.) and magic
|
268
|
+
# methods.
|
248
269
|
def method_missing(m, *args, &block)
|
249
270
|
if m.to_s =~ MUTATION_RE
|
250
271
|
m = $1.to_sym
|
@@ -253,10 +274,36 @@ module Sequel
|
|
253
274
|
super if copy.class != self.class
|
254
275
|
@opts.merge!(copy.opts)
|
255
276
|
self
|
277
|
+
elsif magic_method_missing(m)
|
278
|
+
send(m, *args)
|
256
279
|
else
|
257
|
-
|
280
|
+
super
|
258
281
|
end
|
259
282
|
end
|
283
|
+
|
284
|
+
MAGIC_METHODS = {
|
285
|
+
/^order_by_(.+)$/ => proc {|c| proc {order(c)}},
|
286
|
+
/^first_by_(.+)$/ => proc {|c| proc {order(c).first}},
|
287
|
+
/^last_by_(.+)$/ => proc {|c| proc {order(c).last}},
|
288
|
+
/^filter_by_(.+)$/ => proc {|c| proc {|v| filter(c => v)}},
|
289
|
+
/^all_by_(.+)$/ => proc {|c| proc {|v| filter(c => v).all}},
|
290
|
+
/^find_by_(.+)$/ => proc {|c| proc {|v| filter(c => v).first}},
|
291
|
+
/^group_by_(.+)$/ => proc {|c| proc {group(c)}},
|
292
|
+
/^count_by_(.+)$/ => proc {|c| proc {group_and_count(c)}}
|
293
|
+
}
|
294
|
+
|
295
|
+
# Checks if the given method name represents a magic method and
|
296
|
+
# defines it. Otherwise, nil is returned.
|
297
|
+
def magic_method_missing(m)
|
298
|
+
method_name = m.to_s
|
299
|
+
MAGIC_METHODS.each_pair do |r, p|
|
300
|
+
if method_name =~ r
|
301
|
+
impl = p[$1.to_sym]
|
302
|
+
return Dataset.class_def(m, &impl)
|
303
|
+
end
|
304
|
+
end
|
305
|
+
nil
|
306
|
+
end
|
260
307
|
end
|
261
308
|
end
|
262
309
|
end
|
@@ -154,15 +154,15 @@ class Sequel::Dataset
|
|
154
154
|
end
|
155
155
|
end
|
156
156
|
|
157
|
-
def fcall_expr(e, b)
|
157
|
+
def fcall_expr(e, b) #:nodoc:
|
158
158
|
ext_expr(e, b)
|
159
159
|
end
|
160
160
|
|
161
|
-
def vcall_expr(e, b)
|
161
|
+
def vcall_expr(e, b) #:nodoc:
|
162
162
|
eval(e[1].to_s, b)
|
163
163
|
end
|
164
164
|
|
165
|
-
def iter_expr(e, b)
|
165
|
+
def iter_expr(e, b) #:nodoc:
|
166
166
|
if e[1] == [:fcall, :proc]
|
167
167
|
eval_expr(e[3], b) # inline proc
|
168
168
|
else
|
@@ -223,7 +223,7 @@ class Sequel::Dataset
|
|
223
223
|
end
|
224
224
|
end
|
225
225
|
|
226
|
-
def pt_expr(e, b)
|
226
|
+
def pt_expr(e, b) #:nodoc:
|
227
227
|
case e[0]
|
228
228
|
when :not # negation: !x, (x != y), (x !~ y)
|
229
229
|
if (e[1][0] == :lit) && (Symbol === e[1][1])
|
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -3,56 +3,56 @@ module Sequel
|
|
3
3
|
# The Dataset SQL module implements all the dataset methods concerned with
|
4
4
|
# generating SQL statements for retrieving and manipulating records.
|
5
5
|
module SQL
|
6
|
-
# Returns a valid SQL
|
6
|
+
# Returns a valid SQL column name as a string. Column names specified as
|
7
7
|
# symbols can include double underscores to denote a dot separator, e.g.
|
8
8
|
# :posts__id will be converted into posts.id.
|
9
|
-
def
|
10
|
-
case
|
9
|
+
def column_name(column)
|
10
|
+
case column
|
11
11
|
when Symbol, String:
|
12
|
-
|
12
|
+
quoted_column_name(column.to_column_name)
|
13
13
|
when Hash:
|
14
|
-
|
14
|
+
column.map {|f,a| "#{column_name(f)} AS #{column_name(a)}"}.join(COMMA_SEPARATOR)
|
15
15
|
else
|
16
|
-
|
16
|
+
column
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
# Adds quoting to
|
21
|
-
# be overriden in adapters in order to provide correct
|
20
|
+
# Adds quoting to column references. This method is just a stub and can
|
21
|
+
# be overriden in adapters in order to provide correct column quoting
|
22
22
|
# behavior.
|
23
|
-
def
|
23
|
+
def quoted_column_name(name)
|
24
24
|
name
|
25
25
|
end
|
26
26
|
|
27
27
|
ALIASED_REGEXP = /^(.*)\s(.*)$/.freeze
|
28
28
|
QUALIFIED_REGEXP = /^(.*)\.(.*)$/.freeze
|
29
29
|
|
30
|
-
# Returns a qualified
|
30
|
+
# Returns a qualified column name (including a table name) if the column
|
31
31
|
# name isn't already qualified.
|
32
|
-
def
|
33
|
-
|
34
|
-
if
|
35
|
-
#
|
36
|
-
|
32
|
+
def qualified_column_name(column, table)
|
33
|
+
column = column_name(column)
|
34
|
+
if column =~ QUALIFIED_REGEXP
|
35
|
+
# column is already qualified
|
36
|
+
column
|
37
37
|
else
|
38
38
|
# check if the table is aliased
|
39
39
|
if table =~ ALIASED_REGEXP
|
40
40
|
table = $2
|
41
41
|
end
|
42
|
-
"#{table}.#{
|
42
|
+
"#{table}.#{column}"
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
46
|
WILDCARD = '*'.freeze
|
47
47
|
COMMA_SEPARATOR = ", ".freeze
|
48
48
|
|
49
|
-
# Converts an array of
|
50
|
-
#
|
51
|
-
def
|
52
|
-
if
|
49
|
+
# Converts an array of column names into a comma seperated string of
|
50
|
+
# column names. If the array is empty, a wildcard (*) is returned.
|
51
|
+
def column_list(columns)
|
52
|
+
if columns.empty?
|
53
53
|
WILDCARD
|
54
54
|
else
|
55
|
-
|
55
|
+
columns.map do |i|
|
56
56
|
i.is_a?(Hash) ? i.map {|kv| "#{literal(kv[0])} AS #{kv[1]}"} : literal(i)
|
57
57
|
end.join(COMMA_SEPARATOR)
|
58
58
|
end
|
@@ -87,7 +87,7 @@ module Sequel
|
|
87
87
|
# Returns a literal representation of a value to be used as part
|
88
88
|
# of an SQL expression. The stock implementation supports literalization
|
89
89
|
# of String (with proper escaping to prevent SQL injections), numbers,
|
90
|
-
# Symbol (as
|
90
|
+
# Symbol (as column references), Array (as a list of literalized values),
|
91
91
|
# Time (as an SQL TIMESTAMP), Date (as an SQL DATE), Dataset (as a
|
92
92
|
# subquery) and nil (AS NULL).
|
93
93
|
#
|
@@ -105,7 +105,7 @@ module Sequel
|
|
105
105
|
when NilClass: NULL
|
106
106
|
when TrueClass: TRUE
|
107
107
|
when FalseClass: FALSE
|
108
|
-
when Symbol:
|
108
|
+
when Symbol: quoted_column_name(v.to_column_name)
|
109
109
|
when Array: v.empty? ? NULL : v.map {|i| literal(i)}.join(COMMA_SEPARATOR)
|
110
110
|
when Time: v.strftime(TIMESTAMP_FORMAT)
|
111
111
|
when Date: v.strftime(DATE_FORMAT)
|
@@ -146,9 +146,9 @@ module Sequel
|
|
146
146
|
clone_merge(:from => source)
|
147
147
|
end
|
148
148
|
|
149
|
-
# Returns a copy of the dataset with the selected
|
150
|
-
def select(*
|
151
|
-
clone_merge(:select =>
|
149
|
+
# Returns a copy of the dataset with the selected columns changed.
|
150
|
+
def select(*columns)
|
151
|
+
clone_merge(:select => columns)
|
152
152
|
end
|
153
153
|
|
154
154
|
# Returns a copy of the dataset with the distinct option.
|
@@ -172,7 +172,7 @@ module Sequel
|
|
172
172
|
|
173
173
|
DESC_ORDER_REGEXP = /(.*)\sDESC/i.freeze
|
174
174
|
|
175
|
-
# Inverts the given order by breaking it into a list of
|
175
|
+
# Inverts the given order by breaking it into a list of column references
|
176
176
|
# and inverting them.
|
177
177
|
#
|
178
178
|
# dataset.invert_order('id DESC') #=> "id"
|
@@ -190,9 +190,9 @@ module Sequel
|
|
190
190
|
end
|
191
191
|
|
192
192
|
# Returns a copy of the dataset with the results grouped by the value of
|
193
|
-
# the given
|
194
|
-
def group(*
|
195
|
-
clone_merge(:group =>
|
193
|
+
# the given columns
|
194
|
+
def group(*columns)
|
195
|
+
clone_merge(:group => columns)
|
196
196
|
end
|
197
197
|
|
198
198
|
alias_method :group_by, :group
|
@@ -330,8 +330,8 @@ module Sequel
|
|
330
330
|
|
331
331
|
join_conditions = {}
|
332
332
|
expr.each do |k, v|
|
333
|
-
k =
|
334
|
-
v =
|
333
|
+
k = qualified_column_name(k, table).intern if k.is_a?(Symbol)
|
334
|
+
v = qualified_column_name(v, @opts[:last_joined_table] || @opts[:from].first).intern if v.is_a?(Symbol)
|
335
335
|
join_conditions[k] = v
|
336
336
|
end
|
337
337
|
" #{join_type} #{table} ON #{expression_list(join_conditions)}"
|
@@ -359,7 +359,6 @@ module Sequel
|
|
359
359
|
# Returns an INNER joined dataset.
|
360
360
|
def inner_join(table, expr); join_table(:inner, table, expr); end
|
361
361
|
alias join inner_join
|
362
|
-
|
363
362
|
|
364
363
|
# Inserts multiple values. If a block is given it is invoked for each
|
365
364
|
# item in the given array before inserting it.
|
@@ -380,12 +379,12 @@ module Sequel
|
|
380
379
|
return sql
|
381
380
|
end
|
382
381
|
|
383
|
-
|
384
|
-
|
382
|
+
columns = opts[:select]
|
383
|
+
select_columns = columns ? column_list(columns) : WILDCARD
|
385
384
|
select_source = source_list(opts[:from])
|
386
385
|
sql = opts[:distinct] ? \
|
387
|
-
"SELECT DISTINCT #{
|
388
|
-
"SELECT #{
|
386
|
+
"SELECT DISTINCT #{select_columns} FROM #{select_source}" : \
|
387
|
+
"SELECT #{select_columns} FROM #{select_source}"
|
389
388
|
|
390
389
|
if join = opts[:join]
|
391
390
|
sql << join
|
@@ -396,11 +395,11 @@ module Sequel
|
|
396
395
|
end
|
397
396
|
|
398
397
|
if group = opts[:group]
|
399
|
-
sql << " GROUP BY #{
|
398
|
+
sql << " GROUP BY #{column_list(group)}"
|
400
399
|
end
|
401
400
|
|
402
401
|
if order = opts[:order]
|
403
|
-
sql << " ORDER BY #{
|
402
|
+
sql << " ORDER BY #{column_list(order)}"
|
404
403
|
end
|
405
404
|
|
406
405
|
if having = opts[:having]
|
@@ -430,7 +429,7 @@ module Sequel
|
|
430
429
|
alias sql select_sql
|
431
430
|
|
432
431
|
# Formats an INSERT statement using the given values. If a hash is given,
|
433
|
-
# the resulting statement includes
|
432
|
+
# the resulting statement includes column names. If no values are given,
|
434
433
|
# the resulting statement includes a DEFAULT VALUES clause.
|
435
434
|
#
|
436
435
|
# dataset.insert_sql() #=> 'INSERT INTO items DEFAULT VALUES'
|
@@ -461,7 +460,7 @@ module Sequel
|
|
461
460
|
"INSERT INTO #{@opts[:from]} DEFAULT VALUES;"
|
462
461
|
else
|
463
462
|
fl, vl = [], []
|
464
|
-
values.each {|k, v| fl <<
|
463
|
+
values.each {|k, v| fl << column_name(k); vl << literal(v)}
|
465
464
|
"INSERT INTO #{@opts[:from]} (#{fl.join(COMMA_SEPARATOR)}) VALUES (#{vl.join(COMMA_SEPARATOR)});"
|
466
465
|
end
|
467
466
|
when Dataset
|
@@ -489,7 +488,7 @@ module Sequel
|
|
489
488
|
values = values.to_hash
|
490
489
|
end
|
491
490
|
values = transform_save(values) if @transform
|
492
|
-
set_list = values.map {|k, v| "#{
|
491
|
+
set_list = values.map {|k, v| "#{column_name(k)} = #{literal(v)}"}.
|
493
492
|
join(COMMA_SEPARATOR)
|
494
493
|
sql = "UPDATE #{@opts[:from]} SET #{set_list}"
|
495
494
|
|
data/lib/sequel/db2.rb
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
if !Object.const_defined?('Sequel')
|
2
|
+
require File.join(File.dirname(__FILE__), '../sequel')
|
3
|
+
end
|
4
|
+
|
5
|
+
require 'db2/db2cli'
|
6
|
+
|
7
|
+
module Sequel
|
8
|
+
module DB2
|
9
|
+
class Database < Sequel::Database
|
10
|
+
set_adapter_scheme :db2
|
11
|
+
include DB2CLI
|
12
|
+
|
13
|
+
# AUTO_INCREMENT = 'IDENTITY(1,1)'.freeze
|
14
|
+
#
|
15
|
+
# def auto_increment_sql
|
16
|
+
# AUTO_INCREMENT
|
17
|
+
# end
|
18
|
+
|
19
|
+
def check_error(rc, msg)
|
20
|
+
case rc
|
21
|
+
when SQL_SUCCESS, SQL_SUCCESS_WITH_INFO: nil
|
22
|
+
else
|
23
|
+
raise SequelError, msg
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
rc, @@env = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE)
|
28
|
+
check_error(rc, "Could not allocate DB2 environment")
|
29
|
+
|
30
|
+
def connect
|
31
|
+
rc, dbc = SQLAllocHandle(SQL_HANDLE_DBC, @@env)
|
32
|
+
check_error(rc, "Could not allocate database connection")
|
33
|
+
|
34
|
+
rc = SQLConnect(dbc, @opts[:database], @opts[:user], @opts[:password])
|
35
|
+
check_error(rc, "Could not connect to database")
|
36
|
+
|
37
|
+
dbc
|
38
|
+
end
|
39
|
+
|
40
|
+
def disconnect
|
41
|
+
@pool.disconnect do |conn|
|
42
|
+
rc = SQLDisconnect(conn)
|
43
|
+
check_error(rc, "Could not disconnect from database")
|
44
|
+
|
45
|
+
rc = SQLFreeHandle(SQL_HANDLE_DBC, conn)
|
46
|
+
check_error(rc, "Could not free Database handle")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_connection
|
51
|
+
@pool.hold {|conn|}
|
52
|
+
true
|
53
|
+
end
|
54
|
+
|
55
|
+
def dataset(opts = nil)
|
56
|
+
DB2::Dataset.new(self, opts)
|
57
|
+
end
|
58
|
+
|
59
|
+
def execute(sql, &block)
|
60
|
+
@logger.info(sql) if @logger
|
61
|
+
@pool.hold do |conn|
|
62
|
+
rc, sth = SQLAllocHandle(SQL_HANDLE_STMT, @handle)
|
63
|
+
check_error(rc, "Could not allocate statement")
|
64
|
+
|
65
|
+
begin
|
66
|
+
rc = SQLExecDirect(sth, sql)
|
67
|
+
check_error(rc, "Could not execute statement")
|
68
|
+
|
69
|
+
block[sth] if block
|
70
|
+
|
71
|
+
rc, rpc = SQLRowCount(sth)
|
72
|
+
check_error(rc, "Could not get RPC")
|
73
|
+
rpc
|
74
|
+
ensure
|
75
|
+
rc = SQLFreeHandle(SQL_HANDLE_STMT, sth)
|
76
|
+
check_error(rc, "Could not free statement")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
alias_method :do, :execute
|
81
|
+
end
|
82
|
+
|
83
|
+
class Dataset < Sequel::Dataset
|
84
|
+
def literal(v)
|
85
|
+
case v
|
86
|
+
when Time: literal(v.iso8601)
|
87
|
+
else
|
88
|
+
super
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def fetch_rows(sql, &block)
|
93
|
+
@db.synchronize do
|
94
|
+
@db.execute(sql) do |sth|
|
95
|
+
@column_info = get_column_info(sth)
|
96
|
+
@columns = @column_info.map {|c| c[:name]}
|
97
|
+
while (rc = SQLFetch(@handle)) != SQL_NO_DATA_FOUND
|
98
|
+
@db.check_error(rc, "Could not fetch row")
|
99
|
+
yield hash_row(sth)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
self
|
104
|
+
end
|
105
|
+
|
106
|
+
MAX_COL_SIZE = 256
|
107
|
+
|
108
|
+
def get_column_info(sth)
|
109
|
+
rc, column_count = SQLNumResultCols(sth)
|
110
|
+
@db.check_error(rc, "Could not get number of result columns")
|
111
|
+
|
112
|
+
(1..column_count).map do |i|
|
113
|
+
rc, name, buflen, datatype, size, digits, nullable = SQLDescribeCol(sth, i, MAX_COL_SIZE)
|
114
|
+
@b.check_error(rc, "Could not describe column")
|
115
|
+
|
116
|
+
{:name => name, :db2_type => datatype, :precision => size}
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def hash_row(sth)
|
121
|
+
row = {}
|
122
|
+
@column_info.each_with_index do |c, i|
|
123
|
+
rc, v = SQLGetData(sth, i+1, c[:db2_type], c[:precision])
|
124
|
+
@db.check_error(rc, "Could not get data")
|
125
|
+
|
126
|
+
@row[c[:name]] = convert_type(v)
|
127
|
+
end
|
128
|
+
row
|
129
|
+
end
|
130
|
+
|
131
|
+
def convert_type(v)
|
132
|
+
case v
|
133
|
+
when DB2CLI::Date
|
134
|
+
DBI::Date.new(v.year, v.month, v.day)
|
135
|
+
when DB2CLI::Time
|
136
|
+
DBI::Time.new(v.hour, v.minute, v.second)
|
137
|
+
when DB2CLI::Timestamp
|
138
|
+
DBI::Timestamp.new(v.year, v.month, v.day,
|
139
|
+
v.hour, v.minute, v.second, v.fraction)
|
140
|
+
when DB2CLI::Null
|
141
|
+
nil
|
142
|
+
else
|
143
|
+
v
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def insert(*values)
|
148
|
+
@db.do insert_sql(*values)
|
149
|
+
end
|
150
|
+
|
151
|
+
def update(values, opts = nil)
|
152
|
+
@db.do update_sql(values, opts)
|
153
|
+
end
|
154
|
+
|
155
|
+
def delete(opts = nil)
|
156
|
+
@db.do delete_sql(opts)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|