sequel_impala 1.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 (129) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +50 -0
  3. data/LICENSE +463 -0
  4. data/README.md +45 -0
  5. data/Rakefile +39 -0
  6. data/lib/driver/commons-collections-3.2.1.jar +0 -0
  7. data/lib/driver/commons-configuration-1.10.jar +0 -0
  8. data/lib/driver/commons-logging-1.2.jar +0 -0
  9. data/lib/driver/hadoop-auth-2.9.0.jar +0 -0
  10. data/lib/driver/hadoop-common-2.9.0.jar +0 -0
  11. data/lib/driver/hadoop-core-2.6.0.jar +0 -0
  12. data/lib/driver/hive-exec-1.1.0.jar +0 -0
  13. data/lib/driver/hive-jdbc-1.1.0.jar +0 -0
  14. data/lib/driver/hive-metastore-1.1.0.jar +0 -0
  15. data/lib/driver/hive-service-1.1.0.jar +0 -0
  16. data/lib/driver/httpclient-4.3.jar +0 -0
  17. data/lib/driver/httpcore-4.3.jar +0 -0
  18. data/lib/driver/libfb303-0.9.0.jar +0 -0
  19. data/lib/driver/log4j-1.2.17.jar +0 -0
  20. data/lib/driver/slf4j-api-1.7.5.jar +0 -0
  21. data/lib/driver/stax2-api-3.1.4.jar +0 -0
  22. data/lib/driver/woodstox-core-asl-4.4.1.jar +0 -0
  23. data/lib/impala.rb +55 -0
  24. data/lib/impala/connection.rb +180 -0
  25. data/lib/impala/cursor.rb +200 -0
  26. data/lib/impala/progress_reporter.rb +40 -0
  27. data/lib/impala/protocol.rb +8 -0
  28. data/lib/impala/protocol/beeswax_constants.rb +15 -0
  29. data/lib/impala/protocol/beeswax_service.rb +747 -0
  30. data/lib/impala/protocol/beeswax_types.rb +193 -0
  31. data/lib/impala/protocol/exec_stats_constants.rb +13 -0
  32. data/lib/impala/protocol/exec_stats_types.rb +133 -0
  33. data/lib/impala/protocol/facebook_service.rb +706 -0
  34. data/lib/impala/protocol/fb303_constants.rb +15 -0
  35. data/lib/impala/protocol/fb303_types.rb +25 -0
  36. data/lib/impala/protocol/hive_metastore_constants.rb +53 -0
  37. data/lib/impala/protocol/hive_metastore_types.rb +698 -0
  38. data/lib/impala/protocol/impala_hive_server2_service.rb +137 -0
  39. data/lib/impala/protocol/impala_service.rb +443 -0
  40. data/lib/impala/protocol/impala_service_constants.rb +13 -0
  41. data/lib/impala/protocol/impala_service_types.rb +192 -0
  42. data/lib/impala/protocol/status_constants.rb +13 -0
  43. data/lib/impala/protocol/status_types.rb +46 -0
  44. data/lib/impala/protocol/t_c_l_i_service.rb +1108 -0
  45. data/lib/impala/protocol/t_c_l_i_service_constants.rb +72 -0
  46. data/lib/impala/protocol/t_c_l_i_service_types.rb +1802 -0
  47. data/lib/impala/protocol/thrift_hive_metastore.rb +4707 -0
  48. data/lib/impala/protocol/types_constants.rb +13 -0
  49. data/lib/impala/protocol/types_types.rb +332 -0
  50. data/lib/impala/sasl_transport.rb +117 -0
  51. data/lib/impala/thrift_patch.rb +31 -0
  52. data/lib/impala/version.rb +3 -0
  53. data/lib/jdbc/hive2.rb +52 -0
  54. data/lib/jdbc/impala.rb +50 -0
  55. data/lib/rbhive.rb +8 -0
  56. data/lib/rbhive/connection.rb +150 -0
  57. data/lib/rbhive/explain_result.rb +46 -0
  58. data/lib/rbhive/result_set.rb +37 -0
  59. data/lib/rbhive/schema_definition.rb +86 -0
  60. data/lib/rbhive/t_c_l_i_connection.rb +466 -0
  61. data/lib/rbhive/t_c_l_i_result_set.rb +3 -0
  62. data/lib/rbhive/t_c_l_i_schema_definition.rb +87 -0
  63. data/lib/rbhive/table_schema.rb +122 -0
  64. data/lib/rbhive/version.rb +3 -0
  65. data/lib/sequel/adapters/impala.rb +220 -0
  66. data/lib/sequel/adapters/jdbc/hive2.rb +36 -0
  67. data/lib/sequel/adapters/jdbc/impala.rb +38 -0
  68. data/lib/sequel/adapters/rbhive.rb +177 -0
  69. data/lib/sequel/adapters/shared/impala.rb +808 -0
  70. data/lib/sequel/extensions/csv_to_parquet.rb +166 -0
  71. data/lib/thrift/facebook_service.rb +700 -0
  72. data/lib/thrift/fb303_constants.rb +9 -0
  73. data/lib/thrift/fb303_types.rb +19 -0
  74. data/lib/thrift/hive_metastore_constants.rb +41 -0
  75. data/lib/thrift/hive_metastore_types.rb +630 -0
  76. data/lib/thrift/hive_service_constants.rb +13 -0
  77. data/lib/thrift/hive_service_types.rb +72 -0
  78. data/lib/thrift/queryplan_constants.rb +13 -0
  79. data/lib/thrift/queryplan_types.rb +261 -0
  80. data/lib/thrift/sasl_client_transport.rb +161 -0
  81. data/lib/thrift/serde_constants.rb +92 -0
  82. data/lib/thrift/serde_types.rb +7 -0
  83. data/lib/thrift/t_c_l_i_service.rb +1054 -0
  84. data/lib/thrift/t_c_l_i_service_constants.rb +72 -0
  85. data/lib/thrift/t_c_l_i_service_types.rb +1768 -0
  86. data/lib/thrift/thrift_hive.rb +508 -0
  87. data/lib/thrift/thrift_hive_metastore.rb +3856 -0
  88. data/spec/database_test.rb +56 -0
  89. data/spec/dataset_test.rb +1268 -0
  90. data/spec/files/bad_down_migration/001_create_alt_basic.rb +4 -0
  91. data/spec/files/bad_down_migration/002_create_alt_advanced.rb +4 -0
  92. data/spec/files/bad_timestamped_migrations/1273253849_create_sessions.rb +9 -0
  93. data/spec/files/bad_timestamped_migrations/1273253851_create_nodes.rb +9 -0
  94. data/spec/files/bad_timestamped_migrations/1273253853_3_create_users.rb +3 -0
  95. data/spec/files/bad_up_migration/001_create_alt_basic.rb +4 -0
  96. data/spec/files/bad_up_migration/002_create_alt_advanced.rb +3 -0
  97. data/spec/files/convert_to_timestamp_migrations/001_create_sessions.rb +9 -0
  98. data/spec/files/convert_to_timestamp_migrations/002_create_nodes.rb +9 -0
  99. data/spec/files/convert_to_timestamp_migrations/003_3_create_users.rb +4 -0
  100. data/spec/files/convert_to_timestamp_migrations/1273253850_create_artists.rb +9 -0
  101. data/spec/files/convert_to_timestamp_migrations/1273253852_create_albums.rb +9 -0
  102. data/spec/files/duplicate_timestamped_migrations/1273253849_create_sessions.rb +9 -0
  103. data/spec/files/duplicate_timestamped_migrations/1273253853_create_nodes.rb +9 -0
  104. data/spec/files/duplicate_timestamped_migrations/1273253853_create_users.rb +4 -0
  105. data/spec/files/integer_migrations/001_create_sessions.rb +9 -0
  106. data/spec/files/integer_migrations/002_create_nodes.rb +9 -0
  107. data/spec/files/integer_migrations/003_3_create_users.rb +4 -0
  108. data/spec/files/interleaved_timestamped_migrations/1273253849_create_sessions.rb +9 -0
  109. data/spec/files/interleaved_timestamped_migrations/1273253850_create_artists.rb +9 -0
  110. data/spec/files/interleaved_timestamped_migrations/1273253851_create_nodes.rb +9 -0
  111. data/spec/files/interleaved_timestamped_migrations/1273253852_create_albums.rb +9 -0
  112. data/spec/files/interleaved_timestamped_migrations/1273253853_3_create_users.rb +4 -0
  113. data/spec/files/reversible_migrations/001_reversible.rb +5 -0
  114. data/spec/files/reversible_migrations/002_reversible.rb +5 -0
  115. data/spec/files/reversible_migrations/003_reversible.rb +5 -0
  116. data/spec/files/reversible_migrations/004_reversible.rb +5 -0
  117. data/spec/files/reversible_migrations/005_reversible.rb +10 -0
  118. data/spec/files/timestamped_migrations/1273253849_create_sessions.rb +9 -0
  119. data/spec/files/timestamped_migrations/1273253851_create_nodes.rb +9 -0
  120. data/spec/files/timestamped_migrations/1273253853_3_create_users.rb +4 -0
  121. data/spec/impala_test.rb +290 -0
  122. data/spec/migrator_test.rb +240 -0
  123. data/spec/plugin_test.rb +91 -0
  124. data/spec/prepared_statement_test.rb +327 -0
  125. data/spec/schema_test.rb +356 -0
  126. data/spec/spec_helper.rb +19 -0
  127. data/spec/timezone_test.rb +86 -0
  128. data/spec/type_test.rb +99 -0
  129. metadata +294 -0
