sequel_core 2.0.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/CHANGELOG +50 -0
  2. data/README +1 -2
  3. data/Rakefile +1 -1
  4. data/bin/sequel +26 -11
  5. data/doc/dataset_filtering.rdoc +9 -2
  6. data/lib/sequel_core/adapters/adapter_skeleton.rb +0 -2
  7. data/lib/sequel_core/adapters/mysql.rb +11 -97
  8. data/lib/sequel_core/adapters/odbc_mssql.rb +1 -1
  9. data/lib/sequel_core/adapters/oracle.rb +1 -1
  10. data/lib/sequel_core/adapters/postgres.rb +33 -17
  11. data/lib/sequel_core/adapters/sqlite.rb +1 -1
  12. data/lib/sequel_core/connection_pool.rb +12 -35
  13. data/lib/sequel_core/core_ext.rb +5 -19
  14. data/lib/sequel_core/core_sql.rb +17 -6
  15. data/lib/sequel_core/database.rb +14 -2
  16. data/lib/sequel_core/dataset/callback.rb +0 -3
  17. data/lib/sequel_core/dataset/convenience.rb +4 -3
  18. data/lib/sequel_core/dataset/parse_tree_sequelizer.rb +0 -21
  19. data/lib/sequel_core/dataset/sequelizer.rb +42 -43
  20. data/lib/sequel_core/dataset/sql.rb +121 -62
  21. data/lib/sequel_core/dataset.rb +20 -4
  22. data/lib/sequel_core/deprecated.rb +0 -6
  23. data/lib/sequel_core/migration.rb +4 -0
  24. data/lib/sequel_core/object_graph.rb +8 -6
  25. data/lib/sequel_core/pretty_table.rb +1 -1
  26. data/lib/sequel_core/schema/sql.rb +2 -0
  27. data/lib/sequel_core/sql.rb +393 -153
  28. data/lib/sequel_core.rb +22 -7
  29. data/spec/adapters/mysql_spec.rb +11 -7
  30. data/spec/adapters/postgres_spec.rb +32 -4
  31. data/spec/blockless_filters_spec.rb +33 -6
  32. data/spec/connection_pool_spec.rb +18 -16
  33. data/spec/core_ext_spec.rb +0 -13
  34. data/spec/core_sql_spec.rb +59 -13
  35. data/spec/database_spec.rb +5 -5
  36. data/spec/dataset_spec.rb +167 -55
  37. data/spec/object_graph_spec.rb +9 -4
  38. data/spec/sequelizer_spec.rb +8 -17
  39. data/spec/spec_helper.rb +4 -3
  40. metadata +2 -2
data/lib/sequel_core.rb CHANGED
@@ -2,7 +2,7 @@
2
2
  require f
3
3
  end
4
4
  %w"core_ext sql core_sql connection_pool exceptions pretty_table
5
- dataset migration schema database worker object_graph".each do |f|
5
+ dataset migration schema database worker object_graph deprecated".each do |f|
6
6
  require "sequel_core/#{f}"
7
7
  end
8
8
 
@@ -17,18 +17,30 @@ end
17
17
  # :password=>'password', :host=>'host', :port=>5432, \
18
18
  # :max_connections=>10)
19
19
  #
20
- # If a block is given to these meethods, it is passed the opened Database
21
- # object, which is closed when the block exits. For example:
20
+ # If a block is given to these methods, it is passed the opened Database
21
+ # object, which is closed (disconnected) when the block exits. For example:
22
22
  #
23
23
  # Sequel.sqlite('blog.db'){|db| puts db.users.count}
24
24
  #
25
25
  # Sequel can use either Time or DateTime for times returned from the
26
26
  # database. It defaults to Time. To change it to DateTime, use:
27
27
  #
28
- # Sequel.time_class = DateTime
28
+ # Sequel.datetime_class = DateTime
29
+ #
30
+ # Sequel can either use ParseTree for block filters (deprecated but works),
31
+ # or it can use the block filter syntax inside block filters (which will
32
+ # be the only behavior allowed in Sequel 2.2). To set it not to use
33
+ # ParseTree filters:
34
+ #
35
+ # Sequel.use_parse_tree = false
29
36
  module Sequel
