sequel 3.42.0 → 3.43.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 (80) hide show
  1. data/CHANGELOG +40 -0
  2. data/MIT-LICENSE +1 -1
  3. data/Rakefile +1 -1
  4. data/doc/opening_databases.rdoc +2 -2
  5. data/doc/prepared_statements.rdoc +7 -0
  6. data/doc/release_notes/3.43.0.txt +105 -0
  7. data/doc/schema_modification.rdoc +19 -0
  8. data/lib/sequel/adapters/do/mysql.rb +1 -1
  9. data/lib/sequel/adapters/jdbc.rb +13 -8
  10. data/lib/sequel/adapters/jdbc/hsqldb.rb +5 -0
  11. data/lib/sequel/adapters/jdbc/mysql.rb +1 -1
  12. data/lib/sequel/adapters/jdbc/oracle.rb +6 -0
  13. data/lib/sequel/adapters/jdbc/postgresql.rb +9 -3
  14. data/lib/sequel/adapters/mysql.rb +1 -1
  15. data/lib/sequel/adapters/mysql2.rb +1 -1
  16. data/lib/sequel/adapters/oracle.rb +1 -1
  17. data/lib/sequel/adapters/postgres.rb +4 -2
  18. data/lib/sequel/adapters/shared/db2.rb +12 -0
  19. data/lib/sequel/adapters/shared/mssql.rb +9 -5
  20. data/lib/sequel/adapters/shared/postgres.rb +2 -0
  21. data/lib/sequel/adapters/swift/mysql.rb +1 -1
  22. data/lib/sequel/core.rb +2 -2
  23. data/lib/sequel/database.rb +0 -2
  24. data/lib/sequel/database/query.rb +20 -5
  25. data/lib/sequel/database/schema_generator.rb +5 -0
  26. data/lib/sequel/database/schema_methods.rb +5 -0
  27. data/lib/sequel/dataset.rb +0 -2
  28. data/lib/sequel/dataset/actions.rb +25 -2
  29. data/lib/sequel/dataset/misc.rb +1 -1
  30. data/lib/sequel/dataset/sql.rb +28 -6
  31. data/lib/sequel/extensions/core_refinements.rb +221 -0
  32. data/lib/sequel/extensions/date_arithmetic.rb +194 -0
  33. data/lib/sequel/extensions/meta_def.rb +30 -0
  34. data/lib/sequel/extensions/migration.rb +5 -0
  35. data/lib/sequel/extensions/null_dataset.rb +2 -0
  36. data/lib/sequel/extensions/pagination.rb +2 -0
  37. data/lib/sequel/extensions/pg_array.rb +12 -1
  38. data/lib/sequel/extensions/pg_array_ops.rb +10 -1
  39. data/lib/sequel/extensions/pg_hstore.rb +12 -1
  40. data/lib/sequel/extensions/pg_hstore_ops.rb +10 -1
  41. data/lib/sequel/extensions/pg_json.rb +18 -1
  42. data/lib/sequel/extensions/pg_range.rb +12 -1
  43. data/lib/sequel/extensions/pg_range_ops.rb +10 -1
  44. data/lib/sequel/extensions/pg_row.rb +18 -2
  45. data/lib/sequel/extensions/pg_row_ops.rb +10 -1
  46. data/lib/sequel/extensions/query.rb +2 -0
  47. data/lib/sequel/model/associations.rb +5 -13
  48. data/lib/sequel/model/base.rb +4 -6
  49. data/lib/sequel/plugins/boolean_readers.rb +4 -2
  50. data/lib/sequel/plugins/many_through_many.rb +23 -0
  51. data/lib/sequel/plugins/string_stripper.rb +53 -3
  52. data/lib/sequel/plugins/validation_class_methods.rb +5 -0
  53. data/lib/sequel/sql.rb +3 -3
  54. data/lib/sequel/version.rb +1 -1
  55. data/spec/adapters/db2_spec.rb +19 -8
  56. data/spec/adapters/mssql_spec.rb +1 -2
  57. data/spec/adapters/mysql_spec.rb +2 -2
  58. data/spec/adapters/postgres_spec.rb +29 -3
  59. data/spec/core/dataset_spec.rb +107 -0
  60. data/spec/core/expression_filters_spec.rb +5 -0
  61. data/spec/core/schema_spec.rb +14 -3
  62. data/spec/core/spec_helper.rb +2 -0
  63. data/spec/extensions/core_refinements_spec.rb +551 -0
  64. data/spec/extensions/date_arithmetic_spec.rb +150 -0
  65. data/spec/extensions/force_encoding_spec.rb +1 -1
  66. data/spec/extensions/meta_def_spec.rb +21 -0
  67. data/spec/extensions/spec_helper.rb +5 -0
  68. data/spec/extensions/string_stripper_spec.rb +44 -2
  69. data/spec/integration/associations_test.rb +2 -2
  70. data/spec/integration/plugin_test.rb +90 -0
  71. data/spec/integration/schema_test.rb +1 -1
  72. data/spec/model/association_reflection_spec.rb +4 -4
  73. data/spec/model/associations_spec.rb +2 -2
  74. data/spec/model/base_spec.rb +2 -2
  75. data/spec/model/eager_loading_spec.rb +5 -5
  76. data/spec/model/hooks_spec.rb +4 -4
  77. data/spec/model/model_spec.rb +9 -9
  78. data/spec/model/record_spec.rb +15 -18
  79. metadata +12 -5
  80. data/lib/sequel/metaprogramming.rb +0 -13
