sequel_core 2.0.1 → 2.1.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 (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