30
- @time_class = Time
31
- metaattr_accessor :time_class
37
+ @datetime_class = Time
38
+ @use_parse_tree = !defined?(SEQUEL_NO_PARSE_TREE)
39
+
40
+ metaattr_accessor :datetime_class
41
+ metaattr_accessor :use_parse_tree
42
+
43
+ Deprecation.deprecation_message_stream = $stderr
32
44
 
33
45
  # Creates a new database object based on the supplied connection string
34
46
  # and optional arguments. The specified scheme determines the database
@@ -45,6 +57,8 @@ module Sequel
45
57
  # closed when the block exits. For example:
46
58
  #
47
59
  # Sequel.connect('sqlite://blog.db'){|db| puts db.users.count}
60
+ #
61
+ # This is also aliased as Sequel.open.
48
62
  def self.connect(*args, &block)
49
63
  Database.connect(*args, &block)
50
64
  end
@@ -107,7 +121,8 @@ module Sequel
107
121
  instance_eval("def #{adapter}(*args, &block); adapter_method('#{adapter}', *args, &block) end")
108
122
  end
109
123
  end
110
- metaprivate :adapter_method, :def_adapter_method
124
+
125
+ private_class_method :adapter_method, :def_adapter_method
111
126
 
112
127
  # Add the database adapter class methods to Sequel via metaprogramming
113
128
  def_adapter_method(*Database::ADAPTERS)
@@ -179,13 +179,13 @@ context "A MySQL dataset" do
179
179
  'SELECT * FROM `items` ORDER BY `name` DESC'
180
180
 
181
181
  @d.reverse_order(:name.desc).sql.should == \
182
- 'SELECT * FROM `items` ORDER BY `name`'
182
+ 'SELECT * FROM `items` ORDER BY `name` ASC'
183
183
 
184
184
  @d.reverse_order(:name, :test.desc).sql.should == \
185
- 'SELECT * FROM `items` ORDER BY `name` DESC, `test`'
185
+ 'SELECT * FROM `items` ORDER BY `name` DESC, `test` ASC'
186
186
 
187
187
  @d.reverse_order(:name.desc, :test).sql.should == \
188
- 'SELECT * FROM `items` ORDER BY `name`, `test` DESC'
188
+ 'SELECT * FROM `items` ORDER BY `name` ASC, `test` DESC'
189
189
  end
190
190
 
191
191
  specify "should support ORDER clause in UPDATE statements" do
@@ -318,9 +318,13 @@ context "MySQL join expressions" do
318
318
  @ds.join_table(:natural_inner, :nodes).sql.should == \
319
319
  'SELECT * FROM nodes NATURAL LEFT JOIN nodes'
320
320
  end
321
- specify "should support cross joins (equivalent to inner join in MySQL, not in std SQL)" do
321
+ specify "should support cross joins" do
322
322
  @ds.join_table(:cross, :nodes).sql.should == \
323
- 'SELECT * FROM nodes INNER JOIN nodes'
323
+ 'SELECT * FROM nodes CROSS JOIN nodes'
324
+ end
325
+ specify "should support cross joins as inner joins if conditions are used" do
326
+ @ds.join_table(:cross, :nodes, :id=>:id).sql.should == \
327
+ 'SELECT * FROM nodes INNER JOIN nodes ON (nodes.id = nodes.id)'
324
328
  end
325
329
  specify "should support straight joins (force left table to be read before right)" do
326
330
  @ds.join_table(:straight, :nodes).sql.should == \
@@ -328,11 +332,11 @@ context "MySQL join expressions" do
328
332
  end
329
333
  specify "should support natural joins on multiple tables." do
330
334
  @ds.join_table(:natural_left_outer, [:nodes, :branches]).sql.should == \
331
- 'SELECT * FROM nodes NATURAL LEFT OUTER JOIN ( nodes, branches )'
335
+ 'SELECT * FROM nodes NATURAL LEFT OUTER JOIN (nodes, branches)'
332
336
  end
333
337
  specify "should support straight joins on multiple tables." do
334
338
  @ds.join_table(:straight, [:nodes,:branches]).sql.should == \
335
- 'SELECT * FROM nodes STRAIGHT_JOIN ( nodes, branches )'
339
+ 'SELECT * FROM nodes STRAIGHT_JOIN (nodes, branches)'
336
340
  end
337
341
  end
338
342
 
@@ -39,7 +39,10 @@ context "A PostgreSQL database" do
39
39
  end
40
40
 
41
41
  specify "should correctly parse the schema" do
