sequel_core 2.2.0 → 3.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. metadata +30 -101
  2. data/CHANGELOG +0 -1519
  3. data/COPYING +0 -19
  4. data/README +0 -313
  5. data/Rakefile +0 -158
  6. data/bin/sequel +0 -117
  7. data/doc/cheat_sheet.rdoc +0 -225
  8. data/doc/dataset_filtering.rdoc +0 -182
  9. data/lib/sequel_core.rb +0 -136
  10. data/lib/sequel_core/adapters/adapter_skeleton.rb +0 -68
  11. data/lib/sequel_core/adapters/ado.rb +0 -90
  12. data/lib/sequel_core/adapters/db2.rb +0 -160
  13. data/lib/sequel_core/adapters/dbi.rb +0 -127
  14. data/lib/sequel_core/adapters/informix.rb +0 -89
  15. data/lib/sequel_core/adapters/jdbc.rb +0 -110
  16. data/lib/sequel_core/adapters/mysql.rb +0 -486
  17. data/lib/sequel_core/adapters/odbc.rb +0 -167
  18. data/lib/sequel_core/adapters/odbc_mssql.rb +0 -106
  19. data/lib/sequel_core/adapters/openbase.rb +0 -76
  20. data/lib/sequel_core/adapters/oracle.rb +0 -182
  21. data/lib/sequel_core/adapters/postgres.rb +0 -560
  22. data/lib/sequel_core/adapters/sqlite.rb +0 -270
  23. data/lib/sequel_core/connection_pool.rb +0 -194
  24. data/lib/sequel_core/core_ext.rb +0 -197
  25. data/lib/sequel_core/core_sql.rb +0 -184
  26. data/lib/sequel_core/database.rb +0 -462
  27. data/lib/sequel_core/database/schema.rb +0 -156
  28. data/lib/sequel_core/dataset.rb +0 -457
  29. data/lib/sequel_core/dataset/callback.rb +0 -13
  30. data/lib/sequel_core/dataset/convenience.rb +0 -245
  31. data/lib/sequel_core/dataset/pagination.rb +0 -96
  32. data/lib/sequel_core/dataset/query.rb +0 -41
  33. data/lib/sequel_core/dataset/schema.rb +0 -15
  34. data/lib/sequel_core/dataset/sql.rb +0 -889
  35. data/lib/sequel_core/deprecated.rb +0 -26
  36. data/lib/sequel_core/exceptions.rb +0 -42
  37. data/lib/sequel_core/migration.rb +0 -187
  38. data/lib/sequel_core/object_graph.rb +0 -216
  39. data/lib/sequel_core/pretty_table.rb +0 -71
  40. data/lib/sequel_core/schema.rb +0 -2
  41. data/lib/sequel_core/schema/generator.rb +0 -239
  42. data/lib/sequel_core/schema/sql.rb +0 -326
  43. data/lib/sequel_core/sql.rb +0 -812
  44. data/lib/sequel_core/worker.rb +0 -68
  45. data/spec/adapters/informix_spec.rb +0 -96
  46. data/spec/adapters/mysql_spec.rb +0 -765
  47. data/spec/adapters/oracle_spec.rb +0 -222
  48. data/spec/adapters/postgres_spec.rb +0 -441
  49. data/spec/adapters/sqlite_spec.rb +0 -413
  50. data/spec/connection_pool_spec.rb +0 -363
  51. data/spec/core_ext_spec.rb +0 -156
  52. data/spec/core_sql_spec.rb +0 -427
  53. data/spec/database_spec.rb +0 -963
  54. data/spec/dataset_spec.rb +0 -2933
  55. data/spec/expression_filters_spec.rb +0 -316
  56. data/spec/migration_spec.rb +0 -261
  57. data/spec/object_graph_spec.rb +0 -230
  58. data/spec/pretty_table_spec.rb +0 -58
  59. data/spec/rcov.opts +0 -6
  60. data/spec/schema_generator_spec.rb +0 -122
  61. data/spec/schema_spec.rb +0 -422
  62. data/spec/spec.opts +0 -0
  63. data/spec/spec_config.rb +0 -7
  64. data/spec/spec_config.rb.example +0 -8
  65. data/spec/spec_helper.rb +0 -55
  66. data/spec/worker_spec.rb +0 -96
