sequel_core 2.2.0 → 3.8.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 (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