epugh-sequel 0.0.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.
- data/README.rdoc +652 -0
- data/VERSION.yml +4 -0
- data/bin/sequel +104 -0
- data/lib/sequel.rb +1 -0
- data/lib/sequel/adapters/ado.rb +85 -0
- data/lib/sequel/adapters/db2.rb +132 -0
- data/lib/sequel/adapters/dbi.rb +101 -0
- data/lib/sequel/adapters/do.rb +197 -0
- data/lib/sequel/adapters/do/mysql.rb +38 -0
- data/lib/sequel/adapters/do/postgres.rb +92 -0
- data/lib/sequel/adapters/do/sqlite.rb +31 -0
- data/lib/sequel/adapters/firebird.rb +307 -0
- data/lib/sequel/adapters/informix.rb +75 -0
- data/lib/sequel/adapters/jdbc.rb +485 -0
- data/lib/sequel/adapters/jdbc/h2.rb +62 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +56 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +23 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +101 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +43 -0
- data/lib/sequel/adapters/mysql.rb +370 -0
- data/lib/sequel/adapters/odbc.rb +184 -0
- data/lib/sequel/adapters/openbase.rb +57 -0
- data/lib/sequel/adapters/oracle.rb +140 -0
- data/lib/sequel/adapters/postgres.rb +453 -0
- data/lib/sequel/adapters/shared/mssql.rb +93 -0
- data/lib/sequel/adapters/shared/mysql.rb +341 -0
- data/lib/sequel/adapters/shared/oracle.rb +62 -0
- data/lib/sequel/adapters/shared/postgres.rb +743 -0
- data/lib/sequel/adapters/shared/progress.rb +34 -0
- data/lib/sequel/adapters/shared/sqlite.rb +263 -0
- data/lib/sequel/adapters/sqlite.rb +243 -0
- data/lib/sequel/adapters/utils/date_format.rb +21 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +75 -0
- data/lib/sequel/adapters/utils/unsupported.rb +62 -0
- data/lib/sequel/connection_pool.rb +258 -0
- data/lib/sequel/core.rb +204 -0
- data/lib/sequel/core_sql.rb +185 -0
- data/lib/sequel/database.rb +687 -0
- data/lib/sequel/database/schema_generator.rb +324 -0
- data/lib/sequel/database/schema_methods.rb +164 -0
- data/lib/sequel/database/schema_sql.rb +324 -0
- data/lib/sequel/dataset.rb +422 -0
- data/lib/sequel/dataset/convenience.rb +237 -0
- data/lib/sequel/dataset/prepared_statements.rb +220 -0
- data/lib/sequel/dataset/sql.rb +1105 -0
- data/lib/sequel/deprecated.rb +529 -0
- data/lib/sequel/exceptions.rb +44 -0
- data/lib/sequel/extensions/blank.rb +42 -0
- data/lib/sequel/extensions/inflector.rb +288 -0
- data/lib/sequel/extensions/pagination.rb +96 -0
- data/lib/sequel/extensions/pretty_table.rb +78 -0
- data/lib/sequel/extensions/query.rb +48 -0
- data/lib/sequel/extensions/string_date_time.rb +47 -0
- data/lib/sequel/metaprogramming.rb +44 -0
- data/lib/sequel/migration.rb +212 -0
- data/lib/sequel/model.rb +142 -0
- data/lib/sequel/model/association_reflection.rb +263 -0
- data/lib/sequel/model/associations.rb +1024 -0
- data/lib/sequel/model/base.rb +911 -0
- data/lib/sequel/model/deprecated.rb +188 -0
- data/lib/sequel/model/deprecated_hooks.rb +103 -0
- data/lib/sequel/model/deprecated_inflector.rb +335 -0
- data/lib/sequel/model/deprecated_validations.rb +384 -0
- data/lib/sequel/model/errors.rb +37 -0
- data/lib/sequel/model/exceptions.rb +7 -0
- data/lib/sequel/model/inflections.rb +230 -0
- data/lib/sequel/model/plugins.rb +74 -0
- data/lib/sequel/object_graph.rb +230 -0
- data/lib/sequel/plugins/caching.rb +122 -0
- data/lib/sequel/plugins/hook_class_methods.rb +122 -0
- data/lib/sequel/plugins/schema.rb +53 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +63 -0
- data/lib/sequel/plugins/validation_class_methods.rb +373 -0
- data/lib/sequel/sql.rb +854 -0
- data/lib/sequel/version.rb +11 -0
- data/lib/sequel_core.rb +1 -0
- data/lib/sequel_model.rb +1 -0
- data/spec/adapters/ado_spec.rb +46 -0
- data/spec/adapters/firebird_spec.rb +376 -0
- data/spec/adapters/informix_spec.rb +96 -0
- data/spec/adapters/mysql_spec.rb +875 -0
- data/spec/adapters/oracle_spec.rb +272 -0
- data/spec/adapters/postgres_spec.rb +692 -0
- data/spec/adapters/spec_helper.rb +10 -0
- data/spec/adapters/sqlite_spec.rb +550 -0
- data/spec/core/connection_pool_spec.rb +526 -0
- data/spec/core/core_ext_spec.rb +156 -0
- data/spec/core/core_sql_spec.rb +528 -0
- data/spec/core/database_spec.rb +1214 -0
- data/spec/core/dataset_spec.rb +3513 -0
- data/spec/core/expression_filters_spec.rb +363 -0
- data/spec/core/migration_spec.rb +261 -0
- data/spec/core/object_graph_spec.rb +280 -0
- data/spec/core/pretty_table_spec.rb +58 -0
- data/spec/core/schema_generator_spec.rb +167 -0
- data/spec/core/schema_spec.rb +778 -0
- data/spec/core/spec_helper.rb +82 -0
- data/spec/core/version_spec.rb +7 -0
- data/spec/extensions/blank_spec.rb +67 -0
- data/spec/extensions/caching_spec.rb +201 -0
- data/spec/extensions/hook_class_methods_spec.rb +470 -0
- data/spec/extensions/inflector_spec.rb +122 -0
- data/spec/extensions/pagination_spec.rb +99 -0
- data/spec/extensions/pretty_table_spec.rb +91 -0
- data/spec/extensions/query_spec.rb +85 -0
- data/spec/extensions/schema_spec.rb +111 -0
- data/spec/extensions/single_table_inheritance_spec.rb +53 -0
- data/spec/extensions/spec_helper.rb +90 -0
- data/spec/extensions/string_date_time_spec.rb +93 -0
- data/spec/extensions/validation_class_methods_spec.rb +1054 -0
- data/spec/integration/dataset_test.rb +160 -0
- data/spec/integration/eager_loader_test.rb +683 -0
- data/spec/integration/prepared_statement_test.rb +130 -0
- data/spec/integration/schema_test.rb +183 -0
- data/spec/integration/spec_helper.rb +75 -0
- data/spec/integration/type_test.rb +96 -0
- data/spec/model/association_reflection_spec.rb +93 -0
- data/spec/model/associations_spec.rb +1780 -0
- data/spec/model/base_spec.rb +494 -0
- data/spec/model/caching_spec.rb +217 -0
- data/spec/model/dataset_methods_spec.rb +78 -0
- data/spec/model/eager_loading_spec.rb +1165 -0
- data/spec/model/hooks_spec.rb +472 -0
- data/spec/model/inflector_spec.rb +126 -0
- data/spec/model/model_spec.rb +588 -0
- data/spec/model/plugins_spec.rb +142 -0
- data/spec/model/record_spec.rb +1243 -0
- data/spec/model/schema_spec.rb +92 -0
- data/spec/model/spec_helper.rb +124 -0
- data/spec/model/validations_spec.rb +1080 -0
- data/spec/rcov.opts +6 -0
- data/spec/spec.opts +0 -0
- data/spec/spec_config.rb.example +10 -0
- metadata +202 -0
|
@@ -0,0 +1,875 @@
|
|
|
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
|
+
|
|
16
|
+
MYSQL_DB.create_table! :items do
|
|
17
|
+
text :name
|
|
18
|
+
integer :value, :index => true
|
|
19
|
+
end
|
|
20
|
+
MYSQL_DB.create_table! :test2 do
|
|
21
|
+
text :name
|
|
22
|
+
integer :value
|
|
23
|
+
end
|
|
24
|
+
MYSQL_DB.create_table! :booltest do
|
|
25
|
+
tinyint :value
|
|
26
|
+
end
|
|
27
|
+
def MYSQL_DB.sqls
|
|
28
|
+
(@sqls ||= [])
|
|
29
|
+
end
|
|
30
|
+
logger = Object.new
|
|
31
|
+
def logger.method_missing(m, msg)
|
|
32
|
+
MYSQL_DB.sqls << msg
|
|
33
|
+
end
|
|
34
|
+
MYSQL_DB.logger = logger
|
|
35
|
+
|
|
36
|
+
if MYSQL_DB.class.adapter_scheme == :do
|
|
37
|
+
SQL_BEGIN = 'Transaction.begin'
|
|
38
|
+
SQL_ROLLBACK = 'Transaction.rollback'
|
|
39
|
+
SQL_COMMIT = 'Transaction.commit'
|
|
40
|
+
else
|
|
41
|
+
SQL_BEGIN = 'BEGIN'
|
|
42
|
+
SQL_ROLLBACK = 'ROLLBACK'
|
|
43
|
+
SQL_COMMIT = 'COMMIT'
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
context "MySQL", '#create_table' do
|
|
47
|
+
setup do
|
|
48
|
+
@db = MYSQL_DB
|
|
49
|
+
end
|
|
50
|
+
after(:each) do
|
|
51
|
+
@db.drop_table :dolls
|
|
52
|
+
end
|
|
53
|
+
specify "should allow to specify options for MySQL" do
|
|
54
|
+
@db.create_table(:dolls, :engine => 'MyISAM', :charset => 'latin2') { text :name }
|
|
55
|
+
@db.sqls.should == ["CREATE TABLE dolls (name text) ENGINE=MyISAM DEFAULT CHARSET=latin2"]
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
context "A MySQL database" do
|
|
60
|
+
setup do
|
|
61
|
+
@db = MYSQL_DB
|
|
62
|
+
end
|
|
63
|
+
teardown do
|
|
64
|
+
Sequel.convert_tinyint_to_bool = true
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
specify "should provide the server version" do
|
|
68
|
+
@db.server_version.should >= 40000
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
specify "should support sequential primary keys" do
|
|
72
|
+
@db.create_table!(:with_pk) {primary_key :id; text :name}
|
|
73
|
+
@db[:with_pk] << {:name => 'abc'}
|
|
74
|
+
@db[:with_pk] << {:name => 'def'}
|
|
75
|
+
@db[:with_pk] << {:name => 'ghi'}
|
|
76
|
+
@db[:with_pk].order(:name).all.should == [
|
|
77
|
+
{:id => 1, :name => 'abc'},
|
|
78
|
+
{:id => 2, :name => 'def'},
|
|
79
|
+
{:id => 3, :name => 'ghi'}
|
|
80
|
+
]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
specify "should provide disconnect functionality" do
|
|
84
|
+
@db.pool.size.should == 1
|
|
85
|
+
@db.disconnect
|
|
86
|
+
@db.pool.size.should == 0
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
specify "should convert Mysql::Errors to Sequel::Errors" do
|
|
90
|
+
proc{@db << "SELECT 1 + blah;"}.should raise_error(Sequel::Error)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
specify "should correctly parse the schema" do
|
|
94
|
+
@db.schema(:booltest, :reload=>true).should == [[:value, {:type=>:boolean, :allow_null=>true, :primary_key=>false, :default=>nil, :db_type=>"tinyint(4)"}]]
|
|
95
|
+
|
|
96
|
+
Sequel.convert_tinyint_to_bool = false
|
|
97
|
+
@db.schema(:booltest, :reload=>true).should == [[:value, {:type=>:integer, :allow_null=>true, :primary_key=>false, :default=>nil, :db_type=>"tinyint(4)"}]]
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
context "A MySQL dataset" do
|
|
102
|
+
setup do
|
|
103
|
+
@d = MYSQL_DB[:items]
|
|
104
|
+
@d.delete # remove all records
|
|
105
|
+
MYSQL_DB.sqls.clear
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
specify "should return the correct record count" do
|
|
109
|
+
@d.count.should == 0
|
|
110
|
+
@d << {:name => 'abc', :value => 123}
|
|
111
|
+
@d << {:name => 'abc', :value => 456}
|
|
112
|
+
@d << {:name => 'def', :value => 789}
|
|
113
|
+
@d.count.should == 3
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
specify "should return the correct records" do
|
|
117
|
+
@d.to_a.should == []
|
|
118
|
+
@d << {:name => 'abc', :value => 123}
|
|
119
|
+
@d << {:name => 'abc', :value => 456}
|
|
120
|
+
@d << {:name => 'def', :value => 789}
|
|
121
|
+
|
|
122
|
+
@d.order(:value).to_a.should == [
|
|
123
|
+
{:name => 'abc', :value => 123},
|
|
124
|
+
{:name => 'abc', :value => 456},
|
|
125
|
+
{:name => 'def', :value => 789}
|
|
126
|
+
]
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
specify "should update records correctly" do
|
|
130
|
+
@d << {:name => 'abc', :value => 123}
|
|
131
|
+
@d << {:name => 'abc', :value => 456}
|
|
132
|
+
@d << {:name => 'def', :value => 789}
|
|
133
|
+
@d.filter(:name => 'abc').update(:value => 530)
|
|
134
|
+
|
|
135
|
+
# the third record should stay the same
|
|
136
|
+
# floating-point precision bullshit
|
|
137
|
+
@d[:name => 'def'][:value].should == 789
|
|
138
|
+
@d.filter(:value => 530).count.should == 2
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
specify "should delete records correctly" do
|
|
142
|
+
@d << {:name => 'abc', :value => 123}
|
|
143
|
+
@d << {:name => 'abc', :value => 456}
|
|
144
|
+
@d << {:name => 'def', :value => 789}
|
|
145
|
+
@d.filter(:name => 'abc').delete
|
|
146
|
+
|
|
147
|
+
@d.count.should == 1
|
|
148
|
+
@d.first[:name].should == 'def'
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
specify "should be able to literalize booleans" do
|
|
152
|
+
proc {@d.literal(true)}.should_not raise_error
|
|
153
|
+
proc {@d.literal(false)}.should_not raise_error
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
specify "should quote columns and tables using back-ticks if quoting identifiers" do
|
|
157
|
+
@d.quote_identifiers = true
|
|
158
|
+
@d.select(:name).sql.should == \
|
|
159
|
+
'SELECT `name` FROM `items`'
|
|
160
|
+
|
|
161
|
+
@d.select('COUNT(*)'.lit).sql.should == \
|
|
162
|
+
'SELECT COUNT(*) FROM `items`'
|
|
163
|
+
|
|
164
|
+
@d.select(:max.sql_function(:value)).sql.should == \
|
|
165
|
+
'SELECT max(`value`) FROM `items`'
|
|
166
|
+
|
|
167
|
+
@d.select(:NOW.sql_function).sql.should == \
|
|
168
|
+
'SELECT NOW() FROM `items`'
|
|
169
|
+
|
|
170
|
+
@d.select(:max.sql_function(:items__value)).sql.should == \
|
|
171
|
+
'SELECT max(`items`.`value`) FROM `items`'
|
|
172
|
+
|
|
173
|
+
@d.order(:name.desc).sql.should == \
|
|
174
|
+
'SELECT * FROM `items` ORDER BY `name` DESC'
|
|
175
|
+
|
|
176
|
+
@d.select('items.name AS item_name'.lit).sql.should == \
|
|
177
|
+
'SELECT items.name AS item_name FROM `items`'
|
|
178
|
+
|
|
179
|
+
@d.select('`name`'.lit).sql.should == \
|
|
180
|
+
'SELECT `name` FROM `items`'
|
|
181
|
+
|
|
182
|
+
@d.select('max(items.`name`) AS `max_name`'.lit).sql.should == \
|
|
183
|
+
'SELECT max(items.`name`) AS `max_name` FROM `items`'
|
|
184
|
+
|
|
185
|
+
@d.select(:test.sql_function(:abc, 'hello')).sql.should == \
|
|
186
|
+
"SELECT test(`abc`, 'hello') FROM `items`"
|
|
187
|
+
|
|
188
|
+
@d.select(:test.sql_function(:abc__def, 'hello')).sql.should == \
|
|
189
|
+
"SELECT test(`abc`.`def`, 'hello') FROM `items`"
|
|
190
|
+
|
|
191
|
+
@d.select(:test.sql_function(:abc__def, 'hello').as(:x2)).sql.should == \
|
|
192
|
+
"SELECT test(`abc`.`def`, 'hello') AS `x2` FROM `items`"
|
|
193
|
+
|
|
194
|
+
@d.insert_sql(:value => 333).should == \
|
|
195
|
+
'INSERT INTO `items` (`value`) VALUES (333)'
|
|
196
|
+
|
|
197
|
+
@d.insert_sql(:x => :y).should == \
|
|
198
|
+
'INSERT INTO `items` (`x`) VALUES (`y`)'
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
specify "should quote fields correctly when reversing the order" do
|
|
202
|
+
@d.quote_identifiers = true
|
|
203
|
+
@d.reverse_order(:name).sql.should == \
|
|
204
|
+
'SELECT * FROM `items` ORDER BY `name` DESC'
|
|
205
|
+
|
|
206
|
+
@d.reverse_order(:name.desc).sql.should == \
|
|
207
|
+
'SELECT * FROM `items` ORDER BY `name` ASC'
|
|
208
|
+
|
|
209
|
+
@d.reverse_order(:name, :test.desc).sql.should == \
|
|
210
|
+
'SELECT * FROM `items` ORDER BY `name` DESC, `test` ASC'
|
|
211
|
+
|
|
212
|
+
@d.reverse_order(:name.desc, :test).sql.should == \
|
|
213
|
+
'SELECT * FROM `items` ORDER BY `name` ASC, `test` DESC'
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
specify "should support ORDER clause in UPDATE statements" do
|
|
217
|
+
@d.order(:name).update_sql(:value => 1).should == \
|
|
218
|
+
'UPDATE items SET value = 1 ORDER BY name'
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
specify "should support LIMIT clause in UPDATE statements" do
|
|
222
|
+
@d.limit(10).update_sql(:value => 1).should == \
|
|
223
|
+
'UPDATE items SET value = 1 LIMIT 10'
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
specify "should support transactions" do
|
|
227
|
+
MYSQL_DB.transaction do
|
|
228
|
+
@d << {:name => 'abc', :value => 1}
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
@d.count.should == 1
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
specify "should correctly rollback transactions" do
|
|
235
|
+
proc do
|
|
236
|
+
MYSQL_DB.transaction do
|
|
237
|
+
@d << {:name => 'abc'}
|
|
238
|
+
raise Interrupt, 'asdf'
|
|
239
|
+
end
|
|
240
|
+
end.should raise_error(Interrupt)
|
|
241
|
+
|
|
242
|
+
MYSQL_DB.sqls.should == [SQL_BEGIN, "INSERT INTO items (name) VALUES ('abc')", SQL_ROLLBACK]
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
specify "should handle returning inside of the block by committing" do
|
|
246
|
+
def MYSQL_DB.ret_commit
|
|
247
|
+
transaction do
|
|
248
|
+
self[:items] << {:name => 'abc'}
|
|
249
|
+
return
|
|
250
|
+
self[:items] << {:name => 'd'}
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
MYSQL_DB.ret_commit
|
|
254
|
+
MYSQL_DB.sqls.should == [SQL_BEGIN, "INSERT INTO items (name) VALUES ('abc')", SQL_COMMIT]
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
specify "should support regexps" do
|
|
258
|
+
@d << {:name => 'abc', :value => 1}
|
|
259
|
+
@d << {:name => 'bcd', :value => 2}
|
|
260
|
+
@d.filter(:name => /bc/).count.should == 2
|
|
261
|
+
@d.filter(:name => /^bc/).count.should == 1
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
specify "should correctly literalize strings with comment backslashes in them" do
|
|
265
|
+
@d.delete
|
|
266
|
+
proc {@d << {:name => ':\\'}}.should_not raise_error
|
|
267
|
+
|
|
268
|
+
@d.first[:name].should == ':\\'
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
context "MySQL datasets" do
|
|
273
|
+
setup do
|
|
274
|
+
@d = MYSQL_DB[:orders]
|
|
275
|
+
end
|
|
276
|
+
teardown do
|
|
277
|
+
Sequel.convert_tinyint_to_bool = true
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
specify "should correctly quote column references" do
|
|
281
|
+
@d.quote_identifiers = true
|
|
282
|
+
market = 'ICE'
|
|
283
|
+
ack_stamp = Time.now - 15 * 60 # 15 minutes ago
|
|
284
|
+
@d.select(:market, :minute.sql_function(:from_unixtime.sql_function(:ack)).as(:minute)).
|
|
285
|
+
where{|o|(:ack.sql_number > ack_stamp) & {:market => market}}.
|
|
286
|
+
group_by(:minute.sql_function(:from_unixtime.sql_function(:ack))).sql.should == \
|
|
287
|
+
"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`))"
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
specify "should accept and return tinyints as bools or integers when configured to do so" do
|
|
291
|
+
MYSQL_DB[:booltest].delete
|
|
292
|
+
MYSQL_DB[:booltest] << {:value=>true}
|
|
293
|
+
MYSQL_DB[:booltest].all.should == [{:value=>true}]
|
|
294
|
+
MYSQL_DB[:booltest].delete
|
|
295
|
+
MYSQL_DB[:booltest] << {:value=>false}
|
|
296
|
+
MYSQL_DB[:booltest].all.should == [{:value=>false}]
|
|
297
|
+
|
|
298
|
+
Sequel.convert_tinyint_to_bool = false
|
|
299
|
+
MYSQL_DB[:booltest].delete
|
|
300
|
+
MYSQL_DB[:booltest] << {:value=>true}
|
|
301
|
+
MYSQL_DB[:booltest].all.should == [{:value=>1}]
|
|
302
|
+
MYSQL_DB[:booltest].delete
|
|
303
|
+
MYSQL_DB[:booltest] << {:value=>false}
|
|
304
|
+
MYSQL_DB[:booltest].all.should == [{:value=>0}]
|
|
305
|
+
|
|
306
|
+
MYSQL_DB[:booltest].delete
|
|
307
|
+
MYSQL_DB[:booltest] << {:value=>1}
|
|
308
|
+
MYSQL_DB[:booltest].all.should == [{:value=>1}]
|
|
309
|
+
MYSQL_DB[:booltest].delete
|
|
310
|
+
MYSQL_DB[:booltest] << {:value=>0}
|
|
311
|
+
MYSQL_DB[:booltest].all.should == [{:value=>0}]
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
context "MySQL join expressions" do
|
|
316
|
+
setup do
|
|
317
|
+
@ds = MYSQL_DB[:nodes]
|
|
318
|
+
@ds.db.meta_def(:server_version) {50014}
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
specify "should raise error for :full_outer join requests." do
|
|
322
|
+
lambda{@ds.join_table(:full_outer, :nodes)}.should raise_error(Sequel::Error)
|
|
323
|
+
end
|
|
324
|
+
specify "should support natural left joins" do
|
|
325
|
+
@ds.join_table(:natural_left, :nodes).sql.should == \
|
|
326
|
+
'SELECT * FROM nodes NATURAL LEFT JOIN nodes'
|
|
327
|
+
end
|
|
328
|
+
specify "should support natural right joins" do
|
|
329
|
+
@ds.join_table(:natural_right, :nodes).sql.should == \
|
|
330
|
+
'SELECT * FROM nodes NATURAL RIGHT JOIN nodes'
|
|
331
|
+
end
|
|
332
|
+
specify "should support natural left outer joins" do
|
|
333
|
+
@ds.join_table(:natural_left_outer, :nodes).sql.should == \
|
|
334
|
+
'SELECT * FROM nodes NATURAL LEFT OUTER JOIN nodes'
|
|
335
|
+
end
|
|
336
|
+
specify "should support natural right outer joins" do
|
|
337
|
+
@ds.join_table(:natural_right_outer, :nodes).sql.should == \
|
|
338
|
+
'SELECT * FROM nodes NATURAL RIGHT OUTER JOIN nodes'
|
|
339
|
+
end
|
|
340
|
+
specify "should support natural inner joins" do
|
|
341
|
+
@ds.join_table(:natural_inner, :nodes).sql.should == \
|
|
342
|
+
'SELECT * FROM nodes NATURAL LEFT JOIN nodes'
|
|
343
|
+
end
|
|
344
|
+
specify "should support cross joins" do
|
|
345
|
+
@ds.join_table(:cross, :nodes).sql.should == \
|
|
346
|
+
'SELECT * FROM nodes CROSS JOIN nodes'
|
|
347
|
+
end
|
|
348
|
+
specify "should support cross joins as inner joins if conditions are used" do
|
|
349
|
+
@ds.join_table(:cross, :nodes, :id=>:id).sql.should == \
|
|
350
|
+
'SELECT * FROM nodes INNER JOIN nodes ON (nodes.id = nodes.id)'
|
|
351
|
+
end
|
|
352
|
+
specify "should support straight joins (force left table to be read before right)" do
|
|
353
|
+
@ds.join_table(:straight, :nodes).sql.should == \
|
|
354
|
+
'SELECT * FROM nodes STRAIGHT_JOIN nodes'
|
|
355
|
+
end
|
|
356
|
+
specify "should support natural joins on multiple tables." do
|
|
357
|
+
@ds.join_table(:natural_left_outer, [:nodes, :branches]).sql.should == \
|
|
358
|
+
'SELECT * FROM nodes NATURAL LEFT OUTER JOIN (nodes, branches)'
|
|
359
|
+
end
|
|
360
|
+
specify "should support straight joins on multiple tables." do
|
|
361
|
+
@ds.join_table(:straight, [:nodes,:branches]).sql.should == \
|
|
362
|
+
'SELECT * FROM nodes STRAIGHT_JOIN (nodes, branches)'
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
context "Joined MySQL dataset" do
|
|
367
|
+
setup do
|
|
368
|
+
@ds = MYSQL_DB[:nodes]
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
specify "should quote fields correctly" do
|
|
372
|
+
@ds.quote_identifiers = true
|
|
373
|
+
@ds.join(:attributes, :node_id => :id).sql.should == \
|
|
374
|
+
"SELECT * FROM `nodes` INNER JOIN `attributes` ON (`attributes`.`node_id` = `nodes`.`id`)"
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
specify "should allow a having clause on ungrouped datasets" do
|
|
378
|
+
proc {@ds.having('blah')}.should_not raise_error
|
|
379
|
+
|
|
380
|
+
@ds.having('blah').sql.should == \
|
|
381
|
+
"SELECT * FROM nodes HAVING (blah)"
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
specify "should put a having clause before an order by clause" do
|
|
385
|
+
@ds.order(:aaa).having(:bbb => :ccc).sql.should == \
|
|
386
|
+
"SELECT * FROM nodes HAVING (bbb = ccc) ORDER BY aaa"
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
context "A MySQL database" do
|
|
391
|
+
setup do
|
|
392
|
+
@db = MYSQL_DB
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
specify "should support add_column operations" do
|
|
396
|
+
@db.add_column :test2, :xyz, :text
|
|
397
|
+
|
|
398
|
+
@db[:test2].columns.should == [:name, :value, :xyz]
|
|
399
|
+
@db[:test2] << {:name => 'mmm', :value => 111, :xyz => '000'}
|
|
400
|
+
@db[:test2].first[:xyz].should == '000'
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
specify "should support drop_column operations" do
|
|
404
|
+
@db[:test2].columns.should == [:name, :value, :xyz]
|
|
405
|
+
@db.drop_column :test2, :xyz
|
|
406
|
+
|
|
407
|
+
@db[:test2].columns.should == [:name, :value]
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
specify "should support rename_column operations" do
|
|
411
|
+
@db[:test2].delete
|
|
412
|
+
@db.add_column :test2, :xyz, :text
|
|
413
|
+
@db[:test2] << {:name => 'mmm', :value => 111, :xyz => 'qqqq'}
|
|
414
|
+
|
|
415
|
+
@db[:test2].columns.should == [:name, :value, :xyz]
|
|
416
|
+
@db.rename_column :test2, :xyz, :zyx, :type => :text
|
|
417
|
+
@db[:test2].columns.should == [:name, :value, :zyx]
|
|
418
|
+
@db[:test2].first[:zyx].should == 'qqqq'
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
specify "should support rename_column operations with types like varchar(255)" do
|
|
422
|
+
@db[:test2].delete
|
|
423
|
+
@db.add_column :test2, :tre, :text
|
|
424
|
+
@db[:test2] << {:name => 'mmm', :value => 111, :tre => 'qqqq'}
|
|
425
|
+
|
|
426
|
+
@db[:test2].columns.should == [:name, :value, :zyx, :tre]
|
|
427
|
+
@db.rename_column :test2, :tre, :ert, :type => :varchar, :size=>255
|
|
428
|
+
@db[:test2].columns.should == [:name, :value, :zyx, :ert]
|
|
429
|
+
@db[:test2].first[:ert].should == 'qqqq'
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
specify "should support set_column_type operations" do
|
|
433
|
+
@db.add_column :test2, :xyz, :float
|
|
434
|
+
@db[:test2].delete
|
|
435
|
+
@db[:test2] << {:name => 'mmm', :value => 111, :xyz => 56.78}
|
|
436
|
+
@db.set_column_type :test2, :xyz, :integer
|
|
437
|
+
|
|
438
|
+
@db[:test2].first[:xyz].should == 57
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
specify "should support add_index" do
|
|
442
|
+
@db.add_index :test2, :value
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
specify "should support drop_index" do
|
|
446
|
+
@db.drop_index :test2, :value
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
specify "should support add_foreign_key" do
|
|
450
|
+
@db.alter_table :test2 do
|
|
451
|
+
add_foreign_key :value2, :test2, :key=>:value
|
|
452
|
+
end
|
|
453
|
+
@db[:test2].columns.should == [:name, :value, :zyx, :ert, :xyz, :value2]
|
|
454
|
+
end
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
context "A MySQL database", "with table options" do
|
|
458
|
+
before(:all) do
|
|
459
|
+
@options = {}
|
|
460
|
+
@options[:engine] = 'MyISAM'
|
|
461
|
+
@options[:charset] = 'latin2'
|
|
462
|
+
@options[:collate] = 'swedish'
|
|
463
|
+
|
|
464
|
+
Sequel::MySQL.default_engine = 'InnoDB'
|
|
465
|
+
Sequel::MySQL.default_charset = 'utf8'
|
|
466
|
+
Sequel::MySQL.default_collate = 'utf8'
|
|
467
|
+
|
|
468
|
+
@db = MYSQL_DB
|
|
469
|
+
@g = Sequel::Schema::Generator.new(@db) do
|
|
470
|
+
integer :size
|
|
471
|
+
text :name
|
|
472
|
+
end
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
after(:all) do
|
|
476
|
+
Sequel::MySQL.default_engine = nil
|
|
477
|
+
Sequel::MySQL.default_charset = nil
|
|
478
|
+
Sequel::MySQL.default_collate = nil
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
specify "should allow to pass custom options (engine, charset, collate) for table creation" do
|
|
482
|
+
statements = @db.create_table_sql_list(:items, *(@g.create_info << @options))
|
|
483
|
+
statements.should == [
|
|
484
|
+
"CREATE TABLE items (size integer, name text) ENGINE=MyISAM DEFAULT CHARSET=latin2 DEFAULT COLLATE=swedish"
|
|
485
|
+
]
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
specify "should use default options if specified (engine, charset, collate) for table creation" do
|
|
489
|
+
statements = @db.create_table_sql_list(:items, *(@g.create_info))
|
|
490
|
+
statements.should == [
|
|
491
|
+
"CREATE TABLE items (size integer, name text) ENGINE=InnoDB DEFAULT CHARSET=utf8 DEFAULT COLLATE=utf8"
|
|
492
|
+
]
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
specify "should not use default if option has a nil value" do
|
|
496
|
+
statements = @db.create_table_sql_list(:items, *(@g.create_info << {:engine=>nil, :charset=>nil, :collate=>nil}))
|
|
497
|
+
statements.should == [
|
|
498
|
+
"CREATE TABLE items (size integer, name text)"
|
|
499
|
+
]
|
|
500
|
+
end
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
context "A MySQL database" do
|
|
504
|
+
setup do
|
|
505
|
+
@db = MYSQL_DB
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
specify "should support defaults for boolean columns" do
|
|
509
|
+
g = Sequel::Schema::Generator.new(@db) do
|
|
510
|
+
boolean :active1, :default => true
|
|
511
|
+
boolean :active2, :default => false
|
|
512
|
+
end
|
|
513
|
+
statements = @db.create_table_sql_list(:items, *g.create_info)
|
|
514
|
+
statements.should == [
|
|
515
|
+
"CREATE TABLE items (active1 boolean DEFAULT 1, active2 boolean DEFAULT 0)"
|
|
516
|
+
]
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
specify "should correctly format CREATE TABLE statements with foreign keys" do
|
|
520
|
+
g = Sequel::Schema::Generator.new(@db) do
|
|
521
|
+
foreign_key :p_id, :table => :users, :key => :id,
|
|
522
|
+
:null => false, :on_delete => :cascade
|
|
523
|
+
end
|
|
524
|
+
@db.create_table_sql_list(:items, *g.create_info).should == [
|
|
525
|
+
"CREATE TABLE items (p_id integer NOT NULL, FOREIGN KEY (p_id) REFERENCES users(id) ON DELETE CASCADE)"
|
|
526
|
+
]
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
specify "should correctly format ALTER TABLE statements with foreign keys" do
|
|
530
|
+
g = Sequel::Schema::AlterTableGenerator.new(@db) do
|
|
531
|
+
add_foreign_key :p_id, :users, :key => :id, :null => false, :on_delete => :cascade
|
|
532
|
+
end
|
|
533
|
+
@db.alter_table_sql_list(:items, g.operations).should == [[
|
|
534
|
+
"ALTER TABLE items ADD COLUMN p_id integer NOT NULL",
|
|
535
|
+
"ALTER TABLE items ADD FOREIGN KEY (p_id) REFERENCES users(id) ON DELETE CASCADE"
|
|
536
|
+
]]
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
specify "should accept repeated raw sql statements using Database#<<" do
|
|
540
|
+
@db << 'DELETE FROM items'
|
|
541
|
+
@db[:items].count.should == 0
|
|
542
|
+
|
|
543
|
+
@db << "INSERT INTO items (name, value) VALUES ('tutu', 1234)"
|
|
544
|
+
@db[:items].first.should == {:name => 'tutu', :value => 1234}
|
|
545
|
+
|
|
546
|
+
@db << 'DELETE FROM items'
|
|
547
|
+
@db[:items].first.should == nil
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
specify "should handle multiple select statements at once" do
|
|
551
|
+
@db << 'DELETE FROM items; '
|
|
552
|
+
|
|
553
|
+
@db[:items].delete
|
|
554
|
+
@db[:items].insert(:name => 'tutu', :value => 1234)
|
|
555
|
+
@db["SELECT * FROM items; SELECT * FROM items"].all.should == \
|
|
556
|
+
[{:name => 'tutu', :value => 1234}, {:name => 'tutu', :value => 1234}]
|
|
557
|
+
end
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
# Socket tests should only be run if the MySQL server is on localhost
|
|
561
|
+
if %w'localhost 127.0.0.1 ::1'.include?(MYSQL_URI.host) and MYSQL_DB.class.adapter_scheme == :mysql
|
|
562
|
+
context "A MySQL database" do
|
|
563
|
+
specify "should accept a socket option" do
|
|
564
|
+
db = Sequel.mysql(MYSQL_DB.opts[:database], :host => 'localhost', :user => MYSQL_DB.opts[:user], :password => MYSQL_DB.opts[:password], :socket => MYSQL_SOCKET_FILE)
|
|
565
|
+
proc {db.test_connection}.should_not raise_error
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
specify "should accept a socket option without host option" do
|
|
569
|
+
db = Sequel.mysql(MYSQL_DB.opts[:database], :user => MYSQL_DB.opts[:user], :password => MYSQL_DB.opts[:password], :socket => MYSQL_SOCKET_FILE)
|
|
570
|
+
proc {db.test_connection}.should_not raise_error
|
|
571
|
+
end
|
|
572
|
+
|
|
573
|
+
specify "should fail to connect with invalid socket" do
|
|
574
|
+
db = Sequel.mysql(MYSQL_DB.opts[:database], :user => MYSQL_DB.opts[:user], :password => MYSQL_DB.opts[:password], :socket =>'blah')
|
|
575
|
+
proc {db.test_connection}.should raise_error
|
|
576
|
+
end
|
|
577
|
+
end
|
|
578
|
+
end
|
|
579
|
+
|
|
580
|
+
context "A grouped MySQL dataset" do
|
|
581
|
+
setup do
|
|
582
|
+
MYSQL_DB[:test2].delete
|
|
583
|
+
MYSQL_DB[:test2] << {:name => '11', :value => 10}
|
|
584
|
+
MYSQL_DB[:test2] << {:name => '11', :value => 20}
|
|
585
|
+
MYSQL_DB[:test2] << {:name => '11', :value => 30}
|
|
586
|
+
MYSQL_DB[:test2] << {:name => '12', :value => 10}
|
|
587
|
+
MYSQL_DB[:test2] << {:name => '12', :value => 20}
|
|
588
|
+
MYSQL_DB[:test2] << {:name => '13', :value => 10}
|
|
589
|
+
end
|
|
590
|
+
|
|
591
|
+
specify "should return the correct count for raw sql query" do
|
|
592
|
+
ds = MYSQL_DB["select name FROM test2 WHERE name = '11' GROUP BY name"]
|
|
593
|
+
ds.count.should == 1
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
specify "should return the correct count for a normal dataset" do
|
|
597
|
+
ds = MYSQL_DB[:test2].select(:name).where(:name => '11').group(:name)
|
|
598
|
+
ds.count.should == 1
|
|
599
|
+
end
|
|
600
|
+
end
|
|
601
|
+
|
|
602
|
+
context "A MySQL database" do
|
|
603
|
+
setup do
|
|
604
|
+
end
|
|
605
|
+
|
|
606
|
+
specify "should support fulltext indexes" do
|
|
607
|
+
g = Sequel::Schema::Generator.new(MYSQL_DB) do
|
|
608
|
+
text :title
|
|
609
|
+
text :body
|
|
610
|
+
full_text_index [:title, :body]
|
|
611
|
+
end
|
|
612
|
+
MYSQL_DB.create_table_sql_list(:posts, *g.create_info).should == [
|
|
613
|
+
"CREATE TABLE posts (title text, body text)",
|
|
614
|
+
"CREATE FULLTEXT INDEX posts_title_body_index ON posts (title, body)"
|
|
615
|
+
]
|
|
616
|
+
end
|
|
617
|
+
|
|
618
|
+
specify "should support full_text_search" do
|
|
619
|
+
MYSQL_DB[:posts].full_text_search(:title, 'ruby').sql.should ==
|
|
620
|
+
"SELECT * FROM posts WHERE (MATCH (title) AGAINST ('ruby'))"
|
|
621
|
+
|
|
622
|
+
MYSQL_DB[:posts].full_text_search([:title, :body], ['ruby', 'sequel']).sql.should ==
|
|
623
|
+
"SELECT * FROM posts WHERE (MATCH (title, body) AGAINST ('ruby', 'sequel'))"
|
|
624
|
+
|
|
625
|
+
MYSQL_DB[:posts].full_text_search(:title, '+ruby -rails', :boolean => true).sql.should ==
|
|
626
|
+
"SELECT * FROM posts WHERE (MATCH (title) AGAINST ('+ruby -rails' IN BOOLEAN MODE))"
|
|
627
|
+
end
|
|
628
|
+
|
|
629
|
+
specify "should support spatial indexes" do
|
|
630
|
+
g = Sequel::Schema::Generator.new(MYSQL_DB) do
|
|
631
|
+
point :geom
|
|
632
|
+
spatial_index [:geom]
|
|
633
|
+
end
|
|
634
|
+
MYSQL_DB.create_table_sql_list(:posts, *g.create_info).should == [
|
|
635
|
+
"CREATE TABLE posts (geom point)",
|
|
636
|
+
"CREATE SPATIAL INDEX posts_geom_index ON posts (geom)"
|
|
637
|
+
]
|
|
638
|
+
end
|
|
639
|
+
|
|
640
|
+
specify "should support indexes with index type" do
|
|
641
|
+
g = Sequel::Schema::Generator.new(MYSQL_DB) do
|
|
642
|
+
text :title
|
|
643
|
+
index :title, :type => :hash
|
|
644
|
+
end
|
|
645
|
+
MYSQL_DB.create_table_sql_list(:posts, *g.create_info).should == [
|
|
646
|
+
"CREATE TABLE posts (title text)",
|
|
647
|
+
"CREATE INDEX posts_title_index ON posts (title) USING hash"
|
|
648
|
+
]
|
|
649
|
+
end
|
|
650
|
+
|
|
651
|
+
specify "should support unique indexes with index type" do
|
|
652
|
+
g = Sequel::Schema::Generator.new(MYSQL_DB) do
|
|
653
|
+
text :title
|
|
654
|
+
index :title, :type => :hash, :unique => true
|
|
655
|
+
end
|
|
656
|
+
MYSQL_DB.create_table_sql_list(:posts, *g.create_info).should == [
|
|
657
|
+
"CREATE TABLE posts (title text)",
|
|
658
|
+
"CREATE UNIQUE INDEX posts_title_index ON posts (title) USING hash"
|
|
659
|
+
]
|
|
660
|
+
end
|
|
661
|
+
end
|
|
662
|
+
|
|
663
|
+
context "MySQL::Dataset#insert" do
|
|
664
|
+
setup do
|
|
665
|
+
@d = MYSQL_DB[:items]
|
|
666
|
+
@d.delete # remove all records
|
|
667
|
+
MYSQL_DB.sqls.clear
|
|
668
|
+
end
|
|
669
|
+
|
|
670
|
+
specify "should insert record with default values when no arguments given" do
|
|
671
|
+
@d.insert
|
|
672
|
+
|
|
673
|
+
MYSQL_DB.sqls.should == [
|
|
674
|
+
"INSERT INTO items () VALUES ()"
|
|
675
|
+
]
|
|
676
|
+
|
|
677
|
+
@d.all.should == [
|
|
678
|
+
{:name => nil, :value => nil}
|
|
679
|
+
]
|
|
680
|
+
end
|
|
681
|
+
|
|
682
|
+
specify "should insert record with default values when empty hash given" do
|
|
683
|
+
@d.insert({})
|
|
684
|
+
|
|
685
|
+
MYSQL_DB.sqls.should == [
|
|
686
|
+
"INSERT INTO items () VALUES ()"
|
|
687
|
+
]
|
|
688
|
+
|
|
689
|
+
@d.all.should == [
|
|
690
|
+
{:name => nil, :value => nil}
|
|
691
|
+
]
|
|
692
|
+
end
|
|
693
|
+
|
|
694
|
+
specify "should insert record with default values when empty array given" do
|
|
695
|
+
@d.insert []
|
|
696
|
+
|
|
697
|
+
MYSQL_DB.sqls.should == [
|
|
698
|
+
"INSERT INTO items () VALUES ()"
|
|
699
|
+
]
|
|
700
|
+
|
|
701
|
+
@d.all.should == [
|
|
702
|
+
{:name => nil, :value => nil}
|
|
703
|
+
]
|
|
704
|
+
end
|
|
705
|
+
end
|
|
706
|
+
|
|
707
|
+
context "MySQL::Dataset#multi_insert" do
|
|
708
|
+
setup do
|
|
709
|
+
@d = MYSQL_DB[:items]
|
|
710
|
+
@d.delete # remove all records
|
|
711
|
+
MYSQL_DB.sqls.clear
|
|
712
|
+
end
|
|
713
|
+
|
|
714
|
+
specify "should insert multiple records in a single statement" do
|
|
715
|
+
@d.multi_insert([{:name => 'abc'}, {:name => 'def'}])
|
|
716
|
+
|
|
717
|
+
MYSQL_DB.sqls.should == [
|
|
718
|
+
SQL_BEGIN,
|
|
719
|
+
"INSERT INTO items (name) VALUES ('abc'), ('def')",
|
|
720
|
+
SQL_COMMIT
|
|
721
|
+
]
|
|
722
|
+
|
|
723
|
+
@d.all.should == [
|
|
724
|
+
{:name => 'abc', :value => nil}, {:name => 'def', :value => nil}
|
|
725
|
+
]
|
|
726
|
+
end
|
|
727
|
+
|
|
728
|
+
specify "should split the list of records into batches if :commit_every option is given" do
|
|
729
|
+
@d.multi_insert([{:value => 1}, {:value => 2}, {:value => 3}, {:value => 4}],
|
|
730
|
+
:commit_every => 2)
|
|
731
|
+
|
|
732
|
+
MYSQL_DB.sqls.should == [
|
|
733
|
+
SQL_BEGIN,
|
|
734
|
+
"INSERT INTO items (value) VALUES (1), (2)",
|
|
735
|
+
SQL_COMMIT,
|
|
736
|
+
SQL_BEGIN,
|
|
737
|
+
"INSERT INTO items (value) VALUES (3), (4)",
|
|
738
|
+
SQL_COMMIT
|
|
739
|
+
]
|
|
740
|
+
|
|
741
|
+
@d.all.should == [
|
|
742
|
+
{:name => nil, :value => 1},
|
|
743
|
+
{:name => nil, :value => 2},
|
|
744
|
+
{:name => nil, :value => 3},
|
|
745
|
+
{:name => nil, :value => 4}
|
|
746
|
+
]
|
|
747
|
+
end
|
|
748
|
+
|
|
749
|
+
specify "should split the list of records into batches if :slice option is given" do
|
|
750
|
+
@d.multi_insert([{:value => 1}, {:value => 2}, {:value => 3}, {:value => 4}],
|
|
751
|
+
:slice => 2)
|
|
752
|
+
|
|
753
|
+
MYSQL_DB.sqls.should == [
|
|
754
|
+
SQL_BEGIN,
|
|
755
|
+
"INSERT INTO items (value) VALUES (1), (2)",
|
|
756
|
+
SQL_COMMIT,
|
|
757
|
+
SQL_BEGIN,
|
|
758
|
+
"INSERT INTO items (value) VALUES (3), (4)",
|
|
759
|
+
SQL_COMMIT
|
|
760
|
+
]
|
|
761
|
+
|
|
762
|
+
@d.all.should == [
|
|
763
|
+
{:name => nil, :value => 1},
|
|
764
|
+
{:name => nil, :value => 2},
|
|
765
|
+
{:name => nil, :value => 3},
|
|
766
|
+
{:name => nil, :value => 4}
|
|
767
|
+
]
|
|
768
|
+
end
|
|
769
|
+
|
|
770
|
+
specify "should support inserting using columns and values arrays" do
|
|
771
|
+
@d.multi_insert([:name, :value], [['abc', 1], ['def', 2]])
|
|
772
|
+
|
|
773
|
+
MYSQL_DB.sqls.should == [
|
|
774
|
+
SQL_BEGIN,
|
|
775
|
+
"INSERT INTO items (name, value) VALUES ('abc', 1), ('def', 2)",
|
|
776
|
+
SQL_COMMIT
|
|
777
|
+
]
|
|
778
|
+
|
|
779
|
+
@d.all.should == [
|
|
780
|
+
{:name => 'abc', :value => 1},
|
|
781
|
+
{:name => 'def', :value => 2}
|
|
782
|
+
]
|
|
783
|
+
end
|
|
784
|
+
end
|
|
785
|
+
|
|
786
|
+
context "MySQL::Dataset#replace" do
|
|
787
|
+
setup do
|
|
788
|
+
MYSQL_DB.drop_table(:items) if MYSQL_DB.table_exists?(:items)
|
|
789
|
+
MYSQL_DB.create_table :items do
|
|
790
|
+
integer :id, :unique => true
|
|
791
|
+
integer :value, :index => true
|
|
792
|
+
end
|
|
793
|
+
@d = MYSQL_DB[:items]
|
|
794
|
+
MYSQL_DB.sqls.clear
|
|
795
|
+
end
|
|
796
|
+
|
|
797
|
+
specify "should create a record if the condition is not met" do
|
|
798
|
+
@d.replace(:id => 111, :value => 333)
|
|
799
|
+
@d.all.should == [{:id => 111, :value => 333}]
|
|
800
|
+
end
|
|
801
|
+
|
|
802
|
+
specify "should update a record if the condition is met" do
|
|
803
|
+
@d << {:id => 111}
|
|
804
|
+
@d.all.should == [{:id => 111, :value => nil}]
|
|
805
|
+
@d.replace(:id => 111, :value => 333)
|
|
806
|
+
@d.all.should == [{:id => 111, :value => 333}]
|
|
807
|
+
end
|
|
808
|
+
end
|
|
809
|
+
|
|
810
|
+
context "MySQL::Dataset#complex_expression_sql" do
|
|
811
|
+
setup do
|
|
812
|
+
@d = MYSQL_DB.dataset
|
|
813
|
+
end
|
|
814
|
+
|
|
815
|
+
specify "should handle pattern matches correctly" do
|
|
816
|
+
@d.literal(:x.like('a')).should == "(x LIKE BINARY 'a')"
|
|
817
|
+
@d.literal(~:x.like('a')).should == "(x NOT LIKE BINARY 'a')"
|
|
818
|
+
@d.literal(:x.ilike('a')).should == "(x LIKE 'a')"
|
|
819
|
+
@d.literal(~:x.ilike('a')).should == "(x NOT LIKE 'a')"
|
|
820
|
+
@d.literal(:x.like(/a/)).should == "(x REGEXP BINARY 'a')"
|
|
821
|
+
@d.literal(~:x.like(/a/)).should == "(x NOT REGEXP BINARY 'a')"
|
|
822
|
+
@d.literal(:x.like(/a/i)).should == "(x REGEXP 'a')"
|
|
823
|
+
@d.literal(~:x.like(/a/i)).should == "(x NOT REGEXP 'a')"
|
|
824
|
+
end
|
|
825
|
+
|
|
826
|
+
specify "should handle string concatenation with CONCAT if more than one record" do
|
|
827
|
+
@d.literal([:x, :y].sql_string_join).should == "CONCAT(x, y)"
|
|
828
|
+
@d.literal([:x, :y].sql_string_join(' ')).should == "CONCAT(x, ' ', y)"
|
|
829
|
+
@d.literal([:x.sql_function(:y), 1, 'z'.lit].sql_string_join(:y.sql_subscript(1))).should == "CONCAT(x(y), y[1], '1', y[1], z)"
|
|
830
|
+
end
|
|
831
|
+
|
|
832
|
+
specify "should handle string concatenation as simple string if just one record" do
|
|
833
|
+
@d.literal([:x].sql_string_join).should == "x"
|
|
834
|
+
@d.literal([:x].sql_string_join(' ')).should == "x"
|
|
835
|
+
end
|
|
836
|
+
end
|
|
837
|
+
|
|
838
|
+
unless MYSQL_DB.class.adapter_scheme == :do
|
|
839
|
+
context "MySQL Stored Procedures" do
|
|
840
|
+
teardown do
|
|
841
|
+
MYSQL_DB.execute('DROP PROCEDURE test_sproc')
|
|
842
|
+
end
|
|
843
|
+
|
|
844
|
+
specify "should be callable on the database object" do
|
|
845
|
+
MYSQL_DB.execute('CREATE PROCEDURE test_sproc() BEGIN DELETE FROM items; END')
|
|
846
|
+
MYSQL_DB[:items].delete
|
|
847
|
+
MYSQL_DB[:items].insert(:value=>1)
|
|
848
|
+
MYSQL_DB[:items].count.should == 1
|
|
849
|
+
MYSQL_DB.call_sproc(:test_sproc)
|
|
850
|
+
MYSQL_DB[:items].count.should == 0
|
|
851
|
+
end
|
|
852
|
+
|
|
853
|
+
specify "should be callable on the dataset object" do
|
|
854
|
+
MYSQL_DB.execute('CREATE PROCEDURE test_sproc(a INTEGER) BEGIN SELECT *, a AS b FROM items; END')
|
|
855
|
+
MYSQL_DB[:items].delete
|
|
856
|
+
@d = MYSQL_DB[:items]
|
|
857
|
+
@d.call_sproc(:select, :test_sproc, 3).should == []
|
|
858
|
+
@d.insert(:value=>1)
|
|
859
|
+
@d.call_sproc(:select, :test_sproc, 4).should == [{:id=>nil, :value=>1, :b=>4}]
|
|
860
|
+
@d.row_proc = proc{|r| r.keys.each{|k| r[k] *= 2 if r[k].is_a?(Integer)}; r}
|
|
861
|
+
@d.call_sproc(:select, :test_sproc, 3).should == [{:id=>nil, :value=>2, :b=>6}]
|
|
862
|
+
end
|
|
863
|
+
|
|
864
|
+
specify "should be callable on the dataset object with multiple arguments" do
|
|
865
|
+
MYSQL_DB.execute('CREATE PROCEDURE test_sproc(a INTEGER, c INTEGER) BEGIN SELECT *, a AS b, c AS d FROM items; END')
|
|
866
|
+
MYSQL_DB[:items].delete
|
|
867
|
+
@d = MYSQL_DB[:items]
|
|
868
|
+
@d.call_sproc(:select, :test_sproc, 3, 4).should == []
|
|
869
|
+
@d.insert(:value=>1)
|
|
870
|
+
@d.call_sproc(:select, :test_sproc, 4, 5).should == [{:id=>nil, :value=>1, :b=>4, :d=>5}]
|
|
871
|
+
@d.row_proc = proc{|r| r.keys.each{|k| r[k] *= 2 if r[k].is_a?(Integer)}; r}
|
|
872
|
+
@d.call_sproc(:select, :test_sproc, 3, 4).should == [{:id=>nil, :value=>2, :b=>6, :d => 8}]
|
|
873
|
+
end
|
|
874
|
+
end
|
|
875
|
+
end
|