sequel_core 1.0.0.1 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,17 @@
1
+ === 1.0.1 (2008-01-12)
2
+
3
+ * Changed postgres adapter to quote column references using double quotes.
4
+
5
+ * Applied patch for oracle adapter: fix behavior of limit and offset, transactions, #table_exists?, #tables and additional specs (thanks Liming Lian #122).
6
+
7
+ * Allow for additional filters on a grouped dataset (#119 and #120)
8
+
9
+ * Changed mysql adapter to default to localhost if :host option is not specified (#114).
10
+
11
+ * Refactored Sequelizer to use Proc#to_sexp (method provided by r2r).
12
+
13
+ * Enhanced Database.connect to accept options with string keys, so it can now accept options loaded from YAML files. Database.connect also automatically converts :username option into :user for compatibility with existing YAML configuration files for AR and DataMapper.
14
+
1
15
  === 1.0.0.1 (2008-01-03)
2
16
 
3
17
  * Changed MySQL adapter to support specifying socket option.
data/COPYING CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2006-2007 Sharon Rosner
1
+ Copyright (c) 2006-2008 Sharon Rosner
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  of this software and associated documentation files (the "Software"), to
data/Rakefile CHANGED
@@ -9,7 +9,7 @@ include FileUtils
9
9
  # Configuration
10
10
  ##############################################################################
11
11
  NAME = "sequel_core"
12
- VERS = "1.0.0.1"
12
+ VERS = "1.0.1"
13
13
  CLEAN.include ["**/.*.sw?", "pkg/*", ".config", "doc/*", "coverage/*"]
14
14
  RDOC_OPTS = [
15
15
  "--quiet",
@@ -105,6 +105,9 @@ end
105
105
 
106
106
  task :tag do
107
107
  cwd = FileUtils.pwd
108
+ sh %{rm -rf doc/*}
109
+ sh %{rm -rf pkg/*}
110
+ sh %{rm -rf coverage/*}
108
111
  sh %{cd ../.. && svn copy #{cwd} tags/#{NAME}-#{VERS} && svn commit -m "#{NAME}-#{VERS} tag." tags}
109
112
  end
110
113
 
@@ -86,8 +86,15 @@ module Sequel
86
86
  end
87
87
 
88
88
  def connect
89
- conn = Mysql.real_connect(@opts[:host], @opts[:user], @opts[:password],
90
- @opts[:database], @opts[:port], @opts[:socket], Mysql::CLIENT_MULTI_RESULTS)
89
+ conn = Mysql.real_connect(
90
+ @opts[:host] || 'localhost',
91
+ @opts[:user],
92
+ @opts[:password],
93
+ @opts[:database],
94
+ @opts[:port],
95
+ @opts[:socket],
96
+ Mysql::CLIENT_MULTI_RESULTS
97
+ )
91
98
  conn.query_with_result = false
92
99
  if encoding = @opts[:encoding] || @opts[:charset]
93
100
  conn.query("set character_set_connection = '#{encoding}'")
@@ -38,6 +38,39 @@ module Sequel
38
38
  end
39
39
 
40
40
  alias_method :do, :execute
41
+
42
+ def tables
43
+ from(:tab).select(:tname).filter(:tabtype => 'TABLE').map do |r|
44
+ r[:tname].downcase.to_sym
45
+ end
46
+ end
47
+
48
+ def table_exists?(name)
49
+ from(:tab).filter(:tname => name.to_s.upcase, :tabtype => 'TABLE').count > 0
50
+ end
51
+
52
+ def transaction
53
+ @pool.hold do |conn|
54
+ @transactions ||= []
55
+ if @transactions.include? Thread.current
56
+ return yield(conn)
57
+ end
58
+
59
+ conn.autocommit = false
60
+ begin
61
+ @transactions << Thread.current
62
+ result = yield(conn)
63
+ conn.commit
64
+ result
65
+ rescue => e
66
+ conn.rollback
67
+ raise e unless SequelRollbackError === e
68
+ ensure
69
+ conn.autocommit = true
70
+ @transactions.delete(Thread.current)
71
+ end
72
+ end
73
+ end
41
74
  end
42
75
 
43
76
  class Dataset < Sequel::Dataset
@@ -54,10 +87,10 @@ module Sequel
54
87
  @db.synchronize do
55
88
  cursor = @db.execute sql
56
89
  begin
57
- @columns = cursor.get_col_names.map {|c| c.to_sym}
90
+ @columns = cursor.get_col_names.map {|c| c.downcase.to_sym}
58
91
  while r = cursor.fetch
59
92
  row = {}
60
- r.each_with_index {|v, i| row[@columns[i]] = v}
93
+ r.each_with_index {|v, i| row[columns[i]] = v unless columns[i] == :raw_rnum_}
61
94
  yield row
62
95
  end
63
96
  ensure
@@ -71,9 +104,11 @@ module Sequel
71
104
  @db.synchronize do
72
105
  cursor = @db.execute sql
73
106
  begin
74
- @columns = cursor.get_col_names.map {|c| c.to_sym}
107
+ @columns = cursor.get_col_names.map {|c| c.downcase.to_sym}
108
+ raw_rnum_index = columns.index(:raw_rnum_)
75
109
  while r = cursor.fetch
76
- r.keys = columns
110
+ r.delete_at(raw_rnum_index) if raw_rnum_index
111
+ r.keys = columns.delete_if{|x| x == :raw_rnum_}
77
112
  yield r
78
113
  end
79
114
  ensure
@@ -94,6 +129,70 @@ module Sequel
94
129
  def delete(opts = nil)
95
130
  @db.do delete_sql(opts)
96
131
  end
132
+
133
+
134
+ # Formats a SELECT statement using the given options and the dataset
135
+ # options.
136
+ def select_sql(opts = nil)
137
+ opts = opts ? @opts.merge(opts) : @opts
138
+
139
+ if sql = opts[:sql]
140
+ return sql
141
+ end
142
+
143
+ columns = opts[:select]
144
+ select_columns = columns ? column_list(columns) : WILDCARD
145
+ sql = opts[:distinct] ? \
146
+ "SELECT DISTINCT #{select_columns}" : \
147
+ "SELECT #{select_columns}"
148
+
149
+ if opts[:from]
150
+ sql << " FROM #{source_list(opts[:from])}"
151
+ end
152
+
153
+ if join = opts[:join]
154
+ sql << join
155
+ end
156
+
157
+ if where = opts[:where]
158
+ sql << " WHERE #{where}"
159
+ end
160
+
161
+ if group = opts[:group]
162
+ sql << " GROUP BY #{column_list(group)}"
163
+ end
164
+
165
+ if having = opts[:having]
166
+ sql << " HAVING #{having}"
167
+ end
168
+
169
+ if union = opts[:union]
170
+ sql << (opts[:union_all] ? \
171
+ " UNION ALL #{union.sql}" : " UNION #{union.sql}")
172
+ elsif intersect = opts[:intersect]
173
+ sql << (opts[:intersect_all] ? \
174
+ " INTERSECT ALL #{intersect.sql}" : " INTERSECT #{intersect.sql}")
175
+ elsif except = opts[:except]
176
+ sql << (opts[:except_all] ? \
177
+ " EXCEPT ALL #{except.sql}" : " EXCEPT #{except.sql}")
178
+ end
179
+
180
+ if order = opts[:order]
181
+ sql << " ORDER BY #{column_list(order)}"
182
+ end
183
+
184
+ if limit = opts[:limit]
185
+ if (offset = opts[:offset]) && (offset > 0)
186
+ sql = "SELECT * FROM (SELECT raw_sql_.*, ROWNUM raw_rnum_ FROM(#{sql}) raw_sql_ WHERE ROWNUM <= #{limit + offset}) WHERE raw_rnum_ > #{offset}"
187
+ else
188
+ sql = "SELECT * FROM (#{sql}) WHERE ROWNUM <= #{limit}"
189
+ end
190
+ end
191
+
192
+ sql
193
+ end
194
+
195
+ alias sql select_sql
97
196
  end
98
197
  end
99
198
  end
@@ -313,6 +313,8 @@ module Sequel
313
313
  end
314
314
 
315
315
  class Dataset < Sequel::Dataset
316
+ def quote_column_ref(c); "\"#{c}\""; end
317
+
316
318
  def literal(v)
317
319
  case v
318
320
  when LiteralString
@@ -414,12 +414,18 @@ module Sequel
414
414
  scheme = uri.scheme
415
415
  scheme = :dbi if scheme =~ /^dbi-(.+)/
416
416
  c = adapter_class(scheme)
417
- c.new(c.uri_to_options(uri).merge(opts || {}))
417
+ opts = c.uri_to_options(uri).merge(opts || {})
418
418
  else
419
419
  opts = conn_string.merge(opts || {})
420
- c = adapter_class(opts[:adapter])
421
- c.new(opts)
420
+ c = adapter_class(opts[:adapter] || opts['adapter'])
422
421
  end
422
+ # process opts a bit
423
+ opts = opts.inject({}) do |m, kv| k, v = *kv
424
+ k = :user if k == 'username'
425
+ m[k.to_sym] = v
426
+ m
427
+ end
428
+ c.new(opts)
423
429
  end
424
430
 
425
431
  @@single_threaded = false
@@ -251,8 +251,8 @@ class Sequel::Dataset
251
251
  eval("$#{e[1]}", b)
252
252
  when :lvar # local context
253
253
  if e[1] == :block
254
- pr = eval(e[1].to_s, b)
255
- "#{proc_to_sql(pr)}"
254
+ sub_proc = eval(e[1].to_s, b)
255
+ sub_proc.to_sql(self)
256
256
  else
257
257
  eval(e[1].to_s, b)
258
258
  end
@@ -324,20 +324,20 @@ class Sequel::Dataset
324
324
  end
325
325
  end
326
326
  end
327
+ end
328
+ end
327
329
 
328
- # Translates a Ruby block into an SQL expression.
329
- def proc_to_sql(proc, opts = {})
330
- c = Class.new {define_method(:m, &proc)}
331
- pt_expr(ParseTree.translate(c, :m)[2][2], proc.binding, opts)
332
- end
330
+ class Proc
331
+ def to_sql(dataset, opts = {})
332
+ dataset.pt_expr(to_sexp[2], self.binding, opts)
333
333
  end
334
334
  end
335
335
 
336
336
  begin
337
337
  require 'parse_tree'
338
338
  rescue Exception
339
- module Sequel::Dataset::Sequelizer
340
- def proc_to_sql(*args)
339
+ class Proc
340
+ def to_sql(*args)
341
341
  raise Sequel::Error, "You must have the ParseTree gem installed in order to use block filters."
342
342
  end
343
343
  end
@@ -352,3 +352,14 @@ rescue Exception
352
352
  end
353
353
  end
354
354
  end
355
+
356
+ class Proc
357
+ # replacement for Proc#to_sexp, if it's not available
358
+ unless instance_methods.include?('to_sexp')
359
+ def to_sexp
360
+ block = self
361
+ c = Class.new {define_method(:m, &block)}
362
+ ParseTree.translate(c, :m)[2]
363
+ end
364
+ end
365
+ end
@@ -128,7 +128,7 @@ module Sequel
128
128
  when Array
129
129
  fmt = expr.shift.gsub(QUESTION_MARK) {literal(expr.shift)}
130
130
  when Proc
131
- fmt = proc_to_sql(expr)
131
+ fmt = expr.to_sql(self)
132
132
  else
133
133
  # if the expression is compound, it should be parenthesized in order for
134
134
  # things to be predictable (when using #or and #and.)
@@ -223,14 +223,14 @@ module Sequel
223
223
  # software.filter {price < 100}.sql #=>
224
224
  # "SELECT * FROM items WHERE (category = 'software') AND (price < 100)"
225
225
  def filter(*cond, &block)
226
- clause = (@opts[:group] ? :having : :where)
226
+ clause = (@opts[:having] ? :having : :where)
227
227
  cond = cond.first if cond.size == 1
228
228
  if cond === true || cond === false
229
229
  raise Error::InvalidFilter, "Invalid filter specified. Did you mean to supply a block?"
230
230
  end
231
231
  parenthesize = !(cond.is_a?(Hash) || cond.is_a?(Array))
232
232
  filter = cond.is_a?(Hash) && cond
233
- if @opts[clause]
233
+ if !@opts[clause].nil? and @opts[clause].any?
234
234
  l = expression_list(@opts[clause])
235
235
  r = expression_list(block || cond, parenthesize)
236
236
  clone_merge(clause => "#{l} AND #{r}")
@@ -242,7 +242,7 @@ module Sequel
242
242
  # Adds an alternate filter to an existing filter using OR. If no filter
243
243
  # exists an error is raised.
244
244
  def or(*cond, &block)
245
- clause = (@opts[:group] ? :having : :where)
245
+ clause = (@opts[:having] ? :having : :where)
246
246
  cond = cond.first if cond.size == 1
247
247
  parenthesize = !(cond.is_a?(Hash) || cond.is_a?(Array))
248
248
  if @opts[clause]
@@ -258,7 +258,7 @@ module Sequel
258
258
  # exists an error is raised. This method is identical to #filter except
259
259
  # it expects an existing filter.
260
260
  def and(*cond, &block)
261
- clause = (@opts[:group] ? :having : :where)
261
+ clause = (@opts[:having] ? :having : :where)
262
262
  unless @opts[clause]
263
263
  raise Error::NoExistingFilter, "No existing filter found."
264
264
  end
@@ -270,7 +270,7 @@ module Sequel
270
270
  # dataset.exclude(:category => 'software').sql #=>
271
271
  # "SELECT * FROM items WHERE NOT (category = 'software')"
272
272
  def exclude(*cond, &block)
273
- clause = (@opts[:group] ? :having : :where)
273
+ clause = (@opts[:having] ? :having : :where)
274
274
  cond = cond.first if cond.size == 1
275
275
  parenthesize = !(cond.is_a?(Hash) || cond.is_a?(Array))
276
276
  if @opts[clause]
@@ -286,11 +286,7 @@ module Sequel
286
286
  # Returns a copy of the dataset with the where conditions changed. Raises
287
287
  # if the dataset has been grouped. See also #filter.
288
288
  def where(*cond, &block)
289
- if @opts[:group]
290
- raise Error, "Can't specify a WHERE clause once the dataset has been grouped"
291
- else
292
- filter(*cond, &block)
293
- end
289
+ filter(*cond, &block)
294
290
  end
295
291
 
296
292
  # Returns a copy of the dataset with the having conditions changed. Raises
@@ -299,6 +295,7 @@ module Sequel
299
295
  unless @opts[:group]
300
296
  raise Error, "Can only specify a HAVING clause on a grouped dataset"
301
297
  else
298
+ @opts[:having] = {}
302
299
  filter(*cond, &block)
303
300
  end
304
301
  end
@@ -501,7 +498,7 @@ module Sequel
501
498
 
502
499
  sql = "UPDATE #{@opts[:from]} SET "
503
500
  if block
504
- sql << proc_to_sql(block, :comma_separated => true)
501
+ sql << block.to_sql(self, :comma_separated => true)
505
502
  else
506
503
  # check if array with keys
507
504
  values = values.to_hash if values.is_a?(Array) && values.keys
@@ -1,7 +1,7 @@
1
1
  module Sequel
2
2
  class Model
3
3
  def self.database_opened(db)
4
- @db = db if (self == Model) && !@db
4
+ @db ||= db if (self == Model)
5
5
  end
6
6
  end
7
7
  end
@@ -24,6 +24,18 @@ context "A MySQL database" do
24
24
  @db.disconnect
25
25
  @db.pool.size.should == 0
26
26
  end
27
+
28
+ specify "should support sequential primary keys" do
29
+ @db.create_table!(:with_pk) {primary_key :id; text :name}
30
+ @db[:with_pk] << {:name => 'abc'}
31
+ @db[:with_pk] << {:name => 'def'}
32
+ @db[:with_pk] << {:name => 'ghi'}
33
+ @db[:with_pk].order(:name).all.should == [
34
+ {:id => 1, :name => 'abc'},
35
+ {:id => 2, :name => 'def'},
36
+ {:id => 3, :name => 'ghi'}
37
+ ]
38
+ end
27
39
  end
28
40
 
29
41
  context "A MySQL dataset" do
@@ -335,6 +347,11 @@ context "A MySQL database" do
335
347
  proc {db.test_connection}.should_not raise_error
336
348
  end
337
349
 
350
+ specify "should accept a socket option without host option" do
351
+ db = Sequel.mysql('sandbox', :user => 'root', :socket => '/tmp/mysql.sock')
352
+ proc {db.test_connection}.should_not raise_error
353
+ end
354
+
338
355
  specify "should fail to connect with invalid socket" do
339
356
  db = Sequel.mysql('sandbox', :host => 'localhost', :user => 'root', :socket => 'blah')
340
357
  proc {db.test_connection}.should raise_error
@@ -1,17 +1,35 @@
1
1
  require File.join(File.dirname(__FILE__), '../../lib/sequel_core')
2
2
 
3
- ORACLE_DB = Sequel('oracle://hr:hr@loalhost/XE')
4
- if ORACLE_DB.table_exists?(:test)
5
- ORACLE_DB.drop_table :test
3
+ ORACLE_DB = Sequel('oracle://hr:hr@localhost/XE')
4
+
5
+ if ORACLE_DB.table_exists?(:items)
6
+ ORACLE_DB.drop_table :items
6
7
  end
7
- ORACLE_DB.create_table :test do
8
- text :name
9
- integer :value
8
+ ORACLE_DB.create_table :items do
9
+ varchar2 :name, :size => 50
10
+ number :value, :size => 38
10
11
 
11
12
  index :value
12
13
  end
13
14
 
14
- context "A Oracle database" do
15
+ if ORACLE_DB.table_exists?(:books)
16
+ ORACLE_DB.drop_table :books
17
+ end
18
+ ORACLE_DB.create_table :books do
19
+ number :id, :size => 38
20
+ varchar2 :title, :size => 50
21
+ number :category_id, :size => 38
22
+ end
23
+
24
+ if ORACLE_DB.table_exists?(:categories)
25
+ ORACLE_DB.drop_table :categories
26
+ end
27
+ ORACLE_DB.create_table :categories do
28
+ number :id, :size => 38
29
+ varchar2 :cat_name, :size => 50
30
+ end
31
+
32
+ context "An Oracle database" do
15
33
  specify "should provide disconnect functionality" do
16
34
  ORACLE_DB.execute("select user from dual")
17
35
  ORACLE_DB.pool.size.should == 1
@@ -20,9 +38,9 @@ context "A Oracle database" do
20
38
  end
21
39
  end
22
40
 
23
- context "A Oracle dataset" do
41
+ context "An Oracle dataset" do
24
42
  setup do
25
- @d = ORACLE_DB[:test]
43
+ @d = ORACLE_DB[:items]
26
44
  @d.delete # remove all records
27
45
  end
28
46
 
@@ -45,6 +63,83 @@ context "A Oracle dataset" do
45
63
  {:name => 'abc', :value => 456},
46
64
  {:name => 'def', :value => 789}
47
65
  ]
66
+
67
+ @d.select(:name).uniq.order_by(:name).to_a.should == [
68
+ {:name => 'abc'},
69
+ {:name => 'def'}
70
+ ]
71
+
72
+ @d.order(:value.DESC).limit(1).to_a.should == [
73
+ {:name => 'def', :value => 789}
74
+ ]
75
+
76
+ @d.filter(:name => 'abc').to_a.should == [
77
+ {:name => 'abc', :value => 123},
78
+ {:name => 'abc', :value => 456}
79
+ ]
80
+
81
+ @d.order(:value.DESC).filter(:name => 'abc').to_a.should == [
82
+ {:name => 'abc', :value => 456},
83
+ {:name => 'abc', :value => 123}
84
+ ]
85
+
86
+ @d.filter(:name => 'abc').limit(1).to_a.should == [
87
+ {:name => 'abc', :value => 123}
88
+ ]
89
+
90
+ @d.filter(:name => 'abc').order(:value.DESC).limit(1).to_a.should == [
91
+ {:name => 'abc', :value => 456}
92
+ ]
93
+
94
+ @d.filter(:name => 'abc').order(:value).limit(1).to_a.should == [
95
+ {:name => 'abc', :value => 123}
96
+ ]
97
+
98
+ @d.order(:value).limit(1).to_a.should == [
99
+ {:name => 'abc', :value => 123}
100
+ ]
101
+
102
+ @d.order(:value).limit(1, 1).to_a.should == [
103
+ {:name => 'abc', :value => 456}
104
+ ]
105
+
106
+ @d.order(:value).limit(1, 2).to_a.should == [
107
+ {:name => 'def', :value => 789}
108
+ ]
109
+
110
+ @d.avg(:value).to_i.should == (789+123+456)/3
111
+
112
+ @d.max(:value).to_i.should == 789
113
+
114
+ @d.select(:name, :AVG[:value]).filter(:name => 'abc').group(:name).to_a.should == [
115
+ {:name => 'abc', :"avg(value)" => (456+123)/2.0}
116
+ ]
117
+
118
+ @d.select(:AVG[:value]).group(:name).order(:name).limit(1).to_a.should == [
119
+ {:"avg(value)" => (456+123)/2.0}
120
+ ]
121
+
122
+ @d.select(:name, :AVG[:value]).group(:name).order(:name).to_a.should == [
123
+ {:name => 'abc', :"avg(value)" => (456+123)/2.0},
124
+ {:name => 'def', :"avg(value)" => 789*1.0}
125
+ ]
126
+
127
+ @d.select(:name, :AVG[:value]).group(:name).order(:name).to_a.should == [
128
+ {:name => 'abc', :"avg(value)" => (456+123)/2.0},
129
+ {:name => 'def', :"avg(value)" => 789*1.0}
130
+ ]
131
+
132
+ @d.select(:name, :AVG[:value]).group(:name).having(:name => ['abc', 'def']).order(:name).to_a.should == [
133
+ {:name => 'abc', :"avg(value)" => (456+123)/2.0},
134
+ {:name => 'def', :"avg(value)" => 789*1.0}
135
+ ]
136
+
137
+ @d.select(:name, :value).filter(:name => 'abc').union(@d.select(:name, :value).filter(:name => 'def')).order(:value).to_a.should == [
138
+ {:name => 'abc', :value => 123},
139
+ {:name => 'abc', :value => 456},
140
+ {:name => 'def', :value => 789}
141
+ ]
142
+
48
143
  end
49
144
 
50
145
  specify "should update records correctly" do
@@ -83,9 +178,51 @@ context "A Oracle dataset" do
83
178
  end
84
179
  end
85
180
 
86
- context "A Oracle dataset in array tuples mode" do
181
+ context "Joined Oracle dataset" do
182
+ setup do
183
+ @d1 = ORACLE_DB[:books]
184
+ @d1.delete # remove all records
185
+ @d1 << {:id => 1, :title => 'aaa', :category_id => 100}
186
+ @d1 << {:id => 2, :title => 'bbb', :category_id => 100}
187
+ @d1 << {:id => 3, :title => 'ccc', :category_id => 101}
188
+ @d1 << {:id => 4, :title => 'ddd', :category_id => 102}
189
+
190
+ @d2 = ORACLE_DB[:categories]
191
+ @d2.delete # remove all records
192
+ @d2 << {:id => 100, :cat_name => 'ruby'}
193
+ @d2 << {:id => 101, :cat_name => 'rails'}
194
+ end
195
+
196
+ specify "should return correct result" do
197
+ @d1.join(:categories, :id => :category_id).select(:books__id, :title, :cat_name).order(:books__id).to_a.should == [
198
+ {:id => 1, :title => 'aaa', :cat_name => 'ruby'},
199
+ {:id => 2, :title => 'bbb', :cat_name => 'ruby'},
200
+ {:id => 3, :title => 'ccc', :cat_name => 'rails'}
201
+ ]
202
+
203
+ @d1.join(:categories, :id => :category_id).select(:books__id, :title, :cat_name).order(:books__id).limit(2, 1).to_a.should == [
204
+ {:id => 2, :title => 'bbb', :cat_name => 'ruby'},
205
+ {:id => 3, :title => 'ccc', :cat_name => 'rails'},
206
+ ]
207
+
208
+ @d1.left_outer_join(:categories, :id => :category_id).select(:books__id, :title, :cat_name).order(:books__id).to_a.should == [
209
+ {:id => 1, :title => 'aaa', :cat_name => 'ruby'},
210
+ {:id => 2, :title => 'bbb', :cat_name => 'ruby'},
211
+ {:id => 3, :title => 'ccc', :cat_name => 'rails'},
212
+ {:id => 4, :title => 'ddd', :cat_name => nil}
213
+ ]
214
+
215
+ @d1.left_outer_join(:categories, :id => :category_id).select(:books__id, :title, :cat_name).order(:books__id.DESC).limit(2, 0).to_a.should == [
216
+ {:id => 4, :title => 'ddd', :cat_name => nil},
217
+ {:id => 3, :title => 'ccc', :cat_name => 'rails'}
218
+ ]
219
+ end
220
+ end
221
+
222
+
223
+ context "An Oracle dataset in array tuples mode" do
87
224
  setup do
88
- @d = ORACLE_DB[:test]
225
+ @d = ORACLE_DB[:items]
89
226
  @d.delete # remove all records
90
227
  Sequel.use_array_tuples
91
228
  end
@@ -105,6 +242,15 @@ context "A Oracle dataset in array tuples mode" do
105
242
  ['abc', 456],
106
243
  ['def', 789]
107
244
  ]
245
+
246
+ @d.order(:value).select(:name, :value).limit(1).to_a.should == [
247
+ ['abc',123]
248
+ ]
249
+
250
+ @d.order(:value).select(:name, :value).limit(2,1).to_a.should == [
251
+ ['abc',456],
252
+ ['def',789]
253
+ ]
108
254
  end
109
255
 
110
256
  specify "should work correctly with transforms" do