@@ -0,0 +1,56 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper.rb')
2
+
3
+ describe Sequel::Database do
4
+ before do
5
+ @db = DB
6
+ end
7
+
8
+ it "should provide disconnect functionality" do
9
+ @db.disconnect
10
+ @db.pool.size.must_equal 0
11
+ @db.test_connection
12
+ @db.pool.size.must_equal 1
13
+ end
14
+
15
+ it "should provide disconnect functionality after preparing a statement" do
16
+ @db.create_table!(:items){Integer :i}
17
+ @db[:items].prepare(:first, :a).call
18
+ @db.disconnect
19
+ @db.pool.size.must_equal 0
20
+ @db.drop_table?(:items)
21
+ end
22
+
23
+ it "should raise Sequel::DatabaseError on invalid SQL" do
24
+ proc{@db << "S"}.must_raise(Sequel::DatabaseError)
25
+ end
26
+
27
+ it "should store underlying wrapped exception in Sequel::DatabaseError" do
28
+ begin
29
+ @db << "SELECT"
30
+ rescue Sequel::DatabaseError=>e
31
+ if defined?(Java::JavaLang::Exception)
32
+ (e.wrapped_exception.is_a?(Exception) || e.wrapped_exception.is_a?(Java::JavaLang::Exception)).must_equal true
33
+ else
34
+ e.wrapped_exception.must_be_kind_of(Exception)
35
+ end
36
+ end
37
+ end
38
+
39
+ it "should not have the connection pool swallow non-StandardError based exceptions" do
40
+ proc{@db.pool.hold{raise Interrupt, "test"}}.must_raise(Interrupt)
41
+ end
42
+
43
+ it "should be able to disconnect connections more than once without exceptions" do
44
+ conn = @db.synchronize{|c| c}
45
+ @db.disconnect
46
+ @db.disconnect_connection(conn)
47
+ @db.disconnect_connection(conn)
48
+ end
49
+
50
+ it "should provide ability to check connections for validity" do
51
+ conn = @db.synchronize{|c| c}
52
+ @db.valid_connection?(conn).must_equal true
53
+ @db.disconnect
54
+ @db.valid_connection?(conn).must_equal false
55
+ end
56
+ end
@@ -0,0 +1,1268 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper.rb')
2
+
3
+ describe "Simple Dataset operations" do
4
+ before(:all) do
5
+ @db = DB
6
+ @ds = @db[:items]
7
+ end
8
+ after do
9
+ @db.drop_table?(:items)
10
+ end
11
+
12
+ it "should support sequential primary keys with a Bignum" do
13
+ @db.create_table!(:items) do
14
+ primary_key :id, :type=>Bignum
15
+ Integer :number
16
+ end
17
+ @ds << {:id=>1, :number=>20}
18
+ @ds << {:id=>2, :number=>30}
19
+ @ds.order(:number).all.must_equal [{:id => 1, :number=>20}, {:id => 2, :number=>30}]
20
+ end
21
+
22
+ it "should have insert work correctly with static SQL" do
23
+ @db.create_table!(:items) do
24
+ primary_key :id
25
+ Integer :number
26
+ end
27
+ @db["INSERT INTO #{@ds.literal(:items)} (id, number) VALUES (2, 30)"].insert
28
+ @ds.all.must_equal [{:id => 2, :number=>30}]
29
+ end
30
+
31
+ it "should have insert work correctly when inserting a row with all NULL values" do
32
+ @db.create_table!(:items) do
33
+ Integer :id
34
+ Integer :number
35
+ end
36
+ @ds.insert
37
+ @ds.all.must_equal [{:id=>nil, :number=>nil}]
38
+ end
39
+
40
+ it "should support iterating over large numbers of records with paged_each" do
41
+ @db.create_table!(:items) do
42
+ Integer :id
43
+ Integer :number
44
+ end
45
+ @ds.import([:id, :number], (1..10).map{|i| [i, i*10]})
46
+
47
+ [:offset, :filter].each do |strategy|
48
+ rows = []
49
+ @ds.order(:number).paged_each(:rows_per_fetch=>5, :strategy=>strategy){|row| rows << row}
50
+ rows.must_equal((1..10).map{|i| {:id=>i, :number=>i*10}})
51
+
52
+ rows = []
53
+ @ds.order(:number).paged_each(:rows_per_fetch=>3, :strategy=>strategy){|row| rows << row}
54
+ rows.must_equal((1..10).map{|i| {:id=>i, :number=>i*10}})
55
+
56
+ rows = []
57
+ @ds.order(:number, :id).paged_each(:rows_per_fetch=>5, :strategy=>strategy){|row| rows << row}
58
+ rows.must_equal((1..10).map{|i| {:id=>i, :number=>i*10}})
59
+
60
+ rows = []
61
+ @ds.reverse_order(:number).paged_each(:rows_per_fetch=>5, :strategy=>strategy){|row| rows << row}
62
+ rows.must_equal((1..10).map{|i| {:id=>i, :number=>i*10}}.reverse)
63
+
64
+ rows = []
65
+ @ds.order(Sequel.desc(:number), :id).paged_each(:rows_per_fetch=>5, :strategy=>strategy){|row| rows << row}
66
+ rows.must_equal((1..10).map{|i| {:id=>i, :number=>i*10}}.reverse)
67
+ end
68
+
69
+ rows = []
70
+ @ds.order(:number).limit(5, 2).paged_each(:rows_per_fetch=>3){|row| rows << row}
71
+ rows.must_equal((3..7).map{|i| {:id=>i, :number=>i*10}})
72
+
73
+ rows = []
74
+ @ds.order(Sequel.*(:number, 2)).paged_each(:rows_per_fetch=>5){|row| rows << row}
75
+ rows.must_equal((1..10).map{|i| {:id=>i, :number=>i*10}})
76
+
77
+ rows = []
78
+ @ds.order(Sequel.*(:number, 2)).paged_each(:rows_per_fetch=>5, :strategy=>:filter, :filter_values=>proc{|row, _| [row[:number] * 2]}){|row| rows << row}
79
+ rows.must_equal((1..10).map{|i| {:id=>i, :number=>i*10}})
80
+
81
+ if RUBY_ENGINE == 'jruby'
82
+ # check retrival with varying fetch sizes
83
+ array = (1..10).to_a
84
+ [1, 2, 5, 10, 20].each do |i|
85
+ @ds.with_fetch_size(i).select_order_map(:id).must_equal array
86
+ end
87
+ end
88
+ end
89
+
90
+ it "should fetch correctly with a limit and offset for different combinations of from and join tables" do
91
+ @db.create_table!(:items) do
92
+ Integer :id
93
+ Integer :number
94
+ end
95
+ @ds.insert(:id=>1, :number=>10)
96
+ @db.create_table!(:items2){primary_key :id2; Integer :number2}
97
+ @db[:items2].insert(:id2=>1, :number2=>10)
98
+ @ds.from(:items, :items2).order(:id).limit(2, 0).all.must_equal [{:id=>1, :number=>10, :id2=>1, :number2=>10}]
99
+ @ds.from(:items___i, :items2___i2).order(:id).limit(2, 0).all.must_equal [{:id=>1, :number=>10, :id2=>1, :number2=>10}]
100
+ @ds.cross_join(:items2).order(:id).limit(2, 0).all.must_equal [{:id=>1, :number=>10, :id2=>1, :number2=>10}]
101
+ @ds.from(:items___i).cross_join(:items2___i2).order(:id).limit(2, 0).all.must_equal [{:id=>1, :number=>10, :id2=>1, :number2=>10}]
102
+ @ds.cross_join(:items2___i).cross_join(@db[:items2].select(:id2___id3, :number2___number3)).order(:id).limit(2, 0).all.must_equal [{:id=>1, :number=>10, :id2=>1, :number2=>10, :id3=>1, :number3=>10}]
103
+
104
+ @ds.from(:items, :items2).order(:id).limit(2, 1).all.must_equal []
105
+ @ds.from(:items___i, :items2___i2).order(:id).limit(2, 1).all.must_equal []
106
+ @ds.cross_join(:items2).order(:id).limit(2, 1).all.must_equal []
107
+ @ds.from(:items___i).cross_join(:items2___i2).order(:id).limit(2, 1).all.must_equal []
108
+ @ds.cross_join(:items2___i).cross_join(@db[:items2].select(:id2___id3, :number2___number3)).order(:id).limit(2, 1).all.must_equal []
109
+ @db.drop_table(:items2)
110
+ end
111
+
112
+ end
113
+
114
+ describe "Simple Dataset operations" do
115
+ before(:all) do
116
+ @db = DB
117
+ @db.create_table!(:items) do
118
+ primary_key :id
119
+ Integer :number
120
+ end
121
+ @ds = @db[:items]
122
+ @ds.insert(:id=>1, :number=>10)
123
+ end
124
+ after(:all) do
125
+ @db.drop_table?(:items)
126
+ end
127
+
128
+ it "should join correctly" do
129
+ @ds.join(:items___b, :id=>:id).select_all(:items).all.must_equal [{:id=>1, :number=>10}]
130
+ @ds.join(:items___b, [:id]).select_all(:items).all.must_equal [{:id=>1, :number=>10}]
131
+ end
132
+
133
+ it "should correctly handle subqueries" do
134
+ @ds.from_self(:alias=>:a).all.must_equal [{:id=>1, :number=>10}]
135
+ @ds.join(@ds.as(:a), :id=>:id).select_all(:a).all.must_equal [{:id=>1, :number=>10}]
136
+ end
137
+
138
+ it "should graph correctly" do
139
+ a = [{:items=>{:id=>1, :number=>10}, :b=>{:id=>1, :number=>10}}]
140
+ pr = proc{|t| @ds.graph(t, {:id=>:id}, :table_alias=>:b).extension(:graph_each).all.must_equal a}
141
+ pr[:items]
142
+ pr[:items___foo]
143
+ pr[Sequel.identifier(:items)]
144
+ pr[Sequel.identifier('items')]
145
+ pr[Sequel.as(:items, :foo)]
146
+ pr[Sequel.as(Sequel.identifier('items'), 'foo')]
147
+ end
148
+
149
+ it "should graph correctly with a subselect" do
150
+ @ds.from_self(:alias=>:items).graph(@ds.from_self, {:id=>:id}, :table_alias=>:b).extension(:graph_each).all.must_equal [{:items=>{:id=>1, :number=>10}, :b=>{:id=>1, :number=>10}}]
151
+ end
152
+
153
+ it "should iterate over records as they come in" do
154
+ called = false
155
+ @ds.each{|row| called = true; row.must_equal(:id=>1, :number=>10)}
156
+ called.must_equal true
157
+ end
158
+
159
+ it "should fetch all results correctly" do
160
+ @ds.all.must_equal [{:id=>1, :number=>10}]
161
+ end
162
+
163
+ it "should fetch a single row correctly" do
164
+ @ds.first.must_equal(:id=>1, :number=>10)
165
+ @ds.single_record.must_equal(:id=>1, :number=>10)
166
+ @ds.single_record!.must_equal(:id=>1, :number=>10)
167
+ end
168
+
169
+ it "should work correctly when returning from each without iterating over the whole result set" do
170
+ @ds.order(:id).each{|v| break v}.must_equal(:id=>1, :number=>10)
171
+ @ds.reverse(:id).each{|v| break v}.must_equal(:id=>1, :number=>10)
172
+ end
173
+
174
+ it "should fetch a single value correctly" do
175
+ @ds.get(:id).must_equal 1
176
+ @ds.select(:id).single_value.must_equal 1
177
+ @ds.select(:id).single_value!.must_equal 1
178
+ end
179
+
180
+ it "should have distinct work with limit" do
181
+ @ds.limit(1).distinct.all.must_equal [{:id=>1, :number=>10}]
182
+ end
183
+
184
+ it "should fetch correctly with a limit" do
185
+ @ds.order(:id).limit(1).all.must_equal [{:id=>1, :number=>10}]
186
+ end
187
+
188
+ it "should fetch correctly with a limit and offset" do
189
+ @ds.order(:id).limit(1, 0).all.must_equal [{:id=>1, :number=>10}]
190
+ @ds.order(:id).limit(1, 1).all.must_equal []
191
+ end
192
+
193
+ it "should fetch correctly with just offset" do
194
+ @ds.order(:id).offset(0).all.must_equal [{:id=>1, :number=>10}]
195
+ @ds.order(:id).offset(1).all.must_equal []
196
+ end
197
+
198
+ it "should fetch correctly with a limit and offset using seperate methods" do
199
+ @ds.order(:id).limit(1).offset(0).all.must_equal [{:id=>1, :number=>10}]
200
+ @ds.order(:id).limit(1).offset(1).all.must_equal []
201
+ end
202
+
203
+ it "should provide correct columns when using a limit and offset" do
204
+ ds = @ds.order(:id).limit(1, 1)
205
+ ds.all
206
+ ds.columns.must_equal [:id, :number]
207
+ @ds.order(:id).limit(1, 1).columns.must_equal [:id, :number]
208
+ end
209
+
210
+ it "should fetch correctly with a limit and offset without an order" do
211
+ ds = @ds.order(1)
212
+ ds.limit(2, 1).all.must_equal []
213
+ ds.join(:items___i, :id=>:id).select(:items__id___s, :i__id___id2).limit(2, 1).all.must_equal []
214
+ ds.join(:items___i, :id=>:id).select(:items__id).limit(2, 1).all.must_equal []
215
+ ds.join(:items___i, :id=>:id).select(Sequel.qualify(:items, :id)).limit(2, 1).all.must_equal []
216
+ ds.join(:items___i, :id=>:id).select(Sequel.qualify(:items, :id).as(:s)).limit(2, 1).all.must_equal []
217
+ end
218
+
219
+ it "should be orderable by column number" do
220
+ @ds.order(2, 1).select_map([:id, :number]).must_equal [[1, 10]]
221
+ end
222
+
223
+ it "should fetch correctly with a limit in an IN subselect" do
224
+ @ds.where(:id=>@ds.select(:id).order(:id).limit(1)).all.must_equal [{:id=>1, :number=>10}]
225
+ end
226
+
227
+ it "should fetch correctly with a limit and offset in an IN subselect" do
228
+ @ds.where(:id=>@ds.select(:id).order(:id).limit(2, 0)).all.must_equal [{:id=>1, :number=>10}]
229
+ @ds.where(:id=>@ds.select(:id).order(:id).limit(2, 1)).all.must_equal []
230
+ end
231
+
232
+ it "should fetch correctly when using limit and offset in a from_self" do
233
+ ds = @ds.order(:id).limit(1, 1).from_self
234
+ ds.all.must_equal []
235
+ ds.columns.must_equal [:id, :number]
236
+ @ds.order(:id).limit(1, 1).columns.must_equal [:id, :number]
237
+ end
238
+
239
+ it "should fetch correctly when using nested limit and offset in a from_self" do
240
+ ds = @ds.order(:id).limit(1, 0).from_self.reverse_order(:number).limit(1, 0)
241
+ ds.all.must_equal [{:number=>10, :id=>1}]
242
+ ds.columns.must_equal [:id, :number]
243
+ @ds.order(:id).limit(1, 0).from_self.reverse_order(:number).limit(1, 0).columns.must_equal [:id, :number]
244
+ end
245
+
246
+ it "should alias columns correctly" do
247
+ @ds.select(:id___x, :number___n).first.must_equal(:x=>1, :n=>10)
248
+ end
249
+
250
+ it "should handle true/false properly" do
251
+ @ds.filter(Sequel::TRUE).select_map(:number).must_equal [10]
252
+ @ds.filter(Sequel::FALSE).select_map(:number).must_equal []
253
+ @ds.filter(true).select_map(:number).must_equal [10]
254
+ @ds.filter(false).select_map(:number).must_equal []
255
+ @ds.filter({:id=>1}=>true).select_map(:number).must_equal [10]
256
+ @ds.filter({:id=>1}=>false).select_map(:number).must_equal []
257
+ @ds.literal(true)
258
+ @ds.literal(false)
259
+ end
260
+ end
261
+
262
+ describe Sequel::Dataset do
263
+ before(:all) do
264
+ DB.create_table!(:test) do
265
+ String :name
266
+ Integer :value
267
+ end
268
+ @d = DB[:test]
269
+ @d.multi_insert([{:name => 'abc', :value => 123}, {:name => 'abc', :value => 456}, {:name => 'def', :value => 789}])
270
+ end
271
+ after(:all) do
272
+ DB.drop_table?(:test)
273
+ end
274
+
275
+ it "should correctly return avg" do
276
+ @d.avg(:value).to_i.must_equal 456
277
+ end
278
+
279
+ it "should correctly return sum" do
280
+ @d.sum(:value).to_i.must_equal 1368
281
+ end
282
+
283
+ it "should correctly return max" do
284
+ @d.max(:value).to_i.must_equal 789
285
+ end
286
+
287
+ it "should correctly return min" do
288
+ @d.min(:value).to_i.must_equal 123
289
+ end
290
+
291
+ it "should return the correct record count" do
292
+ @d.count.must_equal 3
293
+ end
294
+
295
+ it "should handle functions with identifier names correctly" do
296
+ @d.get{sum.function(:value)}.must_equal 1368
297
+ end
298
+
299
+ it "should handle aggregate methods on limited datasets correctly" do
300
+ @d = @d.order(:name).limit(2)
301
+ @d.count.must_equal 2
302
+ @d.avg(:value).to_i.must_equal 289
303
+ @d.min(:value).to_i.must_equal 123
304
+ @d.reverse(:value).min(:value).to_i.must_equal 456
305
+ @d.max(:value).to_i.must_equal 456
306
+ @d.sum(:value).to_i.must_equal 579
307
+ @d.interval(:value).to_i.must_equal 333
308
+ end
309
+
310
+ it "should return the correct records" do
311
+ @d.order(:value).to_a.must_equal [{:name => 'abc', :value => 123}, {:name => 'abc', :value => 456}, {:name => 'def', :value => 789}]
312
+ end
313
+ end
314
+
315
+ describe Sequel::Database do
316
+ it "should correctly escape strings" do
317
+ ["\\\n",
318
+ "\\\\\n",
319
+ "\\\r\n",
320
+ "\\\\\r\n",
321
+ "\\\\\n\n",
322
+ "\\\\\r\n\r\n",
323
+ "\b\a'\0\3",
324
+ #"\t\b\a'\0\3",
325
+ "\\dingo",
326
+ "\\'dingo",
327
+ "\\\\''dingo",
328
+ ].each do |str|
329
+ DB.get(Sequel.cast(str, String)).must_equal str
330
+ str = "1#{str}1"
331
+ DB.get(Sequel.cast(str, String)).must_equal str
332
+ str = "#{str}#{str}"
333
+ DB.get(Sequel.cast(str, String)).must_equal str
334
+ end
335
+ end
336
+
337
+ it "should have a working table_exists?" do
338
+ t = :basdfdsafsaddsaf
339
+ DB.drop_table?(t)
340
+ DB.table_exists?(t).must_equal false
341
+ DB.create_table(t){Integer :a}
342
+ begin
343
+ DB.table_exists?(t).must_equal true
344
+ ensure
345
+ DB.drop_table(t)
346
+ end
347
+ end
348
+ end
349
+
350
+ describe "Simple Dataset operations" do
351
+ before do
352
+ DB.create_table!(:items) do
353
+ Integer :number
354
+ TrueClass :flag
355
+ end
356
+ @ds = DB[:items]
357
+ end
358
+ after do
359
+ DB.drop_table?(:items)
360
+ end
361
+
362
+ it "should deal with boolean conditions correctly" do
363
+ @ds.insert(:number=>1, :flag=>true)
364
+ @ds.insert(:number=>2, :flag=>false)
365
+ @ds.insert(:number=>3, :flag=>nil)
366
+ @ds.order!(:number)
367
+ @ds.filter(:flag=>true).map(:number).must_equal [1]
368
+ @ds.filter(:flag=>false).map(:number).must_equal [2]
369
+ @ds.filter(:flag=>nil).map(:number).must_equal [3]
370
+ @ds.exclude(:flag=>true).map(:number).must_equal [2, 3]
371
+ @ds.exclude(:flag=>false).map(:number).must_equal [1, 3]
372
+ @ds.exclude(:flag=>nil).map(:number).must_equal [1, 2]
373
+ end
374
+ end
375
+
376
+ describe "Dataset UNION, EXCEPT, and INTERSECT" do
377
+ before(:all) do
378
+ DB.create_table!(:i1){integer :number}
379
+ DB.create_table!(:i2){integer :number}
380
+ DB.create_table!(:i3){integer :number}
381
+ @ds1 = DB[:i1]
382
+ @ds1.insert(:number=>8)
383
+ @ds1.insert(:number=>10)
384
+ @ds1.insert(:number=>20)
385
+ @ds1.insert(:number=>38)
386
+ @ds2 = DB[:i2]
387
+ @ds2.insert(:number=>9)
388
+ @ds2.insert(:number=>10)
389
+ @ds2.insert(:number=>30)
390
+ @ds2.insert(:number=>39)
391
+ @ds3 = DB[:i3]
392
+ @ds3.insert(:number=>10)
393
+ @ds3.insert(:number=>40)
394
+ end
395
+ after(:all) do
396
+ DB.drop_table?(:i1, :i2, :i3)
397
+ end
398
+
399
+ it "should give the correct results for simple UNION" do
400
+ @ds1.union(@ds2).order(:number).map{|x| x[:number].to_s}.must_equal %w'8 9 10 20 30 38 39'
401
+ end
402
+
403
+ it "should give the correct results for UNION when used with ordering and limits" do
404
+
405
+ @ds1.reverse_order(:number).union(@ds2).order(:number).map{|x| x[:number].to_s}.must_equal %w'8 9 10 20 30 38 39'
406
+ @ds1.union(@ds2.reverse_order(:number)).order(:number).map{|x| x[:number].to_s}.must_equal %w'8 9 10 20 30 38 39'
407
+
408
+ @ds1.reverse_order(:number).limit(1).union(@ds2).order(:number).map{|x| x[:number].to_s}.must_equal %w'9 10 30 38 39'
409
+ @ds2.reverse_order(:number).limit(1).union(@ds1).order(:number).map{|x| x[:number].to_s}.must_equal %w'8 10 20 38 39'
410
+
411
+ @ds1.union(@ds2.order(:number).limit(1)).order(:number).map{|x| x[:number].to_s}.must_equal %w'8 9 10 20 38'
412
+ @ds2.union(@ds1.order(:number).limit(1)).order(:number).map{|x| x[:number].to_s}.must_equal %w'8 9 10 30 39'
413
+
414
+ @ds1.union(@ds2).limit(2).order(:number).map{|x| x[:number].to_s}.must_equal %w'8 9'
415
+ @ds2.union(@ds1).reverse_order(:number).limit(2).map{|x| x[:number].to_s}.must_equal %w'39 38'
416
+
417
+ @ds1.reverse_order(:number).limit(2).union(@ds2.reverse_order(:number).limit(2)).order(:number).limit(3).map{|x| x[:number].to_s}.must_equal %w'20 30 38'
418
+ @ds2.order(:number).limit(2).union(@ds1.order(:number).limit(2)).reverse_order(:number).limit(3).map{|x| x[:number].to_s}.must_equal %w'10 9 8'
419
+ end
420
+
421
+ it "should give the correct results for compound UNION" do
422
+ @ds1.union(@ds2).union(@ds3).order(:number).map{|x| x[:number].to_s}.must_equal %w'8 9 10 20 30 38 39 40'
423
+ @ds1.union(@ds2.union(@ds3)).order(:number).map{|x| x[:number].to_s}.must_equal %w'8 9 10 20 30 38 39 40'
424
+ end
425
+ end
426
+
427
+ describe "Common Table Expressions" do
428
+ before(:all) do
429
+ @db = DB
430
+ @db.create_table!(:i1){Integer :id; Integer :parent_id}
431
+ @ds = @db[:i1]
432
+ @ds.insert(:id=>1)
433
+ @ds.insert(:id=>2)
434
+ @ds.insert(:id=>3, :parent_id=>1)
435
+ @ds.insert(:id=>4, :parent_id=>1)
436
+ @ds.insert(:id=>5, :parent_id=>3)
437
+ @ds.insert(:id=>6, :parent_id=>5)
438
+ end
439
+ after(:all) do
440
+ @db.drop_table?(:i1)
441
+ end
442
+
443
+ it "should give correct results for WITH" do
444
+ @db[:t].with(:t, @ds.filter(:parent_id=>nil).select(:id)).order(:id).map(:id).must_equal [1, 2]
445
+ end
446
+
447
+ it "should support joining a dataset with a CTE" do
448
+ @ds.inner_join(@db[:t].with(:t, @ds.filter(:parent_id=>nil)), :id => :id).select(:i1__id).order(:i1__id).map(:id).must_equal [1,2]
449
+ @db[:t].with(:t, @ds).inner_join(@db[:s].with(:s, @ds.filter(:parent_id=>nil)), :id => :id).select(:t__id).order(:t__id).map(:id).must_equal [1,2]
450
+ end
451
+
452
+ it "should support a subselect in the FROM clause with a CTE" do
453
+ @ds.from(@db[:t].with(:t, @ds)).select_order_map(:id).must_equal [1,2,3,4,5,6]
454
+ @db[:t].with(:t, @ds).from_self.select_order_map(:id).must_equal [1,2,3,4,5,6]
455
+ end
456
+
457
+ it "should support using a CTE inside a CTE" do
458
+ @db[:s].with(:s, @db[:t].with(:t, @ds)).select_order_map(:id).must_equal [1,2,3,4,5,6]
459
+ @db[:s].with_recursive(:s, @db[:t].with(:t, @ds), @db[:t2].with(:t2, @ds)).select_order_map(:id).must_equal [1,1,2,2,3,3,4,4,5,5,6,6]
460
+ end
461
+
462
+ it "should support using a CTE inside UNION/EXCEPT/INTERSECT" do
463
+ @ds.union(@db[:t].with(:t, @ds)).select_order_map(:id).must_equal [1,2,3,4,5,6]
464
+ if @ds.supports_intersect_except?
465
+ @ds.intersect(@db[:t].with(:t, @ds)).select_order_map(:id).must_equal [1,2,3,4,5,6]
466
+ @ds.except(@db[:t].with(:t, @ds)).select_order_map(:id).must_equal []
467
+ end
468
+ end
469
+ end
470
+
471
+ describe "Window Functions" do
472
+ before(:all) do
473
+ @db = DB
474
+ @db.create_table!(:i1){Integer :id; Integer :group_id; Integer :amount}
475
+ @ds = @db[:i1].order(:id)
476
+ @ds.insert(:id=>1, :group_id=>1, :amount=>1)
477
+ @ds.insert(:id=>2, :group_id=>1, :amount=>10)
478
+ @ds.insert(:id=>3, :group_id=>1, :amount=>100)
479
+ @ds.insert(:id=>4, :group_id=>2, :amount=>1000)
480
+ @ds.insert(:id=>5, :group_id=>2, :amount=>10000)
481
+ @ds.insert(:id=>6, :group_id=>2, :amount=>100000)
482
+ end
483
+ after(:all) do
484
+ @db.drop_table?(:i1)
485
+ end
486
+
487
+ it "should give correct results for aggregate window functions" do
488
+ @ds.select(:id){sum(:amount).over(:partition=>:group_id).as(:sum)}.all.
489
+ must_equal [{:sum=>111, :id=>1}, {:sum=>111, :id=>2}, {:sum=>111, :id=>3}, {:sum=>111000, :id=>4}, {:sum=>111000, :id=>5}, {:sum=>111000, :id=>6}]
490
+ @ds.select(:id){sum(:amount).over.as(:sum)}.all.
491
+ must_equal [{:sum=>111111, :id=>1}, {:sum=>111111, :id=>2}, {:sum=>111111, :id=>3}, {:sum=>111111, :id=>4}, {:sum=>111111, :id=>5}, {:sum=>111111, :id=>6}]
492
+ end
493
+
494
+ it "should give correct results for ranking window functions with orders" do
495
+ @ds.select(:id){rank{}.over(:partition=>:group_id, :order=>:id).as(:rank)}.all.
496
+ must_equal [{:rank=>1, :id=>1}, {:rank=>2, :id=>2}, {:rank=>3, :id=>3}, {:rank=>1, :id=>4}, {:rank=>2, :id=>5}, {:rank=>3, :id=>6}]
497
+ @ds.select(:id){rank{}.over(:order=>id).as(:rank)}.all.
498
+ must_equal [{:rank=>1, :id=>1}, {:rank=>2, :id=>2}, {:rank=>3, :id=>3}, {:rank=>4, :id=>4}, {:rank=>5, :id=>5}, {:rank=>6, :id=>6}]
499
+ end
500
+
501
+ it "should give correct results for aggregate window functions with orders" do
502
+ @ds.select(:id){sum(:amount).over(:partition=>:group_id, :order=>:id).as(:sum)}.all.
503
+ must_equal [{:sum=>1, :id=>1}, {:sum=>11, :id=>2}, {:sum=>111, :id=>3}, {:sum=>1000, :id=>4}, {:sum=>11000, :id=>5}, {:sum=>111000, :id=>6}]
504
+ @ds.select(:id){sum(:amount).over(:order=>:id).as(:sum)}.all.
505
+ must_equal [{:sum=>1, :id=>1}, {:sum=>11, :id=>2}, {:sum=>111, :id=>3}, {:sum=>1111, :id=>4}, {:sum=>11111, :id=>5}, {:sum=>111111, :id=>6}]
506
+ end
507
+
508
+ it "should give correct results for aggregate window functions with frames" do
509
+ @ds.select(:id){sum(:amount).over(:partition=>:group_id, :order=>:id, :frame=>:all).as(:sum)}.all.
510
+ must_equal [{:sum=>111, :id=>1}, {:sum=>111, :id=>2}, {:sum=>111, :id=>3}, {:sum=>111000, :id=>4}, {:sum=>111000, :id=>5}, {:sum=>111000, :id=>6}]
511
+ @ds.select(:id){sum(:amount).over(:order=>:id, :frame=>:all).as(:sum)}.all.
512
+ must_equal [{:sum=>111111, :id=>1}, {:sum=>111111, :id=>2}, {:sum=>111111, :id=>3}, {:sum=>111111, :id=>4}, {:sum=>111111, :id=>5}, {:sum=>111111, :id=>6}]
513
+
514
+ @ds.select(:id){sum(:amount).over(:partition=>:group_id, :order=>:id, :frame=>:rows).as(:sum)}.all.
515
+ must_equal [{:sum=>1, :id=>1}, {:sum=>11, :id=>2}, {:sum=>111, :id=>3}, {:sum=>1000, :id=>4}, {:sum=>11000, :id=>5}, {:sum=>111000, :id=>6}]
516
+ @ds.select(:id){sum(:amount).over(:order=>:id, :frame=>:rows).as(:sum)}.all.
517
+ must_equal [{:sum=>1, :id=>1}, {:sum=>11, :id=>2}, {:sum=>111, :id=>3}, {:sum=>1111, :id=>4}, {:sum=>11111, :id=>5}, {:sum=>111111, :id=>6}]
518
+ end
519
+ end
520
+
521
+ describe Sequel::SQL::Constants do
522
+ before do
523
+ @db = DB
524
+ @ds = @db[:constants]
525
+ @c = proc do |v|
526
+ case v
527
+ when Time
528
+ v
529
+ when DateTime, String
530
+ Time.parse(v.to_s)
531
+ else
532
+ v
533
+ end
534
+ end
535
+ @c2 = proc{|v| v.is_a?(Date) ? v : Date.parse(v) }
536
+ end
537
+ after do
538
+ @db.drop_table?(:constants)
539
+ end
540
+
541
+ it "should have working CURRENT_TIMESTAMP" do
542
+ @db.create_table!(:constants){DateTime :ts}
543
+ @ds.insert(:ts=>Sequel::CURRENT_TIMESTAMP)
544
+ (Time.now - @c[@ds.get(:ts)]).must_be_close_to 0, 86400
545
+ end
546
+ end
547
+
548
+ describe "Sequel::Dataset#import and #multi_insert" do
549
+ before do
550
+ @db = DB
551
+ @db.create_table!(:imp){Integer :i}
552
+ @ids = @db[:imp].order(:i)
553
+ end
554
+ after do
555
+ @db.drop_table?(:imp)
556
+ end
557
+
558
+ it "should import with multi_insert and an array of hashes" do
559
+ @ids.multi_insert([{:i=>10}, {:i=>20}])
560
+ @ids.all.must_equal [{:i=>10}, {:i=>20}]
561
+ end
562
+
563
+ it "should import with an array of arrays of values" do
564
+ @ids.import([:i], [[10], [20]])
565
+ @ids.all.must_equal [{:i=>10}, {:i=>20}]
566
+ end
567
+
568
+ it "should import with a dataset" do
569
+ @db.create_table!(:exp2){Integer :i}
570
+ @db[:exp2].import([:i], [[10], [20]])
571
+ @ids.import([:i], @db[:exp2])
572
+ @ids.all.must_equal [{:i=>10}, {:i=>20}]
573
+ @db.drop_table(:exp2)
574
+ end
575
+
576
+ it "should have import work with the :slice_size option" do
577
+ @ids.import([:i], [[10], [20], [30]], :slice_size=>1)
578
+ @ids.all.must_equal [{:i=>10}, {:i=>20}, {:i=>30}]
579
+ end
580
+
581
+ it "should import many rows at once" do
582
+ @ids.import([:i], (1..20).to_a.map{|x| [x]})
583
+ @ids.select_order_map(:i).must_equal((1..20).to_a)
584
+ end
585
+ end
586
+
587
+ describe "Sequel::Dataset convenience methods" do
588
+ before do
589
+ @db = DB
590
+ @db.create_table!(:a){Integer :a; Integer :b}
591
+ @ds = @db[:a].order(:a)
592
+ end
593
+ after do
594
+ @db.drop_table?(:a)
595
+ end
596
+
597
+ it "#empty? should return whether the dataset returns no rows" do
598
+ @ds.empty?.must_equal true
599
+ @ds.insert(20, 10)
600
+ @ds.empty?.must_equal false
601
+ end
602
+
603
+ it "#empty? should work correctly for datasets with limits" do
604
+ ds = @ds.limit(1)
605
+ ds.empty?.must_equal true
606
+ ds.insert(20, 10)
607
+ ds.empty?.must_equal false
608
+ end
609
+
610
+ it "#empty? should work correctly for datasets with limits and offsets" do
611
+ ds = @ds.order(:a).limit(1, 1)
612
+ ds.empty?.must_equal true
613
+ ds.insert(20, 10)
614
+ ds.empty?.must_equal true
615
+ ds.insert(20, 10)
616
+ ds.empty?.must_equal false
617
+ end
618
+
619
+ it "#group_and_count should return a grouping by count" do
620
+ @ds.group_and_count(:a).order{count(:a)}.all.must_equal []
621
+ @ds.insert(20, 10)
622
+ @ds.group_and_count(:a).order{count(:a)}.all.each{|h| h[:count] = h[:count].to_i}.must_equal [{:a=>20, :count=>1}]
623
+ @ds.insert(20, 30)
624
+ @ds.group_and_count(:a).order{count(:a)}.all.each{|h| h[:count] = h[:count].to_i}.must_equal [{:a=>20, :count=>2}]
625
+ @ds.insert(30, 30)
626
+ @ds.group_and_count(:a).order{count(:a)}.all.each{|h| h[:count] = h[:count].to_i}.must_equal [{:a=>30, :count=>1}, {:a=>20, :count=>2}]
627
+ end
628
+
629
+ it "#group_and_count should support column aliases" do
630
+ @ds.group_and_count(:a___c).order{count(:a)}.all.must_equal []
631
+ @ds.insert(20, 10)
632
+ @ds.group_and_count(:a___c).order{count(:a)}.all.each{|h| h[:count] = h[:count].to_i}.must_equal [{:c=>20, :count=>1}]
633
+ @ds.insert(20, 30)
634
+ @ds.group_and_count(:a___c).order{count(:a)}.all.each{|h| h[:count] = h[:count].to_i}.must_equal [{:c=>20, :count=>2}]
635
+ @ds.insert(30, 30)
636
+ @ds.group_and_count(:a___c).order{count(:a)}.all.each{|h| h[:count] = h[:count].to_i}.must_equal [{:c=>30, :count=>1}, {:c=>20, :count=>2}]
637
+ end
638
+
639
+ it "#range should return the range between the maximum and minimum values" do
640
+ @ds = @ds.unordered
641
+ @ds.insert(20, 10)
642
+ @ds.insert(30, 10)
643
+ @ds.range(:a).must_equal(20..30)
644
+ @ds.range(:b).must_equal(10..10)
645
+ end
646
+
647
+ it "#interval should return the different between the maximum and minimum values" do
648
+ @ds = @ds.unordered
649
+ @ds.insert(20, 10)
650
+ @ds.insert(30, 10)
651
+ @ds.interval(:a).to_i.must_equal 10
652
+ @ds.interval(:b).to_i.must_equal 0
653
+ end
654
+ end
655
+
656
+ describe "Sequel::Dataset main SQL methods" do
657
+ before do
658
+ @db = DB
659
+ @db.create_table!(:d){Integer :a; Integer :b}
660
+ @ds = @db[:d].order(:a)
661
+ end
662
+ after do
663
+ @db.drop_table?(:d)
664
+ end
665
+
666
+ it "#exists should return a usable exists clause" do
667
+ @ds.filter(@db[:d___c].filter(:c__a=>:d__b).exists).all.must_equal []
668
+ @ds.insert(20, 30)
669
+ @ds.insert(10, 20)
670
+ @ds.filter(@db[:d___c].filter(:c__a=>:d__b).exists).all.must_equal [{:a=>10, :b=>20}]
671
+ end
672
+
673
+ it "#filter and #exclude should work with placeholder strings" do
674
+ @ds.insert(20, 30)
675
+ @ds.filter("a > ?", 15).all.must_equal [{:a=>20, :b=>30}]
676
+ @ds.exclude("b < ?", 15).all.must_equal [{:a=>20, :b=>30}]
677
+ @ds.filter("b < ?", 15).invert.all.must_equal [{:a=>20, :b=>30}]
678
+ end
679
+
680
+ it "#and and #or should work correctly" do
681
+ @ds.insert(20, 30)
682
+ @ds.filter(:a=>20).and(:b=>30).all.must_equal [{:a=>20, :b=>30}]
683
+ @ds.filter(:a=>20).and(:b=>15).all.must_equal []
684
+ @ds.filter(:a=>20).or(:b=>15).all.must_equal [{:a=>20, :b=>30}]
685
+ @ds.filter(:a=>10).or(:b=>15).all.must_equal []
686
+ end
687
+
688
+ it "#select_group should work correctly" do
689
+ @ds.unordered!
690
+ @ds.select_group(:a).all.must_equal []
691
+ @ds.insert(20, 30)
692
+ @ds.select_group(:a).all.must_equal [{:a=>20}]
693
+ @ds.select_group(:b).all.must_equal [{:b=>30}]
694
+ @ds.insert(20, 40)
695
+ @ds.select_group(:a).all.must_equal [{:a=>20}]
696
+ @ds.order(:b).select_group(:b).all.must_equal [{:b=>30}, {:b=>40}]
697
+ end
698
+
699
+ it "#select_group should work correctly when aliasing" do
700
+ @ds.unordered!
701
+ @ds.insert(20, 30)
702
+ @ds.select_group(:b___c).all.must_equal [{:c=>30}]
703
+ end
704
+
705
+ it "#having should work correctly" do
706
+ @ds.unordered!
707
+ @ds.select{[b, max(a).as(c)]}.group(:b).having{max(a) > 30}.all.must_equal []
708
+ @ds.insert(20, 30)
709
+ @ds.select{[b, max(a).as(c)]}.group(:b).having{max(a) > 30}.all.must_equal []
710
+ @ds.insert(40, 20)
711
+ @ds.select{[b, max(a).as(c)]}.group(:b).having{max(a) > 30}.all.each{|h| h[:c] = h[:c].to_i}.must_equal [{:b=>20, :c=>40}]
712
+ end
713
+
714
+ it "#having should work without a previous group" do
715
+ @ds.unordered!
716
+ @ds.select{max(a).as(c)}.having{max(a) > 30}.all.must_equal []
717
+ @ds.insert(20, 30)
718
+ @ds.select{max(a).as(c)}.having{max(a) > 30}.all.must_equal []
719
+ @ds.insert(40, 20)
720
+ @ds.select{max(a).as(c)}.having{max(a) > 30}.all.each{|h| h[:c] = h[:c].to_i}.must_equal [{:c=>40}]
721
+ end
722
+ end
723
+
724
+ describe "Sequel::Dataset convenience methods" do
725
+ before do
726
+ @db = DB
727
+ @db.create_table!(:a){Integer :a; Integer :b; Integer :c; Integer :d}
728
+ @ds = @db[:a].order(:a)
729
+ @ds.insert(1, 2, 3, 4)
730
+ @ds.insert(5, 6, 7, 8)
731
+ end
732
+ after do
733
+ @db.drop_table?(:a)
734
+ end
735
+
736
+ it "should have working #map" do
737
+ @ds.map(:a).must_equal [1, 5]
738
+ @ds.map(:b).must_equal [2, 6]
739
+ @ds.map([:a, :b]).must_equal [[1, 2], [5, 6]]
740
+ end
741
+
742
+ it "should have working #to_hash" do
743
+ @ds.to_hash(:a).must_equal(1=>{:a=>1, :b=>2, :c=>3, :d=>4}, 5=>{:a=>5, :b=>6, :c=>7, :d=>8})
744
+ @ds.to_hash(:b).must_equal(2=>{:a=>1, :b=>2, :c=>3, :d=>4}, 6=>{:a=>5, :b=>6, :c=>7, :d=>8})
745
+ @ds.to_hash([:a, :b]).must_equal([1, 2]=>{:a=>1, :b=>2, :c=>3, :d=>4}, [5, 6]=>{:a=>5, :b=>6, :c=>7, :d=>8})
746
+
747
+ @ds.to_hash(:a, :b).must_equal(1=>2, 5=>6)
748
+ @ds.to_hash([:a, :c], :b).must_equal([1, 3]=>2, [5, 7]=>6)
749
+ @ds.to_hash(:a, [:b, :c]).must_equal(1=>[2, 3], 5=>[6, 7])
750
+ @ds.to_hash([:a, :c], [:b, :d]).must_equal([1, 3]=>[2, 4], [5, 7]=>[6, 8])
751
+ end
752
+
753
+ it "should have working #to_hash_groups" do
754
+ ds = @ds.order(*@ds.columns)
755
+ ds.insert(1, 2, 3, 9)
756
+ ds.to_hash_groups(:a).must_equal(1=>[{:a=>1, :b=>2, :c=>3, :d=>4}, {:a=>1, :b=>2, :c=>3, :d=>9}], 5=>[{:a=>5, :b=>6, :c=>7, :d=>8}])
757
+ ds.to_hash_groups(:b).must_equal(2=>[{:a=>1, :b=>2, :c=>3, :d=>4}, {:a=>1, :b=>2, :c=>3, :d=>9}], 6=>[{:a=>5, :b=>6, :c=>7, :d=>8}])
758
+ ds.to_hash_groups([:a, :b]).must_equal([1, 2]=>[{:a=>1, :b=>2, :c=>3, :d=>4}, {:a=>1, :b=>2, :c=>3, :d=>9}], [5, 6]=>[{:a=>5, :b=>6, :c=>7, :d=>8}])
759
+
760
+ ds.to_hash_groups(:a, :d).must_equal(1=>[4, 9], 5=>[8])
761
+ ds.to_hash_groups([:a, :c], :d).must_equal([1, 3]=>[4, 9], [5, 7]=>[8])
762
+ ds.to_hash_groups(:a, [:b, :d]).must_equal(1=>[[2, 4], [2, 9]], 5=>[[6, 8]])
763
+ ds.to_hash_groups([:a, :c], [:b, :d]).must_equal([1, 3]=>[[2, 4], [2, 9]], [5, 7]=>[[6, 8]])
764
+ end
765
+
766
+ it "should have working #select_map" do
767
+ @ds.select_map(:a).must_equal [1, 5]
768
+ @ds.select_map(:b).must_equal [2, 6]
769
+ @ds.select_map([:a]).must_equal [[1], [5]]
770
+ @ds.select_map([:a, :b]).must_equal [[1, 2], [5, 6]]
771
+
772
+ @ds.select_map(:a___e).must_equal [1, 5]
773
+ @ds.select_map(:b___e).must_equal [2, 6]
774
+ @ds.select_map([:a___e, :b___f]).must_equal [[1, 2], [5, 6]]
775
+ @ds.select_map([:a__a___e, :a__b___f]).must_equal [[1, 2], [5, 6]]
776
+ @ds.select_map([Sequel.expr(:a__a).as(:e), Sequel.expr(:a__b).as(:f)]).must_equal [[1, 2], [5, 6]]
777
+ @ds.select_map([Sequel.qualify(:a, :a).as(:e), Sequel.qualify(:a, :b).as(:f)]).must_equal [[1, 2], [5, 6]]
778
+ @ds.select_map([Sequel.identifier(:a).qualify(:a).as(:e), Sequel.qualify(:a, :b).as(:f)]).must_equal [[1, 2], [5, 6]]
779
+ end
780
+
781
+ it "should have working #select_order_map" do
782
+ @ds.select_order_map(:a).must_equal [1, 5]
783
+ @ds.select_order_map(Sequel.desc(:a__b)).must_equal [6, 2]
784
+ @ds.select_order_map(Sequel.desc(:a__b___e)).must_equal [6, 2]
785
+ @ds.select_order_map(Sequel.qualify(:a, :b).as(:e)).must_equal [2, 6]
786
+ @ds.select_order_map([:a]).must_equal [[1], [5]]
787
+ @ds.select_order_map([Sequel.desc(:a), :b]).must_equal [[5, 6], [1, 2]]
788
+
789
+ @ds.select_order_map(:a___e).must_equal [1, 5]
790
+ @ds.select_order_map(:b___e).must_equal [2, 6]
791
+ @ds.select_order_map([Sequel.desc(:a___e), :b___f]).must_equal [[5, 6], [1, 2]]
792
+ @ds.select_order_map([Sequel.desc(:a__a___e), :a__b___f]).must_equal [[5, 6], [1, 2]]
793
+ @ds.select_order_map([Sequel.desc(:a__a), Sequel.expr(:a__b).as(:f)]).must_equal [[5, 6], [1, 2]]
794
+ @ds.select_order_map([Sequel.qualify(:a, :a).desc, Sequel.qualify(:a, :b).as(:f)]).must_equal [[5, 6], [1, 2]]
795
+ @ds.select_order_map([Sequel.identifier(:a).qualify(:a).desc, Sequel.qualify(:a, :b).as(:f)]).must_equal [[5, 6], [1, 2]]
796
+ end
797
+
798
+ it "should have working #select_hash" do
799
+ @ds.select_hash(:a, :b).must_equal(1=>2, 5=>6)
800
+ @ds.select_hash(:a__a___e, :b).must_equal(1=>2, 5=>6)
801
+ @ds.select_hash(Sequel.expr(:a__a).as(:e), :b).must_equal(1=>2, 5=>6)
802
+ @ds.select_hash(Sequel.qualify(:a, :a).as(:e), :b).must_equal(1=>2, 5=>6)
803
+ @ds.select_hash(Sequel.identifier(:a).qualify(:a).as(:e), :b).must_equal(1=>2, 5=>6)
804
+ @ds.select_hash([:a, :c], :b).must_equal([1, 3]=>2, [5, 7]=>6)
805
+ @ds.select_hash(:a, [:b, :c]).must_equal(1=>[2, 3], 5=>[6, 7])
806
+ @ds.select_hash([:a, :c], [:b, :d]).must_equal([1, 3]=>[2, 4], [5, 7]=>[6, 8])
807
+ end
808
+
809
+ it "should have working #select_hash_groups" do
810
+ ds = @ds.order(*@ds.columns)
811
+ ds.insert(1, 2, 3, 9)
812
+ ds.select_hash_groups(:a, :d).must_equal(1=>[4, 9], 5=>[8])
813
+ ds.select_hash_groups(:a__a___e, :d).must_equal(1=>[4, 9], 5=>[8])
814
+ ds.select_hash_groups(Sequel.expr(:a__a).as(:e), :d).must_equal(1=>[4, 9], 5=>[8])
815
+ ds.select_hash_groups(Sequel.qualify(:a, :a).as(:e), :d).must_equal(1=>[4, 9], 5=>[8])
816
+ ds.select_hash_groups(Sequel.identifier(:a).qualify(:a).as(:e), :d).must_equal(1=>[4, 9], 5=>[8])
817
+ ds.select_hash_groups([:a, :c], :d).must_equal([1, 3]=>[4, 9], [5, 7]=>[8])
818
+ ds.select_hash_groups(:a, [:b, :d]).must_equal(1=>[[2, 4], [2, 9]], 5=>[[6, 8]])
819
+ ds.select_hash_groups([:a, :c], [:b, :d]).must_equal([1, 3]=>[[2, 4], [2, 9]], [5, 7]=>[[6, 8]])
820
+ end
821
+ end
822
+
823
+ describe "Sequel::Dataset DSL support" do
824
+ before do
825
+ @db = DB
826
+ @db.create_table!(:a){Integer :a; Integer :b}
827
+ @ds = @db[:a].order(:a)
828
+ end
829
+ after do
830
+ @db.drop_table?(:a)
831
+ end
832
+
833
+ it "should work with standard mathematical operators" do
834
+ @ds.insert(20, 10)
835
+ @ds.get{a + b}.to_i.must_equal 30
836
+ @ds.get{a - b}.to_i.must_equal 10
837
+ @ds.get{a * b}.to_i.must_equal 200
838
+ @ds.get{a / b}.to_i.must_equal 2
839
+ end
840
+
841
+ it "should work with bitwise AND and OR operators" do
842
+ @ds.insert(3, 5)
843
+ @ds.get{a.sql_number | b}.to_i.must_equal 7
844
+ @ds.get{a.sql_number & b}.to_i.must_equal 1
845
+ @ds.get{a.sql_number | b | 8}.to_i.must_equal 15
846
+ @ds.get{a.sql_number & b & 8}.to_i.must_equal 0
847
+ end
848
+
849
+ it "should work with the bitwise compliment operator" do
850
+ @ds.insert(-3, 3)
851
+ @ds.get{~a.sql_number}.to_i.must_equal 2
852
+ @ds.get{~b.sql_number}.to_i.must_equal(-4)
853
+ end
854
+
855
+ it "should work with the bitwise xor operator" do
856
+ @ds.insert(3, 5)
857
+ @ds.get{a.sql_number ^ b}.to_i.must_equal 6
858
+ @ds.get{a.sql_number ^ b ^ 1}.to_i.must_equal 7
859
+ end
860
+
861
+ it "should work with the modulus operator" do
862
+ @ds.insert(3, 5)
863
+ @ds.get{a.sql_number % 4}.to_i.must_equal 3
864
+ @ds.get{b.sql_number % 4}.to_i.must_equal 1
865
+ @ds.get{a.sql_number % 4 % 2}.to_i.must_equal 1
866
+ end
867
+
868
+ it "should work with inequality operators" do
869
+ @ds.insert(10, 11)
870
+ @ds.insert(11, 11)
871
+ @ds.insert(20, 19)
872
+ @ds.insert(20, 20)
873
+ @ds.filter{a > b}.select_order_map(:a).must_equal [20]
874
+ @ds.filter{a >= b}.select_order_map(:a).must_equal [11, 20, 20]
875
+ @ds.filter{a < b}.select_order_map(:a).must_equal [10]
876
+ @ds.filter{a <= b}.select_order_map(:a).must_equal [10, 11, 20]
877
+ end
878
+
879
+ it "should work with casting and string concatentation" do
880
+ @ds.insert(20, 20)
881
+ @ds.get{Sequel.cast(a, String).sql_string + Sequel.cast(b, String)}.must_equal '2020'
882
+ end
883
+
884
+ it "should work with ordering" do
885
+ @ds.insert(10, 20)
886
+ @ds.insert(20, 10)
887
+ @ds.order(:a, :b).all.must_equal [{:a=>10, :b=>20}, {:a=>20, :b=>10}]
888
+ @ds.order(Sequel.asc(:a), Sequel.asc(:b)).all.must_equal [{:a=>10, :b=>20}, {:a=>20, :b=>10}]
889
+ @ds.order(Sequel.desc(:a), Sequel.desc(:b)).all.must_equal [{:a=>20, :b=>10}, {:a=>10, :b=>20}]
890
+ end
891
+
892
+ it "should work with qualifying" do
893
+ @ds.insert(10, 20)
894
+ @ds.get(:a__b).must_equal 20
895
+ @ds.get{a__b}.must_equal 20
896
+ @ds.get(Sequel.qualify(:a, :b)).must_equal 20
897
+ end
898
+
899
+ it "should work with aliasing" do
900
+ @ds.insert(10, 20)
901
+ @ds.get(:a__b___c).must_equal 20
902
+ @ds.get{a__b.as(c)}.must_equal 20
903
+ @ds.get(Sequel.qualify(:a, :b).as(:c)).must_equal 20
904
+ @ds.get(Sequel.as(:b, :c)).must_equal 20
905
+ end
906
+
907
+ it "should work with selecting all columns of a table" do
908
+ @ds.insert(20, 10)
909
+ @ds.select_all(:a).all.must_equal [{:a=>20, :b=>10}]
910
+ end
911
+
912
+ it "should work with ranges as hash values" do
913
+ @ds.insert(20, 10)
914
+ @ds.filter(:a=>(10..30)).all.must_equal [{:a=>20, :b=>10}]
915
+ @ds.filter(:a=>(25..30)).all.must_equal []
916
+ @ds.filter(:a=>(10..15)).all.must_equal []
917
+ @ds.exclude(:a=>(10..30)).all.must_equal []
918
+ @ds.exclude(:a=>(25..30)).all.must_equal [{:a=>20, :b=>10}]
919
+ @ds.exclude(:a=>(10..15)).all.must_equal [{:a=>20, :b=>10}]
920
+ end
921
+
922
+ it "should work with nil as hash value" do
923
+ @ds.insert(20, nil)
924
+ @ds.filter(:a=>nil).all.must_equal []
925
+ @ds.filter(:b=>nil).all.must_equal [{:a=>20, :b=>nil}]
926
+ @ds.exclude(:b=>nil).all.must_equal []
927
+ @ds.exclude(:a=>nil).all.must_equal [{:a=>20, :b=>nil}]
928
+ end
929
+
930
+ it "should work with arrays as hash values" do
931
+ @ds.insert(20, 10)
932
+ @ds.filter(:a=>[10]).all.must_equal []
933
+ @ds.filter(:a=>[20, 10]).all.must_equal [{:a=>20, :b=>10}]
934
+ @ds.exclude(:a=>[10]).all.must_equal [{:a=>20, :b=>10}]
935
+ @ds.exclude(:a=>[20, 10]).all.must_equal []
936
+ end
937
+
938
+ it "should work with ranges as hash values" do
939
+ @ds.insert(20, 10)
940
+ @ds.filter(:a=>(10..30)).all.must_equal [{:a=>20, :b=>10}]
941
+ @ds.filter(:a=>(25..30)).all.must_equal []
942
+ @ds.filter(:a=>(10..15)).all.must_equal []
943
+ @ds.exclude(:a=>(10..30)).all.must_equal []
944
+ @ds.exclude(:a=>(25..30)).all.must_equal [{:a=>20, :b=>10}]
945
+ @ds.exclude(:a=>(10..15)).all.must_equal [{:a=>20, :b=>10}]
946
+ end
947
+
948
+ it "should work with CASE statements" do
949
+ @ds.insert(20, 10)
950
+ @ds.filter(Sequel.case({{:a=>20}=>20}, 0) > 0).all.must_equal [{:a=>20, :b=>10}]
951
+ @ds.filter(Sequel.case({{:a=>15}=>20}, 0) > 0).all.must_equal []
952
+ @ds.filter(Sequel.case({20=>20}, 0, :a) > 0).all.must_equal [{:a=>20, :b=>10}]
953
+ @ds.filter(Sequel.case({15=>20}, 0, :a) > 0).all.must_equal []
954
+ end
955
+
956
+ it "should work with multiple value arrays" do
957
+ @ds.insert(20, 10)
958
+ @ds.quote_identifiers = false
959
+ @ds.filter([:a, :b]=>[[20, 10]]).all.must_equal [{:a=>20, :b=>10}]
960
+ @ds.filter([:a, :b]=>[[10, 20]]).all.must_equal []
961
+ @ds.filter([:a, :b]=>[[20, 10], [1, 2]]).all.must_equal [{:a=>20, :b=>10}]
962
+ @ds.filter([:a, :b]=>[[10, 10], [20, 20]]).all.must_equal []
963
+
964
+ @ds.exclude([:a, :b]=>[[20, 10]]).all.must_equal []
965
+ @ds.exclude([:a, :b]=>[[10, 20]]).all.must_equal [{:a=>20, :b=>10}]
966
+ @ds.exclude([:a, :b]=>[[20, 10], [1, 2]]).all.must_equal []
967
+ @ds.exclude([:a, :b]=>[[10, 10], [20, 20]]).all.must_equal [{:a=>20, :b=>10}]
968
+ end
969
+
970
+ it "should work with IN/NOT in with datasets" do
971
+ @ds.insert(20, 10)
972
+ ds = @ds.unordered
973
+ @ds.quote_identifiers = false
974
+
975
+ @ds.filter(:a=>ds.select(:a)).all.must_equal [{:a=>20, :b=>10}]
976
+ @ds.filter(:a=>ds.select(:a).where(:a=>15)).all.must_equal []
977
+ @ds.exclude(:a=>ds.select(:a)).all.must_equal []
978
+ @ds.exclude(:a=>ds.select(:a).where(:a=>15)).all.must_equal [{:a=>20, :b=>10}]
979
+
980
+ @ds.filter([:a, :b]=>ds.select(:a, :b)).all.must_equal [{:a=>20, :b=>10}]
981
+ @ds.filter([:a, :b]=>ds.select(:b, :a)).all.must_equal []
982
+ @ds.exclude([:a, :b]=>ds.select(:a, :b)).all.must_equal []
983
+ @ds.exclude([:a, :b]=>ds.select(:b, :a)).all.must_equal [{:a=>20, :b=>10}]
984
+
985
+ @ds.filter([:a, :b]=>ds.select(:a, :b).where(:a=>15)).all.must_equal []
986
+ @ds.exclude([:a, :b]=>ds.select(:a, :b).where(:a=>15)).all.must_equal [{:a=>20, :b=>10}]
987
+ end
988
+
989
+ it "should work empty arrays" do
990
+ @ds.insert(20, 10)
991
+ @ds.filter(:a=>[]).all.must_equal []
992
+ @ds.exclude(:a=>[]).all.must_equal [{:a=>20, :b=>10}]
993
+ @ds.filter([:a, :b]=>[]).all.must_equal []
994
+ @ds.exclude([:a, :b]=>[]).all.must_equal [{:a=>20, :b=>10}]
995
+ end
996
+
997
+ it "should work empty arrays with nulls" do
998
+ @ds = @ds.extension(:empty_array_consider_nulls)
999
+ @ds.insert(nil, nil)
1000
+ @ds.filter(:a=>[]).all.must_equal []
1001
+ @ds.exclude(:a=>[]).all.must_equal []
1002
+ @ds.filter([:a, :b]=>[]).all.must_equal []
1003
+ @ds.exclude([:a, :b]=>[]).all.must_equal []
1004
+
1005
+ pr = proc{|r| r.is_a?(Integer) ? (r != 0) : r}
1006
+ assert_nil(pr[@ds.get(Sequel.expr(:a=>[]))])
1007
+ assert_nil(pr[@ds.get(~Sequel.expr(:a=>[]))])
1008
+ assert_nil(pr[@ds.get(Sequel.expr([:a, :b]=>[]))])
1009
+ assert_nil(pr[@ds.get(~Sequel.expr([:a, :b]=>[]))])
1010
+ end
1011
+
1012
+ it "should work empty arrays with nulls and the empty_array_ignore_nulls extension" do
1013
+ ds = @ds
1014
+ ds.insert(nil, nil)
1015
+ ds.filter(:a=>[]).all.must_equal []
1016
+ ds.exclude(:a=>[]).all.must_equal [{:a=>nil, :b=>nil}]
1017
+ ds.filter([:a, :b]=>[]).all.must_equal []
1018
+ ds.exclude([:a, :b]=>[]).all.must_equal [{:a=>nil, :b=>nil}]
1019
+
1020
+ pr = proc{|r| r.is_a?(Integer) ? (r != 0) : r}
1021
+ pr[ds.get(Sequel.expr(:a=>[]))].must_equal false
1022
+ pr[ds.get(~Sequel.expr(:a=>[]))].must_equal true
1023
+ pr[ds.get(Sequel.expr([:a, :b]=>[]))].must_equal false
1024
+ pr[ds.get(~Sequel.expr([:a, :b]=>[]))].must_equal true
1025
+ end
1026
+
1027
+ it "should work multiple conditions" do
1028
+ @ds.insert(20, 10)
1029
+ @ds.filter(:a=>20, :b=>10).all.must_equal [{:a=>20, :b=>10}]
1030
+ @ds.filter([[:a, 20], [:b, 10]]).all.must_equal [{:a=>20, :b=>10}]
1031
+ @ds.filter({:a=>20}, {:b=>10}).all.must_equal [{:a=>20, :b=>10}]
1032
+ @ds.filter(Sequel.|({:a=>20}, {:b=>5})).all.must_equal [{:a=>20, :b=>10}]
1033
+ @ds.filter(Sequel.~(:a=>10)).all.must_equal [{:a=>20, :b=>10}]
1034
+ end
1035
+ end
1036
+
1037
+ describe "SQL Extract Function" do
1038
+ before do
1039
+ @db = DB
1040
+ @db.create_table!(:a){DateTime :a}
1041
+ @ds = @db[:a].order(:a)
1042
+ end
1043
+ after do
1044
+ @db.drop_table?(:a)
1045
+ end
1046
+
1047
+ it "should return the part of the datetime asked for" do
1048
+ t = Time.now
1049
+ def @ds.supports_timestamp_timezones?() false end
1050
+ @ds.insert(t)
1051
+ @ds.get{a.extract(:year)}.must_equal t.year
1052
+ @ds.get{a.extract(:month)}.must_equal t.month
1053
+ @ds.get{a.extract(:day)}.must_equal t.day
1054
+ @ds.get{a.extract(:hour)}.must_equal t.hour
1055
+ @ds.get{a.extract(:minute)}.must_equal t.min
1056
+ @ds.get{a.extract(:second)}.to_i.must_equal t.sec
1057
+ end
1058
+ end
1059
+
1060
+ describe "Dataset string methods" do
1061
+ before do
1062
+ @db = DB
1063
+ @db.create_table!(:a) do
1064
+ String :a
1065
+ String :b
1066
+ end
1067
+ @ds = @db[:a].order(:a)
1068
+ end
1069
+ after do
1070
+ @db.drop_table?(:a)
1071
+ end
1072
+
1073
+ it "#grep should return matching rows" do
1074
+ @ds.insert('foo', 'bar')
1075
+ @ds.grep(:a, 'foo').all.must_equal [{:a=>'foo', :b=>'bar'}]
1076
+ @ds.grep(:b, 'foo').all.must_equal []
1077
+ @ds.grep(:b, 'bar').all.must_equal [{:a=>'foo', :b=>'bar'}]
1078
+ @ds.grep(:a, 'bar').all.must_equal []
1079
+ @ds.grep([:a, :b], %w'foo bar').all.must_equal [{:a=>'foo', :b=>'bar'}]
1080
+ @ds.grep([:a, :b], %w'boo far').all.must_equal []
1081
+ end
1082
+
1083
+ it "#grep should work with :all_patterns and :all_columns options" do
1084
+ @ds.insert('foo bar', ' ')
1085
+ @ds.insert('foo d', 'bar')
1086
+ @ds.insert('foo e', ' ')
1087
+ @ds.insert(' ', 'bar')
1088
+ @ds.insert('foo f', 'baz')
1089
+ @ds.insert('foo baz', 'bar baz')
1090
+ @ds.insert('foo boo', 'boo foo')
1091
+
1092
+ @ds.grep([:a, :b], %w'%foo% %bar%', :all_patterns=>true).all.must_equal [{:a=>'foo bar', :b=>' '}, {:a=>'foo baz', :b=>'bar baz'}, {:a=>'foo d', :b=>'bar'}]
1093
+ @ds.grep([:a, :b], %w'%foo% %bar% %blob%', :all_patterns=>true).all.must_equal []
1094
+
1095
+ @ds.grep([:a, :b], %w'%bar% %foo%', :all_columns=>true).all.must_equal [{:a=>"foo baz", :b=>"bar baz"}, {:a=>"foo boo", :b=>"boo foo"}, {:a=>"foo d", :b=>"bar"}]
1096
+ @ds.grep([:a, :b], %w'%baz%', :all_columns=>true).all.must_equal [{:a=>'foo baz', :b=>'bar baz'}]
1097
+
1098
+ @ds.grep([:a, :b], %w'%baz% %foo%', :all_columns=>true, :all_patterns=>true).all.must_equal []
1099
+ @ds.grep([:a, :b], %w'%boo% %foo%', :all_columns=>true, :all_patterns=>true).all.must_equal [{:a=>'foo boo', :b=>'boo foo'}]
1100
+ end
1101
+
1102
+ it "#like should return matching rows" do
1103
+ @ds.insert('foo', 'bar')
1104
+ @ds.filter(Sequel.expr(:a).like('foo')).all.must_equal [{:a=>'foo', :b=>'bar'}]
1105
+ @ds.filter(Sequel.expr(:a).like('bar')).all.must_equal []
1106
+ @ds.filter(Sequel.expr(:a).like('foo', 'bar')).all.must_equal [{:a=>'foo', :b=>'bar'}]
1107
+ @ds.exclude(Sequel.expr(:a).like('foo')).all.must_equal []
1108
+ @ds.exclude(Sequel.expr(:a).like('bar')).all.must_equal [{:a=>'foo', :b=>'bar'}]
1109
+ @ds.exclude(Sequel.expr(:a).like('foo', 'bar')).all.must_equal []
1110
+ end
1111
+
1112
+ it "#like should be case sensitive" do
1113
+ @ds.insert('foo', 'bar')
1114
+ @ds.filter(Sequel.expr(:a).like('Foo')).all.must_equal []
1115
+ @ds.filter(Sequel.expr(:b).like('baR')).all.must_equal []
1116
+ @ds.filter(Sequel.expr(:a).like('FOO', 'BAR')).all.must_equal []
1117
+ @ds.exclude(Sequel.expr(:a).like('Foo')).all.must_equal [{:a=>'foo', :b=>'bar'}]
1118
+ @ds.exclude(Sequel.expr(:b).like('baR')).all.must_equal [{:a=>'foo', :b=>'bar'}]
1119
+ @ds.exclude(Sequel.expr(:a).like('FOO', 'BAR')).all.must_equal [{:a=>'foo', :b=>'bar'}]
1120
+ end
1121
+
1122
+ it "#ilike should return matching rows, in a case insensitive manner" do
1123
+ @ds.insert('foo', 'bar')
1124
+ @ds.filter(Sequel.expr(:a).ilike('Foo')).all.must_equal [{:a=>'foo', :b=>'bar'}]
1125
+ @ds.filter(Sequel.expr(:a).ilike('baR')).all.must_equal []
1126
+ @ds.filter(Sequel.expr(:a).ilike('FOO', 'BAR')).all.must_equal [{:a=>'foo', :b=>'bar'}]
1127
+ @ds.exclude(Sequel.expr(:a).ilike('Foo')).all.must_equal []
1128
+ @ds.exclude(Sequel.expr(:a).ilike('baR')).all.must_equal [{:a=>'foo', :b=>'bar'}]
1129
+ @ds.exclude(Sequel.expr(:a).ilike('FOO', 'BAR')).all.must_equal []
1130
+ end
1131
+
1132
+ it "#like with regexp return matching rows" do
1133
+ @ds.insert('foo', 'bar')
1134
+ @ds.filter(Sequel.expr(:a).like(/fo/)).all.must_equal [{:a=>'foo', :b=>'bar'}]
1135
+ @ds.filter(Sequel.expr(:a).like(/fo$/)).all.must_equal []
1136
+ @ds.filter(Sequel.expr(:a).like(/fo/, /ar/)).all.must_equal [{:a=>'foo', :b=>'bar'}]
1137
+ @ds.exclude(Sequel.expr(:a).like(/fo/)).all.must_equal []
1138
+ @ds.exclude(Sequel.expr(:a).like(/fo$/)).all.must_equal [{:a=>'foo', :b=>'bar'}]
1139
+ @ds.exclude(Sequel.expr(:a).like(/fo/, /ar/)).all.must_equal []
1140
+ end
1141
+
1142
+ it "#like with regexp should be case sensitive if regexp is case sensitive" do
1143
+ @ds.insert('foo', 'bar')
1144
+ @ds.filter(Sequel.expr(:a).like(/Fo/)).all.must_equal []
1145
+ @ds.filter(Sequel.expr(:b).like(/baR/)).all.must_equal []
1146
+ @ds.filter(Sequel.expr(:a).like(/FOO/, /BAR/)).all.must_equal []
1147
+ @ds.exclude(Sequel.expr(:a).like(/Fo/)).all.must_equal [{:a=>'foo', :b=>'bar'}]
1148
+ @ds.exclude(Sequel.expr(:b).like(/baR/)).all.must_equal [{:a=>'foo', :b=>'bar'}]
1149
+ @ds.exclude(Sequel.expr(:a).like(/FOO/, /BAR/)).all.must_equal [{:a=>'foo', :b=>'bar'}]
1150
+
1151
+ @ds.filter(Sequel.expr(:a).like(/Fo/i)).all.must_equal [{:a=>'foo', :b=>'bar'}]
1152
+ @ds.filter(Sequel.expr(:b).like(/baR/i)).all.must_equal [{:a=>'foo', :b=>'bar'}]
1153
+ @ds.filter(Sequel.expr(:a).like(/FOO/i, /BAR/i)).all.must_equal [{:a=>'foo', :b=>'bar'}]
1154
+ @ds.exclude(Sequel.expr(:a).like(/Fo/i)).all.must_equal []
1155
+ @ds.exclude(Sequel.expr(:b).like(/baR/i)).all.must_equal []
1156
+ @ds.exclude(Sequel.expr(:a).like(/FOO/i, /BAR/i)).all.must_equal []
1157
+ end
1158
+
1159
+ it "#ilike with regexp should return matching rows, in a case insensitive manner" do
1160
+ @ds.insert('foo', 'bar')
1161
+ @ds.filter(Sequel.expr(:a).ilike(/Fo/)).all.must_equal [{:a=>'foo', :b=>'bar'}]
1162
+ @ds.filter(Sequel.expr(:b).ilike(/baR/)).all.must_equal [{:a=>'foo', :b=>'bar'}]
1163
+ @ds.filter(Sequel.expr(:a).ilike(/FOO/, /BAR/)).all.must_equal [{:a=>'foo', :b=>'bar'}]
1164
+ @ds.exclude(Sequel.expr(:a).ilike(/Fo/)).all.must_equal []
1165
+ @ds.exclude(Sequel.expr(:b).ilike(/baR/)).all.must_equal []
1166
+ @ds.exclude(Sequel.expr(:a).ilike(/FOO/, /BAR/)).all.must_equal []
1167
+ end
1168
+
1169
+ it "should work with strings created with Sequel.join" do
1170
+ @ds.insert('foo', 'bar')
1171
+ @ds.get(Sequel.join([:a, "bar"])).must_equal 'foobar'
1172
+ @ds.get(Sequel.join(["foo", :b], ' ')).must_equal 'foo bar'
1173
+ end
1174
+ end
1175
+
1176
+ describe "Dataset identifier methods" do
1177
+ before(:all) do
1178
+ class ::String
1179
+ def uprev
1180
+ upcase.reverse
1181
+ end
1182
+ end
1183
+ @db = DB
1184
+ @db.create_table!(:a){Integer :ab}
1185
+ @db[:a].insert(1)
1186
+ end
1187
+ before do
1188
+ @ds = @db[:a].order(:ab)
1189
+ end
1190
+ after(:all) do
1191
+ @db.drop_table?(:a)
1192
+ end
1193
+
1194
+ it "#identifier_output_method should change how identifiers are output" do
1195
+ @ds.identifier_output_method = :upcase
1196
+ @ds.first.must_equal(:AB=>1)
1197
+ @ds.identifier_output_method = :uprev
1198
+ @ds.first.must_equal(:BA=>1)
1199
+ end
1200
+
1201
+ it "should work with a nil identifier_output_method" do
1202
+ @ds.identifier_output_method = nil
1203
+ [{:ab=>1}, {:AB=>1}].must_include(@ds.first)
1204
+ end
1205
+
1206
+ it "should work when not quoting identifiers" do
1207
+ @ds.quote_identifiers = false
1208
+ @ds.first.must_equal(:ab=>1)
1209
+ end
1210
+ end
1211
+
1212
+ describe "Dataset defaults and overrides" do
1213
+ before do
1214
+ @db = DB
1215
+ @db.create_table!(:a){Integer :a}
1216
+ @ds = @db[:a].order(:a).extension(:set_overrides)
1217
+ end
1218
+ after do
1219
+ @db.drop_table?(:a)
1220
+ end
1221
+
1222
+ it "#set_defaults should set defaults that can be overridden" do
1223
+ @ds = @ds.set_defaults(:a=>10)
1224
+ @ds.insert
1225
+ @ds.insert(:a=>20)
1226
+ @ds.all.must_equal [{:a=>10}, {:a=>20}]
1227
+ end
1228
+
1229
+ it "#set_overrides should set defaults that cannot be overridden" do
1230
+ @ds = @ds.set_overrides(:a=>10)
1231
+ @ds.insert
1232
+ @ds.insert(:a=>20)
1233
+ @ds.all.must_equal [{:a=>10}, {:a=>10}]
1234
+ end
1235
+ end
1236
+
1237
+ describe "Emulated functions" do
1238
+ before do
1239
+ @db = DB
1240
+ @db.create_table!(:a){String :a}
1241
+ @ds = @db[:a]
1242
+ end
1243
+ after do
1244
+ @db.drop_table?(:a)
1245
+ end
1246
+
1247
+ it "Sequel.char_length should return the length of characters in the string" do
1248
+ assert_nil(@ds.get(Sequel.char_length(:a)))
1249
+ @ds.insert(:a=>'foo')
1250
+ @ds.get(Sequel.char_length(:a)).must_equal 3
1251
+ end
1252
+
1253
+ it "Sequel.char_length should return the length of characters in the string including trailing blanks" do
1254
+ @ds.insert(:a=>' foo22 ')
1255
+ @ds.get(Sequel.char_length(:a)).must_equal 7
1256
+ end
1257
+
1258
+ it "Sequel.trim should return the string with spaces trimmed from both sides" do
1259
+ assert_nil(@ds.get(Sequel.trim(:a)))
1260
+ @ds.insert(:a=>'foo')
1261
+ @ds.get(Sequel.trim(:a)).must_equal 'foo'
1262
+ end
1263
+
1264
+ it "Sequel.trim should return the string with spaces trimmed from both sides" do
1265
+ @ds.insert(:a=>' foo22 ')
1266
+ @ds.get(Sequel.trim(:a)).must_equal 'foo22'
1267
+ end
1268
+ end