@@ -1,68 +0,0 @@
1
- module Sequel
2
- # A Worker is a thread that accepts jobs off a work queue and
3
- # processes them in the background. It accepts an optional
4
- # database where it wruns all of its work inside a transaction.
5
- class Worker < Thread
6
- attr_reader :queue
7
- attr_reader :errors
8
-
9
- # Setup the interal variables. If a database is given,
10
- # run the thread inside a database transaction. Continue
11
- # to work until #join is called.
12
- def initialize(db = nil)
13
- @queue = Queue.new
14
- @errors = []
15
- t = self
16
- t.abort_on_exception = true
17
- @transaction = !db.nil?
18
- db ? super {db.transaction {t.work}} : super {t.work}
19
- end
20
-
21
- # Add a job to the queue, specified either as a proc argument
22
- # or as a block.
23
- def async(proc = nil, &block)
24
- @queue << (proc || block)
25
- self
26
- end
27
- alias_method :add, :async
28
- alias_method :<<, :async
29
-
30
- # Whether the worker is actively working and/or has work scheduled
31
- def busy?
32
- @cur || !@queue.empty?
33
- end
34
-
35
- # Wait until the worker is no longer busy and then stop working.
36
- def join
37
- sleep(0.1) while busy?
38
- self.raise Error::WorkerStop
39
- super
40
- end
41
-
42
- # Continually get jobs from the work queue and process them.
43
- def work
44
- begin
45
- loop {next_job}
46
- rescue Sequel::Error::WorkerStop # signals the worker thread to stop
47
- ensure
48
- raise Sequel::Error::Rollback if @transaction && !@errors.empty?
49
- end
50
- end
51
-
52
- private
53
-
54
- # Get the next job from the work queue and process it.
55
- def next_job
56
- begin
57
- @cur = @queue.pop
58
- @cur.call
59
- rescue Error::WorkerStop => e
60
- raise e
61
- rescue Exception => e
62
- @errors << e
63
- ensure
64
- @cur = nil
65
- end
66
- end
67
- end
68
- end
@@ -1,96 +0,0 @@
1
- require File.join(File.dirname(__FILE__), '../spec_helper.rb')
2
-
3
- unless defined?(INFORMIX_DB)
4
- INFORMIX_DB = Sequel.connect('informix://localhost/mydb')
5
- end
6
-
7
- if INFORMIX_DB.table_exists?(:test)
8
- INFORMIX_DB.drop_table :test
9
- end
10
- INFORMIX_DB.create_table :test do
11
- text :name
12
- integer :value
13
-
14
- index :value
15
- end
16
-
17
- context "A Informix database" do
18
- specify "should provide disconnect functionality" do
19
- INFORMIX_DB.execute("select user from dual")
20
- INFORMIX_DB.pool.size.should == 1
21
- INFORMIX_DB.disconnect
22
- INFORMIX_DB.pool.size.should == 0
23
- end
24
- end
25
-
26
- context "A Informix dataset" do
27
- setup do
28
- @d = INFORMIX_DB[:test]
29
- @d.delete # remove all records
30
- end
31
-
32
- specify "should return the correct record count" do
33
- @d.count.should == 0
34
- @d << {:name => 'abc', :value => 123}
35
- @d << {:name => 'abc', :value => 456}
36
- @d << {:name => 'def', :value => 789}
37
- @d.count.should == 3
38
- end
39
-
40
- specify "should return the correct records" do
41
- @d.to_a.should == []
42
- @d << {:name => 'abc', :value => 123}
43
- @d << {:name => 'abc', :value => 456}
44
- @d << {:name => 'def', :value => 789}
45
-
46
- @d.order(:value).to_a.should == [
47
- {:name => 'abc', :value => 123},
48
- {:name => 'abc', :value => 456},
49
- {:name => 'def', :value => 789}
50
- ]
51
- end
52
-
53
- specify "should update records correctly" do
54
- @d << {:name => 'abc', :value => 123}
55
- @d << {:name => 'abc', :value => 456}
56
- @d << {:name => 'def', :value => 789}
57
- @d.filter(:name => 'abc').update(:value => 530)
58
-
59
- # the third record should stay the same
60
- # floating-point precision bullshit
61
- @d[:name => 'def'][:value].should == 789
62
- @d.filter(:value => 530).count.should == 2
63
- end
64
-
65
- specify "should delete records correctly" do
66
- @d << {:name => 'abc', :value => 123}
67
- @d << {:name => 'abc', :value => 456}
68
- @d << {:name => 'def', :value => 789}
69
- @d.filter(:name => 'abc').delete
70
-
71
- @d.count.should == 1
72
- @d.first[:name].should == 'def'
73
- end
74
-
75
- specify "should be able to literalize booleans" do
76
- proc {@d.literal(true)}.should_not raise_error
77
- proc {@d.literal(false)}.should_not raise_error
78
- end
79
-
80
- specify "should support transactions" do
81
- INFORMIX_DB.transaction do
82
- @d << {:name => 'abc', :value => 1}
83
- end
84
-
85
- @d.count.should == 1
86
- end
87
-
88
- specify "should support #first and #last" do
89
- @d << {:name => 'abc', :value => 123}
90
- @d << {:name => 'abc', :value => 456}
91
- @d << {:name => 'def', :value => 789}
92
-
93
- @d.order(:value).first.should == {:name => 'abc', :value => 123}
94
- @d.order(:value).last.should == {:name => 'def', :value => 789}
95
- end
96
- end
@@ -1,765 +0,0 @@
1
- require File.join(File.dirname(__FILE__), '../spec_helper.rb')
2
-
3
- unless defined?(MYSQL_USER)
4
- MYSQL_USER = 'root'
5
- end
6
- unless defined?(MYSQL_DB)
7
- MYSQL_URL = (ENV['SEQUEL_MY_SPEC_DB']||"mysql://#{MYSQL_USER}@localhost/sandbox") unless defined? MYSQL_URL
8
- MYSQL_DB = Sequel.connect(MYSQL_URL)
9
- end
10
- unless defined?(MYSQL_SOCKET_FILE)
11
- MYSQL_SOCKET_FILE = '/tmp/mysql.sock'
12
- end
13
-
14
- MYSQL_URI = URI.parse(MYSQL_DB.uri)
15
- MYSQL_DB_NAME = (m = /\/(.*)/.match(MYSQL_URI.path)) && m[1]
16
-
17
- MYSQL_DB.create_table! :items do
18
- text :name
19
- integer :value, :index => true
20
- end
21
- MYSQL_DB.create_table! :test2 do
22
- text :name
23
- integer :value
24
- end
25
- MYSQL_DB.create_table! :booltest do
26
- tinyint :value
27
- end
28
- class Sequel::MySQL::Database
29
- attr_accessor :sqls
30
- end
31
- logger = Object.new
32
- def logger.method_missing(m, msg)
33
- MYSQL_DB.sqls << msg
34
- end
35
- MYSQL_DB.logger = logger
36
- MYSQL_DB.sqls = []
37
-
38
- context "A MySQL database" do
39
- setup do
40
- @db = MYSQL_DB
41
- end
42
- teardown do
43
- Sequel.convert_tinyint_to_bool = true
44
- end
45
-
46
- specify "should provide disconnect functionality" do
47
- @db.tables
48
- @db.pool.size.should == 1
49
- @db.disconnect
50
- @db.pool.size.should == 0
51
- end
52
-
53
- specify "should provide the server version" do
54
- @db.server_version.should >= 40000
55
- end
56
-
57
- specify "should support sequential primary keys" do
58
- @db.create_table!(:with_pk) {primary_key :id; text :name}
59
- @db[:with_pk] << {:name => 'abc'}
60
- @db[:with_pk] << {:name => 'def'}
61
- @db[:with_pk] << {:name => 'ghi'}
62
- @db[:with_pk].order(:name).all.should == [
63
- {:id => 1, :name => 'abc'},
64
- {:id => 2, :name => 'def'},
65
- {:id => 3, :name => 'ghi'}
66
- ]
67
- end
68
-
69
- specify "should convert Mysql::Errors to Sequel::Errors" do
70
- proc{@db << "SELECT 1 + blah;"}.should raise_error(Sequel::Error)
71
- end
72
-
73
- specify "should correctly parse the schema" do
74
- @db.schema(:booltest, :reload=>true).should == [[:value, {:type=>:boolean, :allow_null=>true, :max_chars=>nil, :default=>nil, :db_type=>"tinyint", :numeric_precision=>3}]]
75
-
76
- Sequel.convert_tinyint_to_bool = false
77
- @db.schema(:booltest, :reload=>true).should == [[:value, {:type=>:integer, :allow_null=>true, :max_chars=>nil, :default=>nil, :db_type=>"tinyint", :numeric_precision=>3}]]
78
- end
79
-
80
- specify "should get the schema all database tables if no table name is used" do
81
- @db.schema(:booltest, :reload=>true).should == @db.schema(nil, :reload=>true)[:booltest]
82
- end
83
- end
84
-
85
- context "A MySQL dataset" do
86
- setup do
87
- @d = MYSQL_DB[:items]
88
- @d.delete # remove all records
89
- MYSQL_DB.sqls.clear
90
- end
91
-
92
- specify "should return the correct record count" do
93
- @d.count.should == 0
94
- @d << {:name => 'abc', :value => 123}
95
- @d << {:name => 'abc', :value => 456}
96
- @d << {:name => 'def', :value => 789}
97
- @d.count.should == 3
98
- end
99
-
100
- specify "should return the correct records" do
101
- @d.to_a.should == []
102
- @d << {:name => 'abc', :value => 123}
103
- @d << {:name => 'abc', :value => 456}
104
- @d << {:name => 'def', :value => 789}
105
-
106
- @d.order(:value).to_a.should == [
107
- {:name => 'abc', :value => 123},
108
- {:name => 'abc', :value => 456},
109
- {:name => 'def', :value => 789}
110
- ]
111
- end
112
-
113
- specify "should update records correctly" do
114
- @d << {:name => 'abc', :value => 123}
115
- @d << {:name => 'abc', :value => 456}
116
- @d << {:name => 'def', :value => 789}
117
- @d.filter(:name => 'abc').update(:value => 530)
118
-
119
- # the third record should stay the same
120
- # floating-point precision bullshit
121
- @d[:name => 'def'][:value].should == 789
122
- @d.filter(:value => 530).count.should == 2
123
- end
124
-
125
- specify "should delete records correctly" do
126
- @d << {:name => 'abc', :value => 123}
127
- @d << {:name => 'abc', :value => 456}
128
- @d << {:name => 'def', :value => 789}
129
- @d.filter(:name => 'abc').delete
130
-
131
- @d.count.should == 1
132
- @d.first[:name].should == 'def'
133
- end
134
-
135
- specify "should be able to literalize booleans" do
136
- proc {@d.literal(true)}.should_not raise_error
137
- proc {@d.literal(false)}.should_not raise_error
138
- end
139
-
140
- specify "should quote columns and tables using back-ticks if quoting identifiers" do
141
- @d.quote_identifiers = true
142
- @d.select(:name).sql.should == \
143
- 'SELECT `name` FROM `items`'
144
-
145
- @d.select('COUNT(*)'.lit).sql.should == \
146
- 'SELECT COUNT(*) FROM `items`'
147
-
148
- @d.select(:max[:value]).sql.should == \
149
- 'SELECT max(`value`) FROM `items`'
150
-
151
- @d.select(:NOW[]).sql.should == \
152
- 'SELECT NOW() FROM `items`'
153
-
154
- @d.select(:max[:items__value]).sql.should == \
155
- 'SELECT max(`items`.`value`) FROM `items`'
156
-
157
- @d.order(:name.desc).sql.should == \
158
- 'SELECT * FROM `items` ORDER BY `name` DESC'
159
-
160
- @d.select('items.name AS item_name'.lit).sql.should == \
161
- 'SELECT items.name AS item_name FROM `items`'
162
-
163
- @d.select('`name`'.lit).sql.should == \
164
- 'SELECT `name` FROM `items`'
165
-
166
- @d.select('max(items.`name`) AS `max_name`'.lit).sql.should == \
167
- 'SELECT max(items.`name`) AS `max_name` FROM `items`'
168
-
169
- @d.select(:test[:abc, 'hello']).sql.should == \
170
- "SELECT test(`abc`, 'hello') FROM `items`"
171
-
172
- @d.select(:test[:abc__def, 'hello']).sql.should == \
173
- "SELECT test(`abc`.`def`, 'hello') FROM `items`"
174
-
175
- @d.select(:test[:abc__def, 'hello'].as(:x2)).sql.should == \
176
- "SELECT test(`abc`.`def`, 'hello') AS `x2` FROM `items`"
177
-
178
- @d.insert_sql(:value => 333).should == \
179
- 'INSERT INTO `items` (`value`) VALUES (333)'
180
-
181
- @d.insert_sql(:x => :y).should == \
182
- 'INSERT INTO `items` (`x`) VALUES (`y`)'
183
- end
184
-
185
- specify "should quote fields correctly when reversing the order" do
186
- @d.quote_identifiers = true
187
- @d.reverse_order(:name).sql.should == \
188
- 'SELECT * FROM `items` ORDER BY `name` DESC'
189
-
190
- @d.reverse_order(:name.desc).sql.should == \
191
- 'SELECT * FROM `items` ORDER BY `name` ASC'
192
-
193
- @d.reverse_order(:name, :test.desc).sql.should == \
194
- 'SELECT * FROM `items` ORDER BY `name` DESC, `test` ASC'
195
-
196
- @d.reverse_order(:name.desc, :test).sql.should == \
197
- 'SELECT * FROM `items` ORDER BY `name` ASC, `test` DESC'
198
- end
199
-
200
- specify "should support ORDER clause in UPDATE statements" do
201
- @d.order(:name).update_sql(:value => 1).should == \
202
- 'UPDATE items SET value = 1 ORDER BY name'
203
- end
204
-
205
- specify "should support LIMIT clause in UPDATE statements" do
206
- @d.limit(10).update_sql(:value => 1).should == \
207
- 'UPDATE items SET value = 1 LIMIT 10'
208
- end
209
-
210
- specify "should support transactions" do
211
- MYSQL_DB.transaction do
212
- @d << {:name => 'abc', :value => 1}
213
- end
214
-
215
- @d.count.should == 1
216
- end
217
-
218
- specify "should correctly rollback transactions" do
219
- proc do
220
- MYSQL_DB.transaction do
221
- @d << {:name => 'abc'}
222
- raise Interrupt, 'asdf'
223
- end
224
- end.should raise_error(Interrupt)
225
-
226
- MYSQL_DB.sqls.should == ['BEGIN', "INSERT INTO items (name) VALUES ('abc')", 'ROLLBACK']
227
- end
228
-
229
- specify "should handle returning inside of the block by committing" do
230
- def MYSQL_DB.ret_commit
231
- transaction do
232
- self[:items] << {:name => 'abc'}
233
- return
234
- self[:items] << {:name => 'd'}
235
- end
236
- end
237
- MYSQL_DB.ret_commit
238
- MYSQL_DB.sqls.should == ['BEGIN', "INSERT INTO items (name) VALUES ('abc')", 'COMMIT']
239
- end
240
-
241
- specify "should support regexps" do
242
- @d << {:name => 'abc', :value => 1}
243
- @d << {:name => 'bcd', :value => 2}
244
- @d.filter(:name => /bc/).count.should == 2
245
- @d.filter(:name => /^bc/).count.should == 1
246
- end
247
-
248
- specify "should correctly literalize strings with comment backslashes in them" do
249
- @d.delete
250
- proc {@d << {:name => ':\\'}}.should_not raise_error
251
-
252
- @d.first[:name].should == ':\\'
253
- end
254
- end
255
-
256
- context "MySQL datasets" do
257
- setup do
258
- @d = MYSQL_DB[:orders]
259
- end
260
- teardown do
261
- Sequel.convert_tinyint_to_bool = true
262
- end
263
-
264
- specify "should correctly quote column references" do
265
- @d.quote_identifiers = true
266
- market = 'ICE'
267
- ack_stamp = Time.now - 15 * 60 # 15 minutes ago
268
- @d.query do
269
- select :market, :minute[:from_unixtime[:ack]].as(:minute)
270
- where {(:ack > ack_stamp) & {:market => market}}
271
- group_by :minute[:from_unixtime[:ack]]
272
- end.sql.should == \
273
- "SELECT `market`, minute(from_unixtime(`ack`)) AS `minute` FROM `orders` WHERE ((`ack` > #{@d.literal(ack_stamp)}) AND (`market` = 'ICE')) GROUP BY minute(from_unixtime(`ack`))"
274
- end
275
-
276
- specify "should accept and return tinyints as bools or integers when configured to do so" do
277
- MYSQL_DB[:booltest].delete
278
- MYSQL_DB[:booltest] << {:value=>true}
279
- MYSQL_DB[:booltest].all.should == [{:value=>true}]
280
- MYSQL_DB[:booltest].delete
281
- MYSQL_DB[:booltest] << {:value=>false}
282
- MYSQL_DB[:booltest].all.should == [{:value=>false}]
283
-
284
- Sequel.convert_tinyint_to_bool = false
285
- MYSQL_DB[:booltest].delete
286
- MYSQL_DB[:booltest] << {:value=>true}
287
- MYSQL_DB[:booltest].all.should == [{:value=>1}]
288
- MYSQL_DB[:booltest].delete
289
- MYSQL_DB[:booltest] << {:value=>false}
290
- MYSQL_DB[:booltest].all.should == [{:value=>0}]
291
-
292
- MYSQL_DB[:booltest].delete
293
- MYSQL_DB[:booltest] << {:value=>1}
294
- MYSQL_DB[:booltest].all.should == [{:value=>1}]
295
- MYSQL_DB[:booltest].delete
296
- MYSQL_DB[:booltest] << {:value=>0}
297
- MYSQL_DB[:booltest].all.should == [{:value=>0}]
298
- end
299
- end
300
-
301
- # # Commented out because it was causing subsequent examples to fail for some reason
302
- # context "Simple stored procedure test" do
303
- # setup do
304
- # # Create a simple stored procedure but drop it first if there
305
- # MYSQL_DB.execute("DROP PROCEDURE IF EXISTS sp_get_server_id;")
306
- # MYSQL_DB.execute("CREATE PROCEDURE sp_get_server_id() SQL SECURITY DEFINER SELECT @@SERVER_ID as server_id;")
307
- # end
308
- #
309
- # specify "should return the server-id via a stored procedure call" do
310
- # @server_id = MYSQL_DB["SELECT @@SERVER_ID as server_id;"].first[:server_id] # grab the server_id via a simple query
311
- # @server_id_by_sp = MYSQL_DB["CALL sp_get_server_id();"].first[:server_id]
312
- # @server_id_by_sp.should == @server_id # compare it to output from stored procedure
313
- # end
314
- # end
315
- #
316
- context "MySQL join expressions" do
317
- setup do
318
- @ds = MYSQL_DB[:nodes]
319
- @ds.db.meta_def(:server_version) {50014}
320
- end
321
-
322
- specify "should raise error for :full_outer join requests." do
323
- lambda{@ds.join_table(:full_outer, :nodes)}.should raise_error(Sequel::Error::InvalidJoinType)
324
- end
325
- specify "should support natural left joins" do
326
- @ds.join_table(:natural_left, :nodes).sql.should == \
327
- 'SELECT * FROM nodes NATURAL LEFT JOIN nodes'
328
- end
329
- specify "should support natural right joins" do
330
- @ds.join_table(:natural_right, :nodes).sql.should == \
331
- 'SELECT * FROM nodes NATURAL RIGHT JOIN nodes'
332
- end
333
- specify "should support natural left outer joins" do
334
- @ds.join_table(:natural_left_outer, :nodes).sql.should == \
335
- 'SELECT * FROM nodes NATURAL LEFT OUTER JOIN nodes'
336
- end
337
- specify "should support natural right outer joins" do
338
- @ds.join_table(:natural_right_outer, :nodes).sql.should == \
339
- 'SELECT * FROM nodes NATURAL RIGHT OUTER JOIN nodes'
340
- end
341
- specify "should support natural inner joins" do
342
- @ds.join_table(:natural_inner, :nodes).sql.should == \
343
- 'SELECT * FROM nodes NATURAL LEFT JOIN nodes'
344
- end
345
- specify "should support cross joins" do
346
- @ds.join_table(:cross, :nodes).sql.should == \
347
- 'SELECT * FROM nodes CROSS JOIN nodes'
348
- end
349
- specify "should support cross joins as inner joins if conditions are used" do
350
- @ds.join_table(:cross, :nodes, :id=>:id).sql.should == \
351
- 'SELECT * FROM nodes INNER JOIN nodes ON (nodes.id = nodes.id)'
352
- end
353
- specify "should support straight joins (force left table to be read before right)" do
354
- @ds.join_table(:straight, :nodes).sql.should == \
355
- 'SELECT * FROM nodes STRAIGHT_JOIN nodes'
356
- end
357
- specify "should support natural joins on multiple tables." do
358
- @ds.join_table(:natural_left_outer, [:nodes, :branches]).sql.should == \
359
- 'SELECT * FROM nodes NATURAL LEFT OUTER JOIN (nodes, branches)'
360
- end
361
- specify "should support straight joins on multiple tables." do
362
- @ds.join_table(:straight, [:nodes,:branches]).sql.should == \
363
- 'SELECT * FROM nodes STRAIGHT_JOIN (nodes, branches)'
364
- end
365
- end
366
-
367
- context "Joined MySQL dataset" do
368
- setup do
369
- @ds = MYSQL_DB[:nodes]
370
- end
371
-
372
- specify "should quote fields correctly" do
373
- @ds.quote_identifiers = true
374
- @ds.join(:attributes, :node_id => :id).sql.should == \
375
- "SELECT * FROM `nodes` INNER JOIN `attributes` ON (`attributes`.`node_id` = `nodes`.`id`)"
376
- end
377
-
378
- specify "should allow a having clause on ungrouped datasets" do
379
- proc {@ds.having('blah')}.should_not raise_error
380
-
381
- @ds.having('blah').sql.should == \
382
- "SELECT * FROM nodes HAVING (blah)"
383
- end
384
-
385
- specify "should put a having clause before an order by clause" do
386
- @ds.order(:aaa).having(:bbb => :ccc).sql.should == \
387
- "SELECT * FROM nodes HAVING (bbb = ccc) ORDER BY aaa"
388
- end
389
- end
390
-
391
- context "A MySQL database" do
392
- setup do
393
- @db = MYSQL_DB
394
- end
395
-
396
- specify "should support add_column operations" do
397
- @db.add_column :test2, :xyz, :text
398
-
399
- @db[:test2].columns.should == [:name, :value, :xyz]
400
- @db[:test2] << {:name => 'mmm', :value => 111, :xyz => '000'}
401
- @db[:test2].first[:xyz].should == '000'
402
- end
403
-
404
- specify "should support drop_column operations" do
405
- @db[:test2].columns.should == [:name, :value, :xyz]
406
- @db.drop_column :test2, :xyz
407
-
408
- @db[:test2].columns.should == [:name, :value]
409
- end
410
-
411
- specify "should support rename_column operations" do
412
- @db[:test2].delete
413
- @db.add_column :test2, :xyz, :text
414
- @db[:test2] << {:name => 'mmm', :value => 111, :xyz => 'qqqq'}
415
-
416
- @db[:test2].columns.should == [:name, :value, :xyz]
417
- @db.rename_column :test2, :xyz, :zyx, :type => :text
418
- @db[:test2].columns.should == [:name, :value, :zyx]
419
- @db[:test2].first[:zyx].should == 'qqqq'
420
- end
421
-
422
- specify "should support rename_column operations with types like varchar(255)" do
423
- @db[:test2].delete
424
- @db.add_column :test2, :tre, :text
425
- @db[:test2] << {:name => 'mmm', :value => 111, :tre => 'qqqq'}
426
-
427
- @db[:test2].columns.should == [:name, :value, :zyx, :tre]
428
- @db.rename_column :test2, :tre, :ert, :type => :varchar[255]
429
- @db[:test2].columns.should == [:name, :value, :zyx, :ert]
430
- @db[:test2].first[:ert].should == 'qqqq'
431
- end
432
-
433
- specify "should support set_column_type operations" do
434
- @db.add_column :test2, :xyz, :float
435
- @db[:test2].delete
436
- @db[:test2] << {:name => 'mmm', :value => 111, :xyz => 56.78}
437
- @db.set_column_type :test2, :xyz, :integer
438
-
439
- @db[:test2].first[:xyz].should == 57
440
- end
441
-
442
- specify "should support add_index" do
443
- @db.add_index :test2, :value
444
- end
445
-
446
- specify "should support drop_index" do
447
- @db.drop_index :test2, :value
448
- end
449
- end
450
-
451
- context "A MySQL database" do
452
- setup do
453
- @db = MYSQL_DB
454
- end
455
-
456
- specify "should support defaults for boolean columns" do
457
- g = Sequel::Schema::Generator.new(@db) do
458
- boolean :active1, :default => true
459
- boolean :active2, :default => false
460
- end
461
- statements = @db.create_table_sql_list(:items, *g.create_info)
462
- statements.should == [
463
- "CREATE TABLE items (active1 boolean DEFAULT 1, active2 boolean DEFAULT 0)"
464
- ]
465
- end
466
-
467
- specify "should correctly format CREATE TABLE statements with foreign keys" do
468
- g = Sequel::Schema::Generator.new(@db) do
469
- foreign_key :p_id, :table => :users, :key => :id,
470
- :null => false, :on_delete => :cascade
471
- end
472
- @db.create_table_sql_list(:items, *g.create_info).should == [
473
- "CREATE TABLE items (p_id integer NOT NULL, FOREIGN KEY (p_id) REFERENCES users(id) ON DELETE CASCADE)"
474
- ]
475
- end
476
-
477
- specify "should accept repeated raw sql statements using Database#<<" do
478
- @db << 'DELETE FROM items'
479
- @db[:items].count.should == 0
480
-
481
- @db << "INSERT INTO items (name, value) VALUES ('tutu', 1234)"
482
- @db[:items].first.should == {:name => 'tutu', :value => 1234}
483
-
484
- @db << 'DELETE FROM items'
485
- @db[:items].first.should == nil
486
- end
487
- end
488
-
489
- # Socket tests should only be run if the MySQL server is on localhost
490
- if %w'localhost 127.0.0.1 ::1'.include? MYSQL_URI.host
491
- context "A MySQL database" do
492
- specify "should accept a socket option" do
493
- db = Sequel.mysql(MYSQL_DB_NAME, :host => 'localhost', :user => MYSQL_USER, :socket => MYSQL_SOCKET_FILE)
494
- proc {db.test_connection}.should_not raise_error
495
- end
496
-
497
- specify "should accept a socket option without host option" do
498
- db = Sequel.mysql(MYSQL_DB_NAME, :user => MYSQL_USER, :socket => MYSQL_SOCKET_FILE)
499
- proc {db.test_connection}.should_not raise_error
500
- end
501
-
502
- specify "should fail to connect with invalid socket" do
503
- db = Sequel.mysql(MYSQL_DB_NAME, :host => 'localhost', :user => MYSQL_USER, :socket => 'blah')
504
- proc {db.test_connection}.should raise_error
505
- end
506
- end
507
- end
508
-
509
- context "A grouped MySQL dataset" do
510
- setup do
511
- MYSQL_DB[:test2].delete
512
- MYSQL_DB[:test2] << {:name => '11', :value => 10}
513
- MYSQL_DB[:test2] << {:name => '11', :value => 20}
514
- MYSQL_DB[:test2] << {:name => '11', :value => 30}
515
- MYSQL_DB[:test2] << {:name => '12', :value => 10}
516
- MYSQL_DB[:test2] << {:name => '12', :value => 20}
517
- MYSQL_DB[:test2] << {:name => '13', :value => 10}
518
- end
519
-
520
- specify "should return the correct count for raw sql query" do
521
- ds = MYSQL_DB["select name FROM test2 WHERE name = '11' GROUP BY name"]
522
- ds.count.should == 1
523
- end
524
-
525
- specify "should return the correct count for a normal dataset" do
526
- ds = MYSQL_DB[:test2].select(:name).where(:name => '11').group(:name)
527
- ds.count.should == 1
528
- end
529
- end
530
-
531
- context "A MySQL database" do
532
- setup do
533
- end
534
-
535
- specify "should support fulltext indexes" do
536
- g = Sequel::Schema::Generator.new(MYSQL_DB) do
537
- text :title
538
- text :body
539
- full_text_index [:title, :body]
540
- end
541
- MYSQL_DB.create_table_sql_list(:posts, *g.create_info).should == [
542
- "CREATE TABLE posts (title text, body text)",
543
- "CREATE FULLTEXT INDEX posts_title_body_index ON posts (title, body)"
544
- ]
545
- end
546
-
547
- specify "should support full_text_search" do
548
- MYSQL_DB[:posts].full_text_search(:title, 'ruby').sql.should ==
549
- "SELECT * FROM posts WHERE (MATCH (title) AGAINST ('ruby'))"
550
-
551
- MYSQL_DB[:posts].full_text_search([:title, :body], ['ruby', 'sequel']).sql.should ==
552
- "SELECT * FROM posts WHERE (MATCH (title, body) AGAINST ('ruby', 'sequel'))"
553
-
554
- MYSQL_DB[:posts].full_text_search(:title, '+ruby -rails', :boolean => true).sql.should ==
555
- "SELECT * FROM posts WHERE (MATCH (title) AGAINST ('+ruby -rails' IN BOOLEAN MODE))"
556
- end
557
-
558
- specify "should support spatial indexes" do
559
- g = Sequel::Schema::Generator.new(MYSQL_DB) do
560
- point :geom
561
- spatial_index [:geom]
562
- end
563
- MYSQL_DB.create_table_sql_list(:posts, *g.create_info).should == [
564
- "CREATE TABLE posts (geom point)",
565
- "CREATE SPATIAL INDEX posts_geom_index ON posts (geom)"
566
- ]
567
- end
568
-
569
- specify "should support indexes with index type" do
570
- g = Sequel::Schema::Generator.new(MYSQL_DB) do
571
- text :title
572
- index :title, :type => :hash
573
- end
574
- MYSQL_DB.create_table_sql_list(:posts, *g.create_info).should == [
575
- "CREATE TABLE posts (title text)",
576
- "CREATE INDEX posts_title_index ON posts (title) USING hash"
577
- ]
578
- end
579
-
580
- specify "should support unique indexes with index type" do
581
- g = Sequel::Schema::Generator.new(MYSQL_DB) do
582
- text :title
583
- index :title, :type => :hash, :unique => true
584
- end
585
- MYSQL_DB.create_table_sql_list(:posts, *g.create_info).should == [
586
- "CREATE TABLE posts (title text)",
587
- "CREATE UNIQUE INDEX posts_title_index ON posts (title) USING hash"
588
- ]
589
- end
590
- end
591
-
592
- context "MySQL::Dataset#insert" do
593
- setup do
594
- @d = MYSQL_DB[:items]
595
- @d.delete # remove all records
596
- MYSQL_DB.sqls.clear
597
- end
598
-
599
- specify "should insert record with default values when no arguments given" do
600
- @d.insert
601
-
602
- MYSQL_DB.sqls.should == [
603
- "INSERT INTO items () VALUES ()"
604
- ]
605
-
606
- @d.all.should == [
607
- {:name => nil, :value => nil}
608
- ]
609
- end
610
-
611
- specify "should insert record with default values when empty hash given" do
612
- @d.insert({})
613
-
614
- MYSQL_DB.sqls.should == [
615
- "INSERT INTO items () VALUES ()"
616
- ]
617
-
618
- @d.all.should == [
619
- {:name => nil, :value => nil}
620
- ]
621
- end
622
-
623
- specify "should insert record with default values when empty array given" do
624
- @d.insert []
625
-
626
- MYSQL_DB.sqls.should == [
627
- "INSERT INTO items () VALUES ()"
628
- ]
629
-
630
- @d.all.should == [
631
- {:name => nil, :value => nil}
632
- ]
633
- end
634
- end
635
-
636
- context "MySQL::Dataset#multi_insert" do
637
- setup do
638
- @d = MYSQL_DB[:items]
639
- @d.delete # remove all records
640
- MYSQL_DB.sqls.clear
641
- end
642
-
643
- specify "should insert multiple records in a single statement" do
644
- @d.multi_insert([{:name => 'abc'}, {:name => 'def'}])
645
-
646
- MYSQL_DB.sqls.should == [
647
- 'BEGIN',
648
- "INSERT INTO items (name) VALUES ('abc'), ('def')",
649
- 'COMMIT'
650
- ]
651
-
652
- @d.all.should == [
653
- {:name => 'abc', :value => nil}, {:name => 'def', :value => nil}
654
- ]
655
- end
656
-
657
- specify "should split the list of records into batches if :commit_every option is given" do
658
- @d.multi_insert([{:value => 1}, {:value => 2}, {:value => 3}, {:value => 4}],
659
- :commit_every => 2)
660
-
661
- MYSQL_DB.sqls.should == [
662
- 'BEGIN',
663
- "INSERT INTO items (value) VALUES (1), (2)",
664
- 'COMMIT',
665
- 'BEGIN',
666
- "INSERT INTO items (value) VALUES (3), (4)",
667
- 'COMMIT'
668
- ]
669
-
670
- @d.all.should == [
671
- {:name => nil, :value => 1},
672
- {:name => nil, :value => 2},
673
- {:name => nil, :value => 3},
674
- {:name => nil, :value => 4}
675
- ]
676
- end
677
-
678
- specify "should split the list of records into batches if :slice option is given" do
679
- @d.multi_insert([{:value => 1}, {:value => 2}, {:value => 3}, {:value => 4}],
680
- :slice => 2)
681
-
682
- MYSQL_DB.sqls.should == [
683
- 'BEGIN',
684
- "INSERT INTO items (value) VALUES (1), (2)",
685
- 'COMMIT',
686
- 'BEGIN',
687
- "INSERT INTO items (value) VALUES (3), (4)",
688
- 'COMMIT'
689
- ]
690
-
691
- @d.all.should == [
692
- {:name => nil, :value => 1},
693
- {:name => nil, :value => 2},
694
- {:name => nil, :value => 3},
695
- {:name => nil, :value => 4}
696
- ]
697
- end
698
-
699
- specify "should support inserting using columns and values arrays" do
700
- @d.multi_insert([:name, :value], [['abc', 1], ['def', 2]])
701
-
702
- MYSQL_DB.sqls.should == [
703
- 'BEGIN',
704
- "INSERT INTO items (name, value) VALUES ('abc', 1), ('def', 2)",
705
- 'COMMIT'
706
- ]
707
-
708
- @d.all.should == [
709
- {:name => 'abc', :value => 1},
710
- {:name => 'def', :value => 2}
711
- ]
712
- end
713
- end
714
-
715
- context "MySQL::Dataset#replace" do
716
- setup do
717
- MYSQL_DB.drop_table(:items) if MYSQL_DB.table_exists?(:items)
718
- MYSQL_DB.create_table :items do
719
- integer :id, :unique => true
720
- integer :value, :index => true
721
- end
722
- @d = MYSQL_DB[:items]
723
- MYSQL_DB.sqls.clear
724
- end
725
-
726
- specify "should create a record if the condition is not met" do
727
- @d.replace(:id => 111, :value => 333)
728
- @d.all.should == [{:id => 111, :value => 333}]
729
- end
730
-
731
- specify "should update a record if the condition is met" do
732
- @d << {:id => 111}
733
- @d.all.should == [{:id => 111, :value => nil}]
734
- @d.replace(:id => 111, :value => 333)
735
- @d.all.should == [{:id => 111, :value => 333}]
736
- end
737
- end
738
-
739
- context "MySQL::Dataset#complex_expression_sql" do
740
- setup do
741
- @d = MYSQL_DB.dataset
742
- end
743
-
744
- specify "should handle pattern matches correctly" do
745
- @d.literal(:x.like('a')).should == "(x LIKE BINARY 'a')"
746
- @d.literal(~:x.like('a')).should == "(x NOT LIKE BINARY 'a')"
747
- @d.literal(:x.ilike('a')).should == "(x LIKE 'a')"
748
- @d.literal(~:x.ilike('a')).should == "(x NOT LIKE 'a')"
749
- @d.literal(:x.like(/a/)).should == "(x REGEXP BINARY 'a')"
750
- @d.literal(~:x.like(/a/)).should == "(x NOT REGEXP BINARY 'a')"
751
- @d.literal(:x.like(/a/i)).should == "(x REGEXP 'a')"
752
- @d.literal(~:x.like(/a/i)).should == "(x NOT REGEXP 'a')"
753
- end
754
-
755
- specify "should handle string concatenation with CONCAT if more than one record" do
756
- @d.literal([:x, :y].sql_string_join).should == "CONCAT(x, y)"
757
- @d.literal([:x, :y].sql_string_join(' ')).should == "CONCAT(x, ' ', y)"
758
- @d.literal([:x[:y], 1, 'z'.lit].sql_string_join(:y|1)).should == "CONCAT(x(y), y[1], '1', y[1], z)"
759
- end
760
-
761
- specify "should handle string concatenation as simple string if just one record" do
762
- @d.literal([:x].sql_string_join).should == "x"
763
- @d.literal([:x].sql_string_join(' ')).should == "x"
764
- end
765
- end