42
- @db.schema(:test3, :reload=>true).should == [[:value, {:type=>:integer, :allow_null=>true, :max_chars=>0, :default=>nil, :db_type=>"integer", :numeric_precision=>32}], [:time, {:type=>:datetime, :allow_null=>true, :max_chars=>0, :default=>nil, :db_type=>"timestamp without time zone", :numeric_precision=>0}]]
42
+ @db.schema(:test3, :reload=>true).should == [
43
+ [:value, {:type=>:integer, :allow_null=>true, :max_chars=>nil, :default=>nil, :db_type=>"integer", :numeric_precision=>32}],
44
+ [:time, {:type=>:datetime, :allow_null=>true, :max_chars=>nil, :default=>nil, :db_type=>"timestamp without time zone", :numeric_precision=>nil}]
45
+ ]
43
46
  end
44
47
 
45
48
  specify "should get the schema all database tables if no table name is used" do
@@ -152,13 +155,13 @@ context "A PostgreSQL dataset" do
152
155
  'SELECT * FROM "test" ORDER BY "name" DESC'
153
156
 
154
157
  @d.reverse_order(:name.desc).sql.should == \
155
- 'SELECT * FROM "test" ORDER BY "name"'
158
+ 'SELECT * FROM "test" ORDER BY "name" ASC'
156
159
 
157
160
  @d.reverse_order(:name, :test.desc).sql.should == \
158
- 'SELECT * FROM "test" ORDER BY "name" DESC, "test"'
161
+ 'SELECT * FROM "test" ORDER BY "name" DESC, "test" ASC'
159
162
 
160
163
  @d.reverse_order(:name.desc, :test).sql.should == \
161
- 'SELECT * FROM "test" ORDER BY "name", "test" DESC'
164
+ 'SELECT * FROM "test" ORDER BY "name" ASC, "test" DESC'
162
165
  end
163
166
 
164
167
  specify "should support transactions" do
@@ -202,6 +205,31 @@ context "A PostgreSQL dataset" do
202
205
  @d.count.should == 2
203
206
  end
204
207
 
208
+ specify "should support nested transactions through savepoints" do
209
+ POSTGRES_DB.transaction do
210
+ @d << {:name => '1'}
211
+ POSTGRES_DB.transaction do
212
+ @d << {:name => '2'}
213
+ POSTGRES_DB.transaction do
214
+ @d << {:name => '3'}
215
+ raise Sequel::Error::Rollback
216
+ end
217
+ @d << {:name => '4'}
218
+ POSTGRES_DB.transaction do
219
+ @d << {:name => '6'}
220
+ POSTGRES_DB.transaction do
221
+ @d << {:name => '7'}
222
+ end
223
+ raise Sequel::Error::Rollback
224
+ end
225
+ @d << {:name => '5'}
226
+ end
227
+ end
228
+
229
+ @d.count.should == 4
230
+ @d.order(:name).map(:name).should == %w{1 2 4 5}
231
+ end
232
+
205
233
  specify "should support regexps" do
206
234
  @d << {:name => 'abc', :value => 1}
207
235
  @d << {:name => 'bcd', :value => 2}
@@ -5,13 +5,21 @@ context "Blockless Ruby Filters" do
5
5
  db = Sequel::Database.new
6
6
  db.quote_identifiers = false
7
7
  @d = db[:items]
8
- def @d.l(*args)
9
- literal(filter_expr(*args))
8
+ def @d.l(*args, &block)
9
+ if block_given?
10
+ literal(filter_expr(Proc.new(&block)))
11
+ else
12
+ literal(filter_expr(*args))
13
+ end
10
14
  end
11
15
  def @d.lit(*args)
12
16
  literal(*args)
13
17
  end
14
18
  end
19
+
20
+ after do
21
+ Sequel.use_parse_tree = true
22
+ end
15
23
 
16
24
  it "should support boolean columns directly" do
17
25
  @d.l(:x).should == 'x'
@@ -130,11 +138,8 @@ context "Blockless Ruby Filters" do
130
138
  @d.l(~((((:x - :y)/(:x + :y))*:z) <= 100)).should == '((((x - y) / (x + y)) * z) > 100)'
131
139
  end
132
140
 
133
- it "should not allow negation of non-boolean expressions" do
134
- proc{~(:x + 1 > 100)}.should_not raise_error
135
- proc{~(:x + 1)}.should raise_error
141
+ it "should not allow negation of string expressions" do
136
142
  proc{~:x.sql_string}.should raise_error