@@ -594,6 +594,11 @@ describe "Sequel core extension replacements" do
594
594
  l(Sequel.expr(proc{|v| @o}) + 1, "(foo + 1)")
595
595
  end
596
596
 
597
+ it "Sequel.expr should handle lambda proc virtual rows" do
598
+ l(Sequel.expr(&lambda{1}), "1")
599
+ l(Sequel.expr(&lambda{|| 1}), "1")
600
+ end
601
+
597
602
  it "Sequel.expr should raise an error if given an argument and a block" do
598
603
  proc{Sequel.expr(nil){}}.should raise_error(Sequel::Error)
599
604
  end
@@ -1327,16 +1327,27 @@ describe "Schema Parser" do
1327
1327
 
1328
1328
  specify "should convert various types of table name arguments" do
1329
1329
  @db.meta_def(:schema_parse_table) do |t, opts|
1330
- [[t, {:db_type=>t}]]
1330
+ [[t, opts]]
1331
1331
  end
1332
1332
  s1 = @db.schema(:x)
1333
- s1.should == [['x', {:db_type=>'x', :ruby_default=>nil}]]
1333
+ s1.should == [['x', {:ruby_default=>nil}]]
1334
1334
  @db.schema(:x).object_id.should == s1.object_id
1335
1335
  @db.schema(Sequel.identifier(:x)).object_id.should == s1.object_id
1336
+
1336
1337
  s2 = @db.schema(:x__y)
1337
- s2.should == [['y', {:db_type=>'y', :ruby_default=>nil}]]
1338
+ s2.should == [['y', {:schema=>'x', :ruby_default=>nil}]]
1338
1339
  @db.schema(:x__y).object_id.should == s2.object_id
1339
1340
  @db.schema(Sequel.qualify(:x, :y)).object_id.should == s2.object_id
1341
+
1342
+ s2 = @db.schema(Sequel.qualify(:v, :x__y))
1343
+ s2.should == [['y', {:schema=>'x', :ruby_default=>nil, :information_schema_schema=>Sequel.identifier('v')}]]
1344
+ @db.schema(Sequel.qualify(:v, :x__y)).object_id.should == s2.object_id
1345
+ @db.schema(Sequel.qualify(:v__x, :y)).object_id.should == s2.object_id
1346
+
1347
+ s2 = @db.schema(Sequel.qualify(:u__v, :x__y))
1348
+ s2.should == [['y', {:schema=>'x', :ruby_default=>nil, :information_schema_schema=>Sequel.qualify('u', 'v')}]]
1349
+ @db.schema(Sequel.qualify(:u__v, :x__y)).object_id.should == s2.object_id
1350
+ @db.schema(Sequel.qualify(Sequel.qualify(:u, :v), Sequel.qualify(:x, :y))).object_id.should == s2.object_id
1340
1351
  end
1341
1352
 
1342
1353
  specify "should correctly parse all supported data types" do
@@ -6,6 +6,8 @@ unless Object.const_defined?('Sequel')
6
6
  require 'sequel/core'
7
7
  end
8
8
 
9
+ Sequel.extension :meta_def
10
+
9
11
  if ENV['SEQUEL_COLUMNS_INTROSPECTION']
10
12
  Sequel.extension :columns_introspection
11
13
  Sequel::Dataset.introspect_all_columns