137
- proc{~:x.sql_number}.should raise_error
138
143
  proc{~([:x, :y].sql_string_join)}.should raise_error
139
144
  end
140
145
 
@@ -294,4 +299,26 @@ context "Blockless Ruby Filters" do
294
299
  @d.lit([:x].sql_string_join + :y).should == '((x) || y)'
295
300
  @d.lit([:x, :z].sql_string_join(' ') + :y).should == "((x || ' ' || z) || y)"
296
301
  end
302
+
303
+ it "should be supported inside blocks if Sequel.use_parse_tree = false" do
304
+ @d.l{[[:x, nil], [:y, [1,2,3]]].sql_or}.should == '((x IS NULL) OR (y IN (1, 2, 3)))'
305
+ @d.l{~[[:x, nil], [:y, [1,2,3]]]}.should == '((x IS NOT NULL) OR (y NOT IN (1, 2, 3)))'
306
+ @d.l{~(((('x'.lit - :y)/(:x + :y))*:z) <= 100)}.should == '((((x - y) / (x + y)) * z) > 100)'
307
+ @d.l{{:x => :a} & {:y => :z}}.should == '((x = a) AND (y = z))'
308
+ end
309
+
310
+ it "should support &, |, ^, ~, <<, and >> for NumericExpressions" do
311
+ @d.l(:x.sql_number & 1 > 100).should == '((x & 1) > 100)'
312
+ @d.l(:x.sql_number | 1 > 100).should == '((x | 1) > 100)'
313
+ @d.l(:x.sql_number ^ 1 > 100).should == '((x ^ 1) > 100)'
314
+ @d.l(~:x.sql_number > 100).should == '(~x > 100)'
315
+ @d.l(:x.sql_number << 1 > 100).should == '((x << 1) > 100)'
316
+ @d.l(:x.sql_number >> 1 > 100).should == '((x >> 1) > 100)'
317
+ @d.l((:x + 1) & 1 > 100).should == '(((x + 1) & 1) > 100)'
318
+ @d.l((:x + 1) | 1 > 100).should == '(((x + 1) | 1) > 100)'
319
+ @d.l((:x + 1) ^ 1 > 100).should == '(((x + 1) ^ 1) > 100)'
320
+ @d.l(~(:x + 1) > 100).should == '(~(x + 1) > 100)'
321
+ @d.l((:x + 1) << 1 > 100).should == '(((x + 1) << 1) > 100)'
322
+ @d.l((:x + 1) >> 1 > 100).should == '(((x + 1) >> 1) > 100)'
323
+ end
297
324
  end