@@ -0,0 +1,551 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ if RUBY_VERSION >= '2.0.0'
4
+ using Sequel::CoreRefinements
5
+
6
+ describe "Core refinements" do
7
+ before do
8
+ db = Sequel::Database.new
9
+ @d = db[:items]
10
+ def @d.supports_regexp?; true end
11
+ def @d.l(*args, &block)
12
+ literal(filter_expr(*args, &block))
13
+ end
14
+ def @d.lit(*args)
15
+ literal(*args)
16
+ end
17
+ end
18
+
19
+ if RUBY_VERSION < '1.9.0'
20
+ it "should not allow inequality operations on true, false, or nil" do
21
+ @d.lit(:x > 1).should == "(x > 1)"
22
+ @d.lit(:x < true).should == "(x < 't')"
23
+ @d.lit(:x >= false).should == "(x >= 'f')"
24
+ @d.lit(:x <= nil).should == "(x <= NULL)"
25
+ end
26
+
27
+ it "should not allow inequality operations on boolean complex expressions" do
28
+ @d.lit(:x > (:y > 5)).should == "(x > (y > 5))"
29
+ @d.lit(:x < (:y < 5)).should == "(x < (y < 5))"
30
+ @d.lit(:x >= (:y >= 5)).should == "(x >= (y >= 5))"
31
+ @d.lit(:x <= (:y <= 5)).should == "(x <= (y <= 5))"
32
+ @d.lit(:x > {:y => nil}).should == "(x > (y IS NULL))"
33
+ @d.lit(:x < ~{:y => nil}).should == "(x < (y IS NOT NULL))"
34
+ @d.lit(:x >= {:y => 5}).should == "(x >= (y = 5))"
35
+ @d.lit(:x <= ~{:y => 5}).should == "(x <= (y != 5))"
36
+ @d.lit(:x >= {:y => [1,2,3]}).should == "(x >= (y IN (1, 2, 3)))"
37
+ @d.lit(:x <= ~{:y => [1,2,3]}).should == "(x <= (y NOT IN (1, 2, 3)))"
38
+ end
39
+
40
+ it "should support >, <, >=, and <= via Symbol#>,<,>=,<=" do
41
+ @d.l(:x > 100).should == '(x > 100)'
42
+ @d.l(:x < 100.01).should == '(x < 100.01)'
43
+ @d.l(:x >= 100000000000000000000000000000000000).should == '(x >= 100000000000000000000000000000000000)'
44
+ @d.l(:x <= 100).should == '(x <= 100)'
45
+ end
46
+
47
+ it "should support negation of >, <, >=, and <= via Symbol#~" do
48
+ @d.l(~(:x > 100)).should == '(x <= 100)'
49
+ @d.l(~(:x < 100.01)).should == '(x >= 100.01)'
50
+ @d.l(~(:x >= 100000000000000000000000000000000000)).should == '(x < 100000000000000000000000000000000000)'
51
+ @d.l(~(:x <= 100)).should == '(x > 100)'
52
+ end
53
+
54
+ it "should support double negation via ~" do
55
+ @d.l(~~(:x > 100)).should == '(x > 100)'
56
+ end
57
+ end
58
+ it "should support NOT via Symbol#~" do
59
+ @d.l(~:x).should == 'NOT x'
60
+ @d.l(~:x__y).should == 'NOT x.y'
61
+ end
62
+
63
+ it "should support + - * / via Symbol#+,-,*,/" do
64
+ @d.l(:x + 1 > 100).should == '((x + 1) > 100)'
65
+ @d.l((:x * :y) < 100.01).should == '((x * y) < 100.01)'
66
+ @d.l((:x - :y/2) >= 100000000000000000000000000000000000).should == '((x - (y / 2)) >= 100000000000000000000000000000000000)'
67
+ @d.l((((:x - :y)/(:x + :y))*:z) <= 100).should == '((((x - y) / (x + y)) * z) <= 100)'
68
+ @d.l(~((((:x - :y)/(:x + :y))*:z) <= 100)).should == '((((x - y) / (x + y)) * z) > 100)'
69
+ end
70
+
71
+ it "should support LIKE via Symbol#like" do
72
+ @d.l(:x.like('a')).should == '(x LIKE \'a\')'
73
+ @d.l(:x.like(/a/)).should == '(x ~ \'a\')'
74
+ @d.l(:x.like('a', 'b')).should == '((x LIKE \'a\') OR (x LIKE \'b\'))'
75
+ @d.l(:x.like(/a/, /b/i)).should == '((x ~ \'a\') OR (x ~* \'b\'))'
76
+ @d.l(:x.like('a', /b/)).should == '((x LIKE \'a\') OR (x ~ \'b\'))'
77
+ end
78
+
79
+ it "should support NOT LIKE via Symbol#like and Symbol#~" do
80
+ @d.l(~:x.like('a')).should == '(x NOT LIKE \'a\')'
81
+ @d.l(~:x.like(/a/)).should == '(x !~ \'a\')'
82
+ @d.l(~:x.like('a', 'b')).should == '((x NOT LIKE \'a\') AND (x NOT LIKE \'b\'))'
83
+ @d.l(~:x.like(/a/, /b/i)).should == '((x !~ \'a\') AND (x !~* \'b\'))'
84
+ @d.l(~:x.like('a', /b/)).should == '((x NOT LIKE \'a\') AND (x !~ \'b\'))'
85
+ end
86
+
87
+ it "should support ILIKE via Symbol#ilike" do
88
+ @d.l(:x.ilike('a')).should == '(x ILIKE \'a\')'
89
+ @d.l(:x.ilike(/a/)).should == '(x ~* \'a\')'
90
+ @d.l(:x.ilike('a', 'b')).should == '((x ILIKE \'a\') OR (x ILIKE \'b\'))'
91
+ @d.l(:x.ilike(/a/, /b/i)).should == '((x ~* \'a\') OR (x ~* \'b\'))'
92
+ @d.l(:x.ilike('a', /b/)).should == '((x ILIKE \'a\') OR (x ~* \'b\'))'
93
+ end
94
+
95
+ it "should support NOT ILIKE via Symbol#ilike and Symbol#~" do
96
+ @d.l(~:x.ilike('a')).should == '(x NOT ILIKE \'a\')'
97
+ @d.l(~:x.ilike(/a/)).should == '(x !~* \'a\')'
98
+ @d.l(~:x.ilike('a', 'b')).should == '((x NOT ILIKE \'a\') AND (x NOT ILIKE \'b\'))'
99
+ @d.l(~:x.ilike(/a/, /b/i)).should == '((x !~* \'a\') AND (x !~* \'b\'))'
100
+ @d.l(~:x.ilike('a', /b/)).should == '((x NOT ILIKE \'a\') AND (x !~* \'b\'))'
101
+ end
102
+
103
+ it "should support sql_expr on arrays with all two pairs" do
104
+ @d.l([[:x, 100],[:y, 'a']].sql_expr).should == '((x = 100) AND (y = \'a\'))'
105
+ @d.l([[:x, true], [:y, false]].sql_expr).should == '((x IS TRUE) AND (y IS FALSE))'
106
+ @d.l([[:x, nil], [:y, [1,2,3]]].sql_expr).should == '((x IS NULL) AND (y IN (1, 2, 3)))'
107
+ end
108
+
109
+ it "should support sql_negate on arrays with all two pairs" do
110
+ @d.l([[:x, 100],[:y, 'a']].sql_negate).should == '((x != 100) AND (y != \'a\'))'
111
+ @d.l([[:x, true], [:y, false]].sql_negate).should == '((x IS NOT TRUE) AND (y IS NOT FALSE))'
112
+ @d.l([[:x, nil], [:y, [1,2,3]]].sql_negate).should == '((x IS NOT NULL) AND (y NOT IN (1, 2, 3)))'
113
+ end
114
+
115
+ it "should support ~ on arrays with all two pairs" do
116
+ @d.l(~[[:x, 100],[:y, 'a']]).should == '((x != 100) OR (y != \'a\'))'
117
+ @d.l(~[[:x, true], [:y, false]]).should == '((x IS NOT TRUE) OR (y IS NOT FALSE))'
118
+ @d.l(~[[:x, nil], [:y, [1,2,3]]]).should == '((x IS NOT NULL) OR (y NOT IN (1, 2, 3)))'
119
+ end
120
+
121
+ it "should support sql_or on arrays with all two pairs" do
122
+ @d.l([[:x, 100],[:y, 'a']].sql_or).should == '((x = 100) OR (y = \'a\'))'
123
+ @d.l([[:x, true], [:y, false]].sql_or).should == '((x IS TRUE) OR (y IS FALSE))'
124
+ @d.l([[:x, nil], [:y, [1,2,3]]].sql_or).should == '((x IS NULL) OR (y IN (1, 2, 3)))'
125
+ end
126
+
127
+ it "should support Array#sql_string_join for concatenation of SQL strings" do
128
+ @d.lit([:x].sql_string_join).should == '(x)'
129
+ @d.lit([:x].sql_string_join(', ')).should == '(x)'
130
+ @d.lit([:x, :y].sql_string_join).should == '(x || y)'
131
+ @d.lit([:x, :y].sql_string_join(', ')).should == "(x || ', ' || y)"
132
+ @d.lit([:x.sql_function(1), :y.sql_subscript(1)].sql_string_join).should == '(x(1) || y[1])'
133
+ @d.lit([:x.sql_function(1), 'y.z'.lit].sql_string_join(', ')).should == "(x(1) || ', ' || y.z)"
134
+ @d.lit([:x, 1, :y].sql_string_join).should == "(x || '1' || y)"
135
+ @d.lit([:x, 1, :y].sql_string_join(', ')).should == "(x || ', ' || '1' || ', ' || y)"
136
+ @d.lit([:x, 1, :y].sql_string_join(:y__z)).should == "(x || y.z || '1' || y.z || y)"
137
+ @d.lit([:x, 1, :y].sql_string_join(1)).should == "(x || '1' || '1' || '1' || y)"
138
+ @d.lit([:x, :y].sql_string_join('y.x || x.y'.lit)).should == "(x || y.x || x.y || y)"
139
+ @d.lit([[:x, :y].sql_string_join, [:a, :b].sql_string_join].sql_string_join).should == "(x || y || a || b)"
140
+ end
141
+
142
+ it "should support sql_expr on hashes" do
143
+ @d.l({:x => 100, :y => 'a'}.sql_expr)[1...-1].split(' AND ').sort.should == ['(x = 100)', '(y = \'a\')']
144
+ @d.l({:x => true, :y => false}.sql_expr)[1...-1].split(' AND ').sort.should == ['(x IS TRUE)', '(y IS FALSE)']
145
+ @d.l({:x => nil, :y => [1,2,3]}.sql_expr)[1...-1].split(' AND ').sort.should == ['(x IS NULL)', '(y IN (1, 2, 3))']
146
+ end
147
+
148
+ it "should support sql_negate on hashes" do
149
+ @d.l({:x => 100, :y => 'a'}.sql_negate)[1...-1].split(' AND ').sort.should == ['(x != 100)', '(y != \'a\')']
150
+ @d.l({:x => true, :y => false}.sql_negate)[1...-1].split(' AND ').sort.should == ['(x IS NOT TRUE)', '(y IS NOT FALSE)']
151
+ @d.l({:x => nil, :y => [1,2,3]}.sql_negate)[1...-1].split(' AND ').sort.should == ['(x IS NOT NULL)', '(y NOT IN (1, 2, 3))']
152
+ end
153
+
154
+ it "should support ~ on hashes" do
155
+ @d.l(~{:x => 100, :y => 'a'})[1...-1].split(' OR ').sort.should == ['(x != 100)', '(y != \'a\')']
156
+ @d.l(~{:x => true, :y => false})[1...-1].split(' OR ').sort.should == ['(x IS NOT TRUE)', '(y IS NOT FALSE)']
157
+ @d.l(~{:x => nil, :y => [1,2,3]})[1...-1].split(' OR ').sort.should == ['(x IS NOT NULL)', '(y NOT IN (1, 2, 3))']
158
+ end
159
+
160
+ it "should support sql_or on hashes" do
161
+ @d.l({:x => 100, :y => 'a'}.sql_or)[1...-1].split(' OR ').sort.should == ['(x = 100)', '(y = \'a\')']
162
+ @d.l({:x => true, :y => false}.sql_or)[1...-1].split(' OR ').sort.should == ['(x IS TRUE)', '(y IS FALSE)']
163
+ @d.l({:x => nil, :y => [1,2,3]}.sql_or)[1...-1].split(' OR ').sort.should == ['(x IS NULL)', '(y IN (1, 2, 3))']
164
+ end
165
+
166
+ it "should Hash#& and Hash#|" do
167
+ @d.l({:y => :z} & :x).should == '((y = z) AND x)'
168
+ @d.l({:x => :a} & {:y => :z}).should == '((x = a) AND (y = z))'
169
+ @d.l({:y => :z} | :x).should == '((y = z) OR x)'
170
+ @d.l({:x => :a} | {:y => :z}).should == '((x = a) OR (y = z))'
171
+ end
172
+ end
173
+
174
+ describe "Array#case and Hash#case" do
175
+ before do
176
+ @d = Sequel::Dataset.new(nil)
177
+ end
178
+
179
+ specify "should return SQL CASE expression" do
180
+ @d.literal({:x=>:y}.case(:z)).should == '(CASE WHEN x THEN y ELSE z END)'
181
+ @d.literal({:x=>:y}.case(:z, :exp)).should == '(CASE exp WHEN x THEN y ELSE z END)'
182
+ ['(CASE WHEN x THEN y WHEN a THEN b ELSE z END)',
183
+ '(CASE WHEN a THEN b WHEN x THEN y ELSE z END)'].should(include(@d.literal({:x=>:y, :a=>:b}.case(:z))))
184
+ @d.literal([[:x, :y]].case(:z)).should == '(CASE WHEN x THEN y ELSE z END)'
185
+ @d.literal([[:x, :y], [:a, :b]].case(:z)).should == '(CASE WHEN x THEN y WHEN a THEN b ELSE z END)'
186
+ @d.literal([[:x, :y], [:a, :b]].case(:z, :exp)).should == '(CASE exp WHEN x THEN y WHEN a THEN b ELSE z END)'
187
+ @d.literal([[:x, :y], [:a, :b]].case(:z, :exp__w)).should == '(CASE exp.w WHEN x THEN y WHEN a THEN b ELSE z END)'
188
+ end
189
+
190
+ specify "should return SQL CASE expression with expression even if nil" do
191
+ @d.literal({:x=>:y}.case(:z, nil)).should == '(CASE NULL WHEN x THEN y ELSE z END)'
192
+ end
193
+
194
+ specify "should raise an error if an array that isn't all two pairs is used" do
195
+ proc{[:b].case(:a)}.should raise_error(Sequel::Error)
196
+ proc{[:b, :c].case(:a)}.should raise_error(Sequel::Error)
197
+ proc{[[:b, :c], :d].case(:a)}.should raise_error(Sequel::Error)
198
+ end
199
+
200
+ specify "should raise an error if an empty array/hash is used" do
201
+ proc{[].case(:a)}.should raise_error(Sequel::Error)
202
+ proc{{}.case(:a)}.should raise_error(Sequel::Error)
203
+ end
204
+ end
205
+
206
+ describe "Array#sql_value_list and #sql_array" do
207
+ before do
208
+ @d = Sequel::Dataset.new(nil)
209
+ end
210
+
211
+ specify "should treat the array as an SQL value list instead of conditions when used as a placeholder value" do
212
+ @d.filter("(a, b) IN ?", [[:x, 1], [:y, 2]]).sql.should == 'SELECT * WHERE ((a, b) IN ((x = 1) AND (y = 2)))'
213
+ @d.filter("(a, b) IN ?", [[:x, 1], [:y, 2]].sql_value_list).sql.should == 'SELECT * WHERE ((a, b) IN ((x, 1), (y, 2)))'
214
+ end
215
+
216
+ specify "should be no difference when used as a hash value" do
217
+ @d.filter([:a, :b]=>[[:x, 1], [:y, 2]]).sql.should == 'SELECT * WHERE ((a, b) IN ((x, 1), (y, 2)))'
218
+ @d.filter([:a, :b]=>[[:x, 1], [:y, 2]].sql_value_list).sql.should == 'SELECT * WHERE ((a, b) IN ((x, 1), (y, 2)))'
219
+ end
220
+ end
221
+
222
+ describe "String#lit" do
223
+ before do
224
+ @ds = ds = Sequel::Database.new[:t]
225
+ end
226
+
227
+ specify "should return an LiteralString object" do
228
+ 'xyz'.lit.should be_a_kind_of(Sequel::LiteralString)
229
+ 'xyz'.lit.to_s.should == 'xyz'
230
+ end
231
+
232
+ specify "should inhibit string literalization" do
233
+ @ds.update_sql(:stamp => "NOW()".lit).should == "UPDATE t SET stamp = NOW()"
234
+ end
235
+
236
+ specify "should return a PlaceholderLiteralString object if args are given" do
237
+ a = 'DISTINCT ?'.lit(:a)
238
+ a.should be_a_kind_of(Sequel::SQL::PlaceholderLiteralString)
239
+ @ds.literal(a).should == 'DISTINCT a'
240
+ @ds.quote_identifiers = true
241
+ @ds.literal(a).should == 'DISTINCT "a"'
242
+ end
243
+
244
+ specify "should handle named placeholders if given a single argument hash" do
245
+ a = 'DISTINCT :b'.lit(:b=>:a)
246
+ a.should be_a_kind_of(Sequel::SQL::PlaceholderLiteralString)
247
+ @ds.literal(a).should == 'DISTINCT a'
248
+ @ds.quote_identifiers = true
249
+ @ds.literal(a).should == 'DISTINCT "a"'
250
+ end
251
+
252
+ specify "should treat placeholder literal strings as generic expressions" do
253
+ a = ':b'.lit(:b=>:a)
254
+ @ds.literal(a + 1).should == "(a + 1)"
255
+ @ds.literal(a & :b).should == "(a AND b)"
256
+ @ds.literal(a.sql_string + :b).should == "(a || b)"
257
+ end
258
+ end
259
+
260
+ describe "String#to_sequel_blob" do
261
+ specify "should return a Blob object" do
262
+ 'xyz'.to_sequel_blob.should be_a_kind_of(::Sequel::SQL::Blob)
263
+ 'xyz'.to_sequel_blob.should == 'xyz'
264
+ end
265
+
266
+ specify "should retain binary data" do
267
+ "\1\2\3\4".to_sequel_blob.should == "\1\2\3\4"
268
+ end
269
+ end
270
+
271
+ describe "#desc" do
272
+ before do
273
+ @ds = Sequel::Dataset.new(nil)
274
+ end
275
+
276
+ specify "should format a DESC clause for a column ref" do
277
+ :test.desc.to_s(@ds).should == 'test DESC'
278
+
279
+ :items__price.desc.to_s(@ds).should == 'items.price DESC'
280
+ end
281
+
282
+ specify "should format a DESC clause for a function" do
283
+ :avg.sql_function(:test).desc.to_s(@ds).should == 'avg(test) DESC'
284
+ end
285
+ end
286
+
287
+ describe "#asc" do
288
+ before do
289
+ @ds = Sequel::Dataset.new(nil)
290
+ end
291
+
292
+ specify "should format a ASC clause for a column ref" do
293
+ :test.asc.to_s(@ds).should == 'test ASC'
294
+
295
+ :items__price.asc.to_s(@ds).should == 'items.price ASC'
296
+ end
297
+
298
+ specify "should format a ASC clause for a function" do
299
+ :avg.sql_function(:test).asc.to_s(@ds).should == 'avg(test) ASC'
300
+ end
301
+ end
302
+
303
+ describe "#as" do
304
+ before do
305
+ @ds = Sequel::Dataset.new(nil)
306
+ end
307
+
308
+ specify "should format a AS clause for a column ref" do
309
+ :test.as(:t).to_s(@ds).should == 'test AS t'
310
+
311
+ :items__price.as(:p).to_s(@ds).should == 'items.price AS p'
312
+ end
313
+
314
+ specify "should format a AS clause for a function" do
315
+ :avg.sql_function(:test).as(:avg).to_s(@ds).should == 'avg(test) AS avg'
316
+ end
317
+
318
+ specify "should format a AS clause for a literal value" do
319
+ 'abc'.as(:abc).to_s(@ds).should == "'abc' AS abc"
320
+ end
321
+ end
322
+
323
+ describe "Column references" do
324
+ before do
325
+ @ds = Sequel::Database.new.dataset
326
+ def @ds.quoted_identifier_append(sql, c)
327
+ sql << "`#{c}`"
328
+ end
329
+ @ds.quote_identifiers = true
330
+ end
331
+
332
+ specify "should be quoted properly" do
333
+ @ds.literal(:xyz).should == "`xyz`"
334
+ @ds.literal(:xyz__abc).should == "`xyz`.`abc`"
335
+
336
+ @ds.literal(:xyz.as(:x)).should == "`xyz` AS `x`"
337
+ @ds.literal(:xyz__abc.as(:x)).should == "`xyz`.`abc` AS `x`"
338
+
339
+ @ds.literal(:xyz___x).should == "`xyz` AS `x`"
340
+ @ds.literal(:xyz__abc___x).should == "`xyz`.`abc` AS `x`"
341
+ end
342
+
343
+ specify "should be quoted properly in SQL functions" do
344
+ @ds.literal(:avg.sql_function(:xyz)).should == "avg(`xyz`)"
345
+ @ds.literal(:avg.sql_function(:xyz, 1)).should == "avg(`xyz`, 1)"
346
+ @ds.literal(:avg.sql_function(:xyz).as(:a)).should == "avg(`xyz`) AS `a`"
347
+ end
348
+
349
+ specify "should be quoted properly in ASC/DESC clauses" do
350
+ @ds.literal(:xyz.asc).should == "`xyz` ASC"
351
+ @ds.literal(:avg.sql_function(:xyz, 1).desc).should == "avg(`xyz`, 1) DESC"
352
+ end
353
+
354
+ specify "should be quoted properly in a cast function" do
355
+ @ds.literal(:x.cast(:integer)).should == "CAST(`x` AS integer)"
356
+ @ds.literal(:x__y.cast('varchar(20)')).should == "CAST(`x`.`y` AS varchar(20))"
357
+ end
358
+ end
359
+
360
+ describe "Blob" do
361
+ specify "#to_sequel_blob should return self" do
362
+ blob = "x".to_sequel_blob
363
+ blob.to_sequel_blob.object_id.should == blob.object_id
364
+ end
365
+ end
366
+
367
+ if RUBY_VERSION < '1.9.0'
368
+ describe "Symbol#[]" do
369
+ specify "should format an SQL Function" do
370
+ ds = Sequel::Dataset.new(nil)
371
+ ds.literal(:xyz[]).should == 'xyz()'
372
+ ds.literal(:xyz[1]).should == 'xyz(1)'
373
+ ds.literal(:xyz[1, 2, :abc[3]]).should == 'xyz(1, 2, abc(3))'
374
+ end
375
+ end
376
+ end
377
+
378
+ describe "Symbol#*" do
379
+ before do
380
+ @ds = Sequel::Dataset.new(nil)
381
+ end
382
+
383
+ specify "should format a qualified wildcard if no argument" do
384
+ :xyz.*.to_s(@ds).should == 'xyz.*'
385
+ :abc.*.to_s(@ds).should == 'abc.*'
386
+ end
387
+
388
+ specify "should format a filter expression if an argument" do
389
+ :xyz.*(3).to_s(@ds).should == '(xyz * 3)'
390
+ :abc.*(5).to_s(@ds).should == '(abc * 5)'
391
+ end
392
+
393
+ specify "should support qualified symbols if no argument" do
394
+ :xyz__abc.*.to_s(@ds).should == 'xyz.abc.*'
395
+ end
396
+ end
397
+
398
+ describe "Symbol" do
399
+ before do
400
+ @ds = Sequel::Dataset.new(nil)
401
+ @ds.quote_identifiers = true
402
+ @ds.identifier_input_method = :upcase
403
+ end
404
+
405
+ specify "#identifier should format an identifier" do
406
+ @ds.literal(:xyz__abc.identifier).should == '"XYZ__ABC"'
407
+ end
408
+
409
+ specify "#qualify should format a qualified column" do
410
+ @ds.literal(:xyz.qualify(:abc)).should == '"ABC"."XYZ"'
411
+ end
412
+
413
+ specify "#qualify should work on QualifiedIdentifiers" do
414
+ @ds.literal(:xyz.qualify(:abc).qualify(:def)).should == '"DEF"."ABC"."XYZ"'
415
+ end
416
+
417
+ specify "should be able to qualify an identifier" do
418
+ @ds.literal(:xyz.identifier.qualify(:xyz__abc)).should == '"XYZ"."ABC"."XYZ"'
419
+ end
420
+
421
+ specify "should be able to specify a schema.table.column" do
422
+ @ds.literal(:column.qualify(:table.qualify(:schema))).should == '"SCHEMA"."TABLE"."COLUMN"'
423
+ @ds.literal(:column.qualify(:table__name.identifier.qualify(:schema))).should == '"SCHEMA"."TABLE__NAME"."COLUMN"'
424
+ end
425
+
426
+ specify "should be able to specify order" do
427
+ @oe = :xyz.desc
428
+ @oe.class.should == Sequel::SQL::OrderedExpression
429
+ @oe.descending.should == true
430
+ @oe = :xyz.asc
431
+ @oe.class.should == Sequel::SQL::OrderedExpression
432
+ @oe.descending.should == false
433
+ end
434
+
435
+ specify "should work correctly with objects" do
436
+ o = Object.new
437
+ def o.sql_literal(ds) "(foo)" end
438
+ @ds.literal(:column.qualify(o)).should == '(foo)."COLUMN"'
439
+ end
440
+ end
441
+
442
+ describe "Symbol" do
443
+ before do
444
+ @ds = Sequel::Database.new.dataset
445
+ end
446
+
447
+ specify "should support sql_function method" do
448
+ :COUNT.sql_function('1').to_s(@ds).should == "COUNT('1')"
449
+ @ds.select(:COUNT.sql_function('1')).sql.should == "SELECT COUNT('1')"
450
+ end
451
+
452
+ specify "should support cast method" do
453
+ :abc.cast(:integer).to_s(@ds).should == "CAST(abc AS integer)"
454
+ end
455
+
456
+ specify "should support sql array accesses via sql_subscript" do
457
+ @ds.literal(:abc.sql_subscript(1)).should == "abc[1]"
458
+ @ds.literal(:abc__def.sql_subscript(1)).should == "abc.def[1]"
459
+ @ds.literal(:abc.sql_subscript(1)|2).should == "abc[1, 2]"
460
+ @ds.literal(:abc.sql_subscript(1)[2]).should == "abc[1][2]"
461
+ end
462
+
463
+ specify "should support cast_numeric and cast_string" do
464
+ x = :abc.cast_numeric
465
+ x.should be_a_kind_of(Sequel::SQL::NumericExpression)
466
+ x.to_s(@ds).should == "CAST(abc AS integer)"
467
+
468
+ x = :abc.cast_numeric(:real)
469
+ x.should be_a_kind_of(Sequel::SQL::NumericExpression)
470
+ x.to_s(@ds).should == "CAST(abc AS real)"
471
+
472
+ x = :abc.cast_string
473
+ x.should be_a_kind_of(Sequel::SQL::StringExpression)
474
+ x.to_s(@ds).should == "CAST(abc AS varchar(255))"
475
+
476
+ x = :abc.cast_string(:varchar)
477
+ x.should be_a_kind_of(Sequel::SQL::StringExpression)
478
+ x.to_s(@ds).should == "CAST(abc AS varchar(255))"
479
+ end
480
+
481
+ specify "should allow database independent types when casting" do
482
+ db = @ds.db
483
+ def db.cast_type_literal(type)
484
+ return :foo if type == Integer
485
+ return :bar if type == String
486
+ type
487
+ end
488
+ :abc.cast(String).to_s(@ds).should == "CAST(abc AS bar)"
489
+ :abc.cast(String).to_s(@ds).should == "CAST(abc AS bar)"
490
+ :abc.cast_string.to_s(@ds).should == "CAST(abc AS bar)"
491
+ :abc.cast_string(Integer).to_s(@ds).should == "CAST(abc AS foo)"
492
+ :abc.cast_numeric.to_s(@ds).should == "CAST(abc AS foo)"
493
+ :abc.cast_numeric(String).to_s(@ds).should == "CAST(abc AS bar)"
494
+ end
495
+
496
+ specify "should support SQL EXTRACT function via #extract " do
497
+ :abc.extract(:year).to_s(@ds).should == "extract(year FROM abc)"
498
+ end
499
+ end
500
+
501
+ describe "Postgres extensions integration" do
502
+ before do
503
+ @db = Sequel.mock
504
+ Sequel.extension(:pg_array, :pg_array_ops, :pg_hstore, :pg_hstore_ops, :pg_json, :pg_range, :pg_range_ops, :pg_row, :pg_row_ops)
505
+ end
506
+
507
+ it "Symbol#pg_array should return an ArrayOp" do
508
+ @db.literal(:a.pg_array.unnest).should == "unnest(a)"
509
+ end
510
+
511
+ it "Symbol#pg_row should return a PGRowOp" do
512
+ @db.literal(:a.pg_row[:a]).should == "(a).a"
513
+ end
514
+
515
+ it "Symbol#hstore should return an HStoreOp" do
516
+ @db.literal(:a.hstore['a']).should == "(a -> 'a')"
517
+ end
518
+
519
+ it "Symbol#pg_range should return a RangeOp" do
520
+ @db.literal(:a.pg_range.lower).should == "lower(a)"
521
+ end
522
+
523
+ it "Array#pg_array should return a PGArray" do
524
+ @db.literal([1].pg_array.op.unnest).should == "unnest(ARRAY[1])"
525
+ @db.literal([1].pg_array(:int4).op.unnest).should == "unnest(ARRAY[1]::int4[])"
526
+ end
527
+
528
+ it "Array#pg_json should return a JSONArray" do
529
+ @db.literal([1].pg_json).should == "'[1]'::json"
530
+ end
531
+
532
+ it "Array#pg_row should return a ArrayRow" do
533
+ @db.literal([1].pg_row).should == "ROW(1)"
534
+ end
535
+
536
+ it "Hash#hstore should return an HStore" do
537
+ @db.literal({'a'=>1}.hstore.op['a']).should == '(\'"a"=>"1"\'::hstore -> \'a\')'
538
+ end
539
+
540
+ it "Hash#pg_json should return an JSONHash" do
541
+ @db.literal({'a'=>'b'}.pg_json).should == "'{\"a\":\"b\"}'::json"
542
+ end
543
+
544
+ it "Range#pg_range should return an PGRange" do
545
+ @db.literal((1..2).pg_range).should == "'[1,2]'"
546
+ @db.literal((1..2).pg_range(:int4range)).should == "'[1,2]'::int4range"
547
+ end
548
+ end
549
+ else
550
+ skip_warn "core_refinements extension: only works on ruby 2.0+"
551
+ end