@@ -4,7 +4,7 @@ CONNECTION_POOL_DEFAULTS = {:pool_timeout=>5, :pool_sleep_time=>0.001,
4
4
 
5
5
  context "An empty ConnectionPool" do
6
6
  setup do
7
- @cpool = ConnectionPool.new(CONNECTION_POOL_DEFAULTS)
7
+ @cpool = Sequel::ConnectionPool.new(CONNECTION_POOL_DEFAULTS)
8
8
  end
9
9
 
10
10
  specify "should have no available connections" do
@@ -12,7 +12,7 @@ context "An empty ConnectionPool" do
12
12
  end
13
13
 
14
14
  specify "should have no allocated connections" do
15
- @cpool.allocated.should == []
15
+ @cpool.allocated.should == {}
16
16
  end
17
17
 
18
18
  specify "should have a created_count of zero" do
@@ -23,14 +23,14 @@ end
23
23
  context "A connection pool handling connections" do
24
24
  setup do
25
25
  @max_size = 2
26
- @cpool = ConnectionPool.new(CONNECTION_POOL_DEFAULTS.merge(:max_connections=>@max_size)) {:got_connection}
26
+ @cpool = Sequel::ConnectionPool.new(CONNECTION_POOL_DEFAULTS.merge(:max_connections=>@max_size)) {:got_connection}
27
27
  end
28
28
 
29
29
  specify "#hold should increment #created_count" do
30
30
  @cpool.hold do
31
31
  @cpool.created_count.should == 1
32
- @cpool.hold {@cpool.created_count.should == 2}
33
- @cpool.hold {@cpool.hold {@cpool.created_count.should == 2}}
32
+ @cpool.hold {@cpool.hold {@cpool.created_count.should == 1}}
33
+ Thread.new{@cpool.hold {@cpool.created_count.should == 2}}.join
34
34
  end
35
35
  end
36
36
 
@@ -38,7 +38,7 @@ context "A connection pool handling connections" do
38
38
  @cpool.hold do
39
39
  @cpool.allocated.size.should == 1
40
40
 
41
- @cpool.allocated.should == [[Thread.current, :got_connection]]
41
+ @cpool.allocated.should == {Thread.current=>:got_connection}
42
42
  end
43
43
  end
44
44
 
@@ -76,7 +76,7 @@ end
76
76
 
77
77
  context "ConnectionPool#hold" do
78
78
  setup do
79
- @pool = ConnectionPool.new(CONNECTION_POOL_DEFAULTS) {DummyConnection.new}
79
+ @pool = Sequel::ConnectionPool.new(CONNECTION_POOL_DEFAULTS) {DummyConnection.new}
80
80
  end
81
81
 
82
82
  specify "should pass the result of the connection maker proc to the supplied block" do
@@ -112,7 +112,7 @@ end
112
112
 
113
113
  context "ConnectionPool#connection_proc" do
114
114
  setup do
115
- @pool = ConnectionPool.new(CONNECTION_POOL_DEFAULTS)
115
+ @pool = Sequel::ConnectionPool.new(CONNECTION_POOL_DEFAULTS)
116
116
  end
117
117
 
118
118
  specify "should be nil if no block is supplied to the pool" do
@@ -131,7 +131,7 @@ end
131
131
  context "A connection pool with a max size of 1" do
132
132
  setup do
133
133
  @invoked_count = 0
134
- @pool = ConnectionPool.new(CONNECTION_POOL_DEFAULTS.merge(:max_connections=>1)) {@invoked_count += 1; 'herro'}
134
+ @pool = Sequel::ConnectionPool.new(CONNECTION_POOL_DEFAULTS.merge(:max_connections=>1)) {@invoked_count += 1; 'herro'}
135
135
  end
136
136
 
137
137
  specify "should let only one thread access the connection at any time" do
@@ -154,7 +154,7 @@ context "A connection pool with a max size of 1" do
154
154
  c2.should be_nil
155
155
 
156
156
  @pool.available_connections.should be_empty
157
- @pool.allocated.should == [[t1, cc]]
157
+ @pool.allocated.should == {t1=>cc}
158
158
 
159
159
  cc.gsub!('rr', 'll')
160
160
  sleep 0.5
@@ -166,7 +166,7 @@ context "A connection pool with a max size of 1" do
166
166
  c2.should == 'hello'
167
167
 
168
168
  @pool.available_connections.should be_empty
169
- @pool.allocated.should == [[t2, cc]]
169
+ @pool.allocated.should == {t2=>cc}
170
170
 
171
171
  cc.gsub!('ll', 'rr')
172
172
  sleep 0.5
@@ -207,7 +207,7 @@ end
207
207
  context "A connection pool with a max size of 5" do
208
208
  setup do
209
209
  @invoked_count = 0
210
- @pool = ConnectionPool.new(CONNECTION_POOL_DEFAULTS.merge(:max_connections=>5)) {@invoked_count += 1}
210
+ @pool = Sequel::ConnectionPool.new(CONNECTION_POOL_DEFAULTS.merge(:max_connections=>5)) {@invoked_count += 1}
211
211
  end
212
212
 
213
213
  specify "should let five threads simultaneously access separate connections" do
@@ -223,7 +223,9 @@ context "A connection pool with a max size of 5" do
223
223
  @pool.size.should == 5
224
224
  @pool.available_connections.should be_empty
225
225
  i = 0
226
- @pool.allocated.should == threads.collect{|t| [t,i+=1]}
226
+ h = {}
227
+ threads.each{|t| h[t] = (i+=1)}
228
+ @pool.allocated.should == h
227
229
 
228
230
  threads[0].raise "your'e dead"
229
231
  sleep 0.1
@@ -232,7 +234,7 @@ context "A connection pool with a max size of 5" do
232
234
  sleep 0.1
233
235
 
234
236
  @pool.available_connections.should == [1, 4]
235
- @pool.allocated.should == [[threads[1], 2], [threads[2], 3], [threads[4], 5]]
237
+ @pool.allocated.should == {threads[1]=>2, threads[2]=>3, threads[4]=>5}
236
238
 
237
239
  stop = true
238
240
  sleep 0.2
@@ -277,7 +279,7 @@ end
277
279
  context "ConnectionPool#disconnect" do
278
280
  setup do
279
281
  @count = 0
280
- @pool = ConnectionPool.new(CONNECTION_POOL_DEFAULTS.merge(:max_connections=>5)) {{:id => @count += 1}}
282
+ @pool = Sequel::ConnectionPool.new(CONNECTION_POOL_DEFAULTS.merge(:max_connections=>5)) {{:id => @count += 1}}
281
283
  end
282
284
 
283
285
  specify "should invoke the given block for each available connection" do
@@ -341,7 +343,7 @@ end
341
343
 
342
344
  context "SingleThreadedPool" do
343
345
  setup do
344
- @pool = SingleThreadedPool.new(CONNECTION_POOL_DEFAULTS){1234}
346
+ @pool = Sequel::SingleThreadedPool.new(CONNECTION_POOL_DEFAULTS){1234}
345
347
  end
346
348
 
347
349
  specify "should provide a #hold method" do
@@ -76,19 +76,6 @@ context "Module#metaattr_reader" do
76
76
  end
77
77
  end
78
78
 
79
- context "Module#metaprivate" do
80
- specify "it should create aliases of singleton/class methods" do
81
- @c = Class.new do
82
- def self.x; 1; end
83
- end
84
- @c.x.should == 1
85
- @c.class_eval do
86
- metaprivate :x
87
- end
88
- proc{@c.x}.should raise_error(NoMethodError)
89
- end
90
- end
91
-
92
79
  context "Object#is_one_of?" do
93
80
  specify "it should be true if the object is one of the classes" do
94
81
  1.is_one_of?(Numeric, Array).should == true
@@ -23,6 +23,31 @@ context "Array#all_two_pairs?" do
23
23
  end
24
24
  end
25
25
 
26
+ context "Array#case and Hash#case" do
27
+ setup do
28
+ @d = Sequel::Dataset.new(nil)
29
+ end
30
+
31
+ specify "should return SQL CASE expression" do
32
+ @d.literal({:x=>:y}.case(:z)).should == '(CASE WHEN x THEN y ELSE z END)'
33
+ ['(CASE WHEN x THEN y WHEN a THEN b ELSE z END)',
34
+ '(CASE WHEN a THEN b WHEN x THEN y ELSE z END)'].should(include(@d.literal({:x=>:y, :a=>:b}.case(:z))))
35
+ @d.literal([[:x, :y]].case(:z)).should == '(CASE WHEN x THEN y ELSE z END)'
36
+ @d.literal([[:x, :y], [:a, :b]].case(:z)).should == '(CASE WHEN x THEN y WHEN a THEN b ELSE z END)'
37
+ end
38
+
39
+ specify "should raise an error if an array that isn't all two pairs is used" do
40
+ proc{[:b].case(:a)}.should raise_error(Sequel::Error)
41
+ proc{[:b, :c].case(:a)}.should raise_error(Sequel::Error)
42
+ proc{[[:b, :c], :d].case(:a)}.should raise_error(Sequel::Error)
43
+ end
44
+
45
+ specify "should raise an error if an empty array/hash is used" do
46
+ proc{[].case(:a)}.should raise_error(Sequel::Error)
47
+ proc{{}.case(:a)}.should raise_error(Sequel::Error)
48
+ end
49
+ end
50
+
26
51
  context "Array#to_sql" do
27
52
  specify "should concatenate multiple lines into a single string" do
28
53
  "SELECT * \r\nFROM items\r\n WHERE a = 1".split.to_sql. \
@@ -112,10 +137,6 @@ context "#desc" do
112
137
  specify "should format a DESC clause for a function" do
113
138
  :avg[:test].desc.to_s(@ds).should == 'avg(test) DESC'
114
139
  end
115
-
116
- specify "should format a DESC clause for a literal value" do
117
- 'abc'.desc.to_s(@ds).should == "'abc' DESC"
118
- end
119
140
  end
120
141
 
121
142
  context "#asc" do
@@ -132,10 +153,6 @@ context "#asc" do
132
153
  specify "should format a ASC clause for a function" do
133
154
  :avg[:test].asc.to_s(@ds).should == 'avg(test) ASC'
134
155
  end
135
-
136
- specify "should format a ASC clause for a literal value" do
137
- 'abc'.asc.to_s(@ds).should == "'abc' ASC"
138
- end
139
156
  end
140
157
 
141
158
  context "#as" do
@@ -191,7 +208,7 @@ context "Column references" do
191
208
 
192
209
  specify "should be quoted properly in a cast function" do
193
210
  @ds.literal(:x.cast_as(:integer)).should == "cast(`x` AS integer)"
194
- @ds.literal(:x__y.cast_as(:varchar[20])).should == "cast(`x`.`y` AS varchar(20))"
211
+ @ds.literal(:x__y.cast_as('varchar(20)')).should == "cast(`x`.`y` AS varchar(20))"
195
212
  end
196
213
  end
197
214
 
@@ -211,6 +228,12 @@ context "Symbol#*" do
211
228
  end
212
229
  end
213
230
 
231
+ context "Symbol#qualify" do
232
+ specify "should format a qualified column" do
233
+ :xyz.qualify(:abc).to_s(Sequel::Dataset.new(nil)).should == 'abc.xyz'
234
+ end
235
+ end
236
+
214
237
  context "Symbol#to_column_ref" do
215
238
  setup do
216
239
  @ds = MockDataset.new(nil)
@@ -262,8 +285,27 @@ context "Symbol" do
262
285
  ds.select(:COUNT['1']).sql.should == "SELECT COUNT('1') FROM t"
263
286
  end
264
287
 
265
- specify "should support cast function" do
288
+ specify "should support cast method and its cast_as alias" do
266
289
  :abc.cast_as(:integer).to_s(@ds).should == "cast(abc AS integer)"
290
+ :abc.cast(:integer).to_s(@ds).should == "cast(abc AS integer)"
291
+ end
292
+
293
+ specify "should support cast_numeric and cast_string" do
294
+ x = :abc.cast_numeric
295
+ x.should be_a_kind_of(Sequel::SQL::NumericExpression)
296
+ x.to_s(@ds).should == "cast(abc AS integer)"
297
+
298
+ x = :abc.cast_numeric(:real)
299
+ x.should be_a_kind_of(Sequel::SQL::NumericExpression)
300
+ x.to_s(@ds).should == "cast(abc AS real)"
301
+
302
+ x = :abc.cast_string
303
+ x.should be_a_kind_of(Sequel::SQL::StringExpression)
304
+ x.to_s(@ds).should == "cast(abc AS text)"
305
+
306
+ x = :abc.cast_string(:varchar)
307
+ x.should be_a_kind_of(Sequel::SQL::StringExpression)
308
+ x.to_s(@ds).should == "cast(abc AS varchar)"
267
309
  end
268
310
 
269
311
  specify "should support subscript access using | operator" do
@@ -272,6 +314,10 @@ context "Symbol" do
272
314
  (:abc|[1, 2]).to_s(@ds).should == 'abc[1, 2]'
273
315
  (:abc|1|2).to_s(@ds).should == 'abc[1, 2]'
274
316
  end
317
+
318
+ specify "should support SQL EXTRACT function via #extract " do
319
+ :abc.extract(:year).to_s(@ds).should == "extract(year FROM abc)"
320
+ end
275
321
  end
276
322
 
277
323
  context "String#to_time" do
@@ -307,7 +353,7 @@ end
307
353
 
308
354
  context "String#to_sequel_time" do
309
355
  after do
310
- Sequel.time_class = Time
356
+ Sequel.datetime_class = Time
311
357
  end
312
358
 
313
359
  specify "should convert the string into a Time object by default" do
@@ -316,14 +362,14 @@ context "String#to_sequel_time" do
316
362
  end
317
363
 
318
364
  specify "should convert the string into a DateTime object if that is set" do
319
- Sequel.time_class = DateTime
365
+ Sequel.datetime_class = DateTime
320
366
  "2007-07-11 10:11:12a".to_sequel_time.class.should == DateTime
321
367
  "2007-07-11 10:11:12a".to_sequel_time.should == DateTime.parse("2007-07-11 10:11:12a")
322
368
  end
323
369
 
324
370
  specify "should raise Error::InvalidValue for an invalid time" do
325
371
  proc {'0000-00-00'.to_sequel_time}.should raise_error(Sequel::Error::InvalidValue)
326
- Sequel.time_class = DateTime
372
+ Sequel.datetime_class = DateTime
327
373
  proc {'0000-00-00'.to_sequel_time}.should raise_error(Sequel::Error::InvalidValue)
328
374
  end
329
375
  end