sequel 3.44.0 → 3.45.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +44 -0
- data/Rakefile +12 -4
- data/doc/reflection.rdoc +3 -3
- data/doc/release_notes/3.45.0.txt +179 -0
- data/doc/schema_modification.rdoc +1 -1
- data/doc/transactions.rdoc +23 -0
- data/lib/sequel/adapters/db2.rb +1 -0
- data/lib/sequel/adapters/ibmdb.rb +19 -3
- data/lib/sequel/adapters/jdbc.rb +15 -0
- data/lib/sequel/adapters/jdbc/derby.rb +1 -5
- data/lib/sequel/adapters/jdbc/h2.rb +1 -0
- data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -1
- data/lib/sequel/adapters/jdbc/jtds.rb +5 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +5 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +7 -1
- data/lib/sequel/adapters/jdbc/sqlite.rb +1 -1
- data/lib/sequel/adapters/jdbc/transactions.rb +28 -1
- data/lib/sequel/adapters/mysql.rb +4 -0
- data/lib/sequel/adapters/mysql2.rb +5 -1
- data/lib/sequel/adapters/oracle.rb +18 -0
- data/lib/sequel/adapters/postgres.rb +11 -1
- data/lib/sequel/adapters/shared/access.rb +14 -2
- data/lib/sequel/adapters/shared/cubrid.rb +1 -11
- data/lib/sequel/adapters/shared/db2.rb +11 -6
- data/lib/sequel/adapters/shared/mssql.rb +10 -10
- data/lib/sequel/adapters/shared/mysql.rb +11 -1
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +17 -1
- data/lib/sequel/adapters/shared/oracle.rb +16 -15
- data/lib/sequel/adapters/shared/postgres.rb +91 -59
- data/lib/sequel/adapters/shared/sqlite.rb +1 -4
- data/lib/sequel/adapters/tinytds.rb +15 -0
- data/lib/sequel/connection_pool/threaded.rb +1 -1
- data/lib/sequel/core.rb +10 -0
- data/lib/sequel/database/connecting.rb +2 -0
- data/lib/sequel/database/misc.rb +46 -4
- data/lib/sequel/database/query.rb +33 -14
- data/lib/sequel/database/schema_methods.rb +0 -5
- data/lib/sequel/dataset/misc.rb +9 -0
- data/lib/sequel/dataset/mutation.rb +9 -7
- data/lib/sequel/dataset/sql.rb +13 -0
- data/lib/sequel/exceptions.rb +3 -0
- data/lib/sequel/extensions/connection_validator.rb +1 -1
- data/lib/sequel/extensions/date_arithmetic.rb +0 -8
- data/lib/sequel/extensions/eval_inspect.rb +2 -0
- data/lib/sequel/extensions/named_timezones.rb +18 -2
- data/lib/sequel/extensions/pg_array.rb +5 -1
- data/lib/sequel/extensions/pg_array_ops.rb +2 -0
- data/lib/sequel/extensions/pg_hstore.rb +2 -0
- data/lib/sequel/extensions/pg_hstore_ops.rb +2 -0
- data/lib/sequel/extensions/pg_json.rb +3 -1
- data/lib/sequel/extensions/pg_range.rb +2 -0
- data/lib/sequel/extensions/pg_range_ops.rb +2 -0
- data/lib/sequel/extensions/pg_row.rb +2 -0
- data/lib/sequel/extensions/pg_row_ops.rb +2 -0
- data/lib/sequel/extensions/query.rb +18 -22
- data/lib/sequel/model/associations.rb +3 -4
- data/lib/sequel/model/base.rb +2 -0
- data/lib/sequel/plugins/force_encoding.rb +2 -0
- data/lib/sequel/plugins/json_serializer.rb +155 -39
- data/lib/sequel/plugins/serialization.rb +14 -2
- data/lib/sequel/plugins/unlimited_update.rb +31 -0
- data/lib/sequel/plugins/validation_class_methods.rb +6 -4
- data/lib/sequel/plugins/xml_serializer.rb +133 -30
- data/lib/sequel/sql.rb +2 -0
- data/lib/sequel/timezones.rb +4 -0
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mysql_spec.rb +0 -11
- data/spec/adapters/postgres_spec.rb +86 -54
- data/spec/adapters/spec_helper.rb +6 -0
- data/spec/core/connection_pool_spec.rb +16 -0
- data/spec/core/database_spec.rb +77 -1
- data/spec/core/dataset_spec.rb +30 -15
- data/spec/core/expression_filters_spec.rb +55 -13
- data/spec/core/mock_adapter_spec.rb +4 -0
- data/spec/core/schema_spec.rb +0 -2
- data/spec/core/spec_helper.rb +5 -0
- data/spec/core_extensions_spec.rb +33 -28
- data/spec/extensions/constraint_validations_spec.rb +2 -2
- data/spec/extensions/core_refinements_spec.rb +12 -12
- data/spec/extensions/json_serializer_spec.rb +137 -31
- data/spec/extensions/named_timezones_spec.rb +10 -0
- data/spec/extensions/pg_auto_parameterize_spec.rb +5 -0
- data/spec/extensions/pg_json_spec.rb +14 -0
- data/spec/extensions/pg_row_spec.rb +11 -0
- data/spec/extensions/pretty_table_spec.rb +2 -2
- data/spec/extensions/query_spec.rb +11 -8
- data/spec/extensions/serialization_spec.rb +20 -0
- data/spec/extensions/spec_helper.rb +8 -2
- data/spec/extensions/sql_expr_spec.rb +1 -1
- data/spec/extensions/unlimited_update_spec.rb +20 -0
- data/spec/extensions/xml_serializer_spec.rb +68 -16
- data/spec/integration/dataset_test.rb +28 -0
- data/spec/integration/spec_helper.rb +6 -0
- data/spec/integration/transaction_test.rb +39 -0
- data/spec/model/model_spec.rb +1 -1
- data/spec/sequel_coverage.rb +15 -0
- metadata +8 -3
@@ -1,5 +1,11 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'logger'
|
3
|
+
|
4
|
+
if ENV['COVERAGE']
|
5
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "../sequel_coverage")
|
6
|
+
SimpleCov.sequel_coverage(:group=>%r{lib/sequel/adapters})
|
7
|
+
end
|
8
|
+
|
3
9
|
unless Object.const_defined?('Sequel')
|
4
10
|
$:.unshift(File.join(File.dirname(File.expand_path(__FILE__)), "../../lib/"))
|
5
11
|
require 'sequel/no_core_ext'
|
@@ -256,6 +256,22 @@ shared_examples_for "A threaded connection pool" do
|
|
256
256
|
t.join
|
257
257
|
end
|
258
258
|
|
259
|
+
specify "should wait until a connection is available if all are checked out" do
|
260
|
+
pool = Sequel::ConnectionPool.get_pool(mock_db.call(&@icpp), @cp_opts.merge(:max_connections=>1, :pool_timeout=>0.1, :pool_sleep_time=>0))
|
261
|
+
q, q1 = Queue.new, Queue.new
|
262
|
+
t = Thread.new do
|
263
|
+
pool.hold do |c|
|
264
|
+
q1.push nil
|
265
|
+
3.times{Thread.pass}
|
266
|
+
q.pop
|
267
|
+
end
|
268
|
+
end
|
269
|
+
q1.pop
|
270
|
+
proc{pool.hold{}}.should raise_error(Sequel::PoolTimeout)
|
271
|
+
q.push nil
|
272
|
+
t.join
|
273
|
+
end
|
274
|
+
|
259
275
|
specify "should not have all_connections yield all available connections" do
|
260
276
|
pool = Sequel::ConnectionPool.get_pool(mock_db.call(&@icpp), @cp_opts.merge(:max_connections=>2, :pool_timeout=>0))
|
261
277
|
q, q1 = Queue.new, Queue.new
|
data/spec/core/database_spec.rb
CHANGED
@@ -656,9 +656,58 @@ shared_examples_for "Database#transaction" do
|
|
656
656
|
a.should == [1, 1]
|
657
657
|
end
|
658
658
|
|
659
|
-
specify "should
|
659
|
+
specify "should support :retry_on option for automatically retrying transactions" do
|
660
|
+
a = []
|
661
|
+
@db.transaction(:retry_on=>Sequel::DatabaseDisconnectError){a << 1; raise Sequel::DatabaseDisconnectError if a.length < 2}
|
662
|
+
@db.sqls.should == ['BEGIN', 'ROLLBACK', 'BEGIN', 'COMMIT']
|
663
|
+
a.should == [1, 1]
|
664
|
+
|
665
|
+
a = []
|
666
|
+
@db.transaction(:retry_on=>[Sequel::ConstraintViolation, Sequel::SerializationFailure]) do
|
667
|
+
a << 1
|
668
|
+
raise Sequel::SerializationFailure if a.length == 1
|
669
|
+
raise Sequel::ConstraintViolation if a.length == 2
|
670
|
+
end
|
671
|
+
@db.sqls.should == ['BEGIN', 'ROLLBACK', 'BEGIN', 'ROLLBACK', 'BEGIN', 'COMMIT']
|
672
|
+
a.should == [1, 1, 1]
|
673
|
+
end
|
674
|
+
|
675
|
+
specify "should support :num_retries option for limiting the number of retry times" do
|
676
|
+
a = []
|
677
|
+
lambda do
|
678
|
+
@db.transaction(:num_retries=>1, :retry_on=>[Sequel::ConstraintViolation, Sequel::SerializationFailure]) do
|
679
|
+
a << 1
|
680
|
+
raise Sequel::SerializationFailure if a.length == 1
|
681
|
+
raise Sequel::ConstraintViolation if a.length == 2
|
682
|
+
end
|
683
|
+
end.should raise_error(Sequel::ConstraintViolation)
|
684
|
+
@db.sqls.should == ['BEGIN', 'ROLLBACK', 'BEGIN', 'ROLLBACK']
|
685
|
+
a.should == [1, 1]
|
686
|
+
end
|
687
|
+
|
688
|
+
specify "should support :num_retries=>nil option to retry indefinitely" do
|
689
|
+
a = []
|
690
|
+
lambda do
|
691
|
+
@db.transaction(:num_retries=>nil, :retry_on=>[Sequel::ConstraintViolation]) do
|
692
|
+
a << 1
|
693
|
+
raise Sequel::SerializationFailure if a.length >= 100
|
694
|
+
raise Sequel::ConstraintViolation
|
695
|
+
end
|
696
|
+
end.should raise_error(Sequel::SerializationFailure)
|
697
|
+
@db.sqls.should == ['BEGIN', 'ROLLBACK'] * 100
|
698
|
+
a.should == [1] * 100
|
699
|
+
end
|
700
|
+
|
701
|
+
specify "should raise an error if using :disconnect=>:retry and :retry_on together" do
|
702
|
+
proc{@db.transaction(:disconnect=>:retry, :retry_on=>Sequel::ConstraintViolation){}}.should raise_error(Sequel::Error)
|
703
|
+
@db.sqls.should == []
|
704
|
+
end
|
705
|
+
|
706
|
+
specify "should raise an error if attempting to use :disconnect=>:retry or :retry_on inside another transaction" do
|
660
707
|
proc{@db.transaction{@db.transaction(:disconnect=>:retry){}}}.should raise_error(Sequel::Error)
|
661
708
|
@db.sqls.should == ['BEGIN', 'ROLLBACK']
|
709
|
+
proc{@db.transaction{@db.transaction(:retry_on=>Sequel::ConstraintViolation){}}}.should raise_error(Sequel::Error)
|
710
|
+
@db.sqls.should == ['BEGIN', 'ROLLBACK']
|
662
711
|
end
|
663
712
|
|
664
713
|
specify "should handle returning inside of the block by committing" do
|
@@ -2259,3 +2308,30 @@ describe "Database extensions" do
|
|
2259
2308
|
proc{@db.extension(:foo2)}.should raise_error(Sequel::Error)
|
2260
2309
|
end
|
2261
2310
|
end
|
2311
|
+
|
2312
|
+
describe "Database specific exception classes" do
|
2313
|
+
before do
|
2314
|
+
@db = Sequel.mock
|
2315
|
+
class << @db
|
2316
|
+
attr_accessor :sql_state
|
2317
|
+
|
2318
|
+
def database_exception_sqlstate(exception, opts={})
|
2319
|
+
@sql_state
|
2320
|
+
end
|
2321
|
+
end
|
2322
|
+
end
|
2323
|
+
|
2324
|
+
specify "should use appropriate exception classes for given SQL states" do
|
2325
|
+
@db.fetch = ArgumentError
|
2326
|
+
@db.sql_state = '23502'
|
2327
|
+
proc{@db.get(1)}.should raise_error(Sequel::NotNullConstraintViolation)
|
2328
|
+
@db.sql_state = '23503'
|
2329
|
+
proc{@db.get(1)}.should raise_error(Sequel::ForeignKeyConstraintViolation)
|
2330
|
+
@db.sql_state = '23505'
|
2331
|
+
proc{@db.get(1)}.should raise_error(Sequel::UniqueConstraintViolation)
|
2332
|
+
@db.sql_state = '23513'
|
2333
|
+
proc{@db.get(1)}.should raise_error(Sequel::CheckConstraintViolation)
|
2334
|
+
@db.sql_state = '40001'
|
2335
|
+
proc{@db.get(1)}.should raise_error(Sequel::SerializationFailure)
|
2336
|
+
end
|
2337
|
+
end
|
data/spec/core/dataset_spec.rb
CHANGED
@@ -575,8 +575,8 @@ describe "Dataset#where" do
|
|
575
575
|
@dataset.filter{gdp > d}.sql.should == "SELECT * FROM test WHERE (gdp > (SELECT avg(gdp) FROM test WHERE (region = 'Asia')))"
|
576
576
|
@dataset.filter{a < 1}.sql.should == 'SELECT * FROM test WHERE (a < 1)'
|
577
577
|
@dataset.filter{(a >= 1) & (b <= 2)}.sql.should == 'SELECT * FROM test WHERE ((a >= 1) AND (b <= 2))'
|
578
|
-
@dataset.filter{c.like 'ABC%'}.sql.should == "SELECT * FROM test WHERE (c LIKE 'ABC%')"
|
579
|
-
@dataset.filter{c.like 'ABC%', '%XYZ'}.sql.should == "SELECT * FROM test WHERE ((c LIKE 'ABC%') OR (c LIKE '%XYZ'))"
|
578
|
+
@dataset.filter{c.like 'ABC%'}.sql.should == "SELECT * FROM test WHERE (c LIKE 'ABC%' ESCAPE '\\')"
|
579
|
+
@dataset.filter{c.like 'ABC%', '%XYZ'}.sql.should == "SELECT * FROM test WHERE ((c LIKE 'ABC%' ESCAPE '\\') OR (c LIKE '%XYZ' ESCAPE '\\'))"
|
580
580
|
end
|
581
581
|
|
582
582
|
specify "should work for grouped datasets" do
|
@@ -2047,6 +2047,11 @@ describe "Dataset#from_self" do
|
|
2047
2047
|
ds.from(:a).with(:a, ds.from(:b)).from_self.sql.should == 'WITH a AS (SELECT * FROM b) SELECT * FROM (SELECT * FROM a) AS t1'
|
2048
2048
|
ds.from(:a, :c).with(:a, ds.from(:b)).with(:c, ds.from(:d)).from_self.sql.should == 'WITH a AS (SELECT * FROM b), c AS (SELECT * FROM d) SELECT * FROM (SELECT * FROM a, c) AS t1'
|
2049
2049
|
end
|
2050
|
+
|
2051
|
+
specify "should have working mutation method" do
|
2052
|
+
@ds.from_self!
|
2053
|
+
@ds.sql.should == 'SELECT * FROM (SELECT name FROM test LIMIT 1) AS t1'
|
2054
|
+
end
|
2050
2055
|
end
|
2051
2056
|
|
2052
2057
|
describe "Dataset#join_table" do
|
@@ -3194,47 +3199,47 @@ describe "Dataset#grep" do
|
|
3194
3199
|
end
|
3195
3200
|
|
3196
3201
|
specify "should format a SQL filter correctly" do
|
3197
|
-
@ds.grep(:title, 'ruby').sql.should == "SELECT * FROM posts WHERE ((title LIKE 'ruby'))"
|
3202
|
+
@ds.grep(:title, 'ruby').sql.should == "SELECT * FROM posts WHERE ((title LIKE 'ruby' ESCAPE '\\'))"
|
3198
3203
|
end
|
3199
3204
|
|
3200
3205
|
specify "should support multiple columns" do
|
3201
|
-
@ds.grep([:title, :body], 'ruby').sql.should == "SELECT * FROM posts WHERE ((title LIKE 'ruby') OR (body LIKE 'ruby'))"
|
3206
|
+
@ds.grep([:title, :body], 'ruby').sql.should == "SELECT * FROM posts WHERE ((title LIKE 'ruby' ESCAPE '\\') OR (body LIKE 'ruby' ESCAPE '\\'))"
|
3202
3207
|
end
|
3203
3208
|
|
3204
3209
|
specify "should support multiple search terms" do
|
3205
|
-
@ds.grep(:title, ['abc', 'def']).sql.should == "SELECT * FROM posts WHERE ((title LIKE 'abc') OR (title LIKE 'def'))"
|
3210
|
+
@ds.grep(:title, ['abc', 'def']).sql.should == "SELECT * FROM posts WHERE ((title LIKE 'abc' ESCAPE '\\') OR (title LIKE 'def' ESCAPE '\\'))"
|
3206
3211
|
end
|
3207
3212
|
|
3208
3213
|
specify "should support multiple columns and search terms" do
|
3209
|
-
@ds.grep([:title, :body], ['abc', 'def']).sql.should == "SELECT * FROM posts WHERE ((title LIKE 'abc') OR (title LIKE 'def') OR (body LIKE 'abc') OR (body LIKE 'def'))"
|
3214
|
+
@ds.grep([:title, :body], ['abc', 'def']).sql.should == "SELECT * FROM posts WHERE ((title LIKE 'abc' ESCAPE '\\') OR (title LIKE 'def' ESCAPE '\\') OR (body LIKE 'abc' ESCAPE '\\') OR (body LIKE 'def' ESCAPE '\\'))"
|
3210
3215
|
end
|
3211
3216
|
|
3212
3217
|
specify "should support the :all_patterns option" do
|
3213
|
-
@ds.grep([:title, :body], ['abc', 'def'], :all_patterns=>true).sql.should == "SELECT * FROM posts WHERE (((title LIKE 'abc') OR (body LIKE 'abc')) AND ((title LIKE 'def') OR (body LIKE 'def')))"
|
3218
|
+
@ds.grep([:title, :body], ['abc', 'def'], :all_patterns=>true).sql.should == "SELECT * FROM posts WHERE (((title LIKE 'abc' ESCAPE '\\') OR (body LIKE 'abc' ESCAPE '\\')) AND ((title LIKE 'def' ESCAPE '\\') OR (body LIKE 'def' ESCAPE '\\')))"
|
3214
3219
|
end
|
3215
3220
|
|
3216
3221
|
specify "should support the :all_columns option" do
|
3217
|
-
@ds.grep([:title, :body], ['abc', 'def'], :all_columns=>true).sql.should == "SELECT * FROM posts WHERE (((title LIKE 'abc') OR (title LIKE 'def')) AND ((body LIKE 'abc') OR (body LIKE 'def')))"
|
3222
|
+
@ds.grep([:title, :body], ['abc', 'def'], :all_columns=>true).sql.should == "SELECT * FROM posts WHERE (((title LIKE 'abc' ESCAPE '\\') OR (title LIKE 'def' ESCAPE '\\')) AND ((body LIKE 'abc' ESCAPE '\\') OR (body LIKE 'def' ESCAPE '\\')))"
|
3218
3223
|
end
|
3219
3224
|
|
3220
3225
|
specify "should support the :case_insensitive option" do
|
3221
|
-
@ds.grep([:title, :body], ['abc', 'def'], :case_insensitive=>true).sql.should == "SELECT * FROM posts WHERE ((title
|
3226
|
+
@ds.grep([:title, :body], ['abc', 'def'], :case_insensitive=>true).sql.should == "SELECT * FROM posts WHERE ((UPPER(title) LIKE UPPER('abc') ESCAPE '\\') OR (UPPER(title) LIKE UPPER('def') ESCAPE '\\') OR (UPPER(body) LIKE UPPER('abc') ESCAPE '\\') OR (UPPER(body) LIKE UPPER('def') ESCAPE '\\'))"
|
3222
3227
|
end
|
3223
3228
|
|
3224
3229
|
specify "should support the :all_patterns and :all_columns options together" do
|
3225
|
-
@ds.grep([:title, :body], ['abc', 'def'], :all_patterns=>true, :all_columns=>true).sql.should == "SELECT * FROM posts WHERE ((title LIKE 'abc') AND (body LIKE 'abc') AND (title LIKE 'def') AND (body LIKE 'def'))"
|
3230
|
+
@ds.grep([:title, :body], ['abc', 'def'], :all_patterns=>true, :all_columns=>true).sql.should == "SELECT * FROM posts WHERE ((title LIKE 'abc' ESCAPE '\\') AND (body LIKE 'abc' ESCAPE '\\') AND (title LIKE 'def' ESCAPE '\\') AND (body LIKE 'def' ESCAPE '\\'))"
|
3226
3231
|
end
|
3227
3232
|
|
3228
3233
|
specify "should support the :all_patterns and :case_insensitive options together" do
|
3229
|
-
@ds.grep([:title, :body], ['abc', 'def'], :all_patterns=>true, :case_insensitive=>true).sql.should == "SELECT * FROM posts WHERE (((title
|
3234
|
+
@ds.grep([:title, :body], ['abc', 'def'], :all_patterns=>true, :case_insensitive=>true).sql.should == "SELECT * FROM posts WHERE (((UPPER(title) LIKE UPPER('abc') ESCAPE '\\') OR (UPPER(body) LIKE UPPER('abc') ESCAPE '\\')) AND ((UPPER(title) LIKE UPPER('def') ESCAPE '\\') OR (UPPER(body) LIKE UPPER('def') ESCAPE '\\')))"
|
3230
3235
|
end
|
3231
3236
|
|
3232
3237
|
specify "should support the :all_columns and :case_insensitive options together" do
|
3233
|
-
@ds.grep([:title, :body], ['abc', 'def'], :all_columns=>true, :case_insensitive=>true).sql.should == "SELECT * FROM posts WHERE (((title
|
3238
|
+
@ds.grep([:title, :body], ['abc', 'def'], :all_columns=>true, :case_insensitive=>true).sql.should == "SELECT * FROM posts WHERE (((UPPER(title) LIKE UPPER('abc') ESCAPE '\\') OR (UPPER(title) LIKE UPPER('def') ESCAPE '\\')) AND ((UPPER(body) LIKE UPPER('abc') ESCAPE '\\') OR (UPPER(body) LIKE UPPER('def') ESCAPE '\\')))"
|
3234
3239
|
end
|
3235
3240
|
|
3236
3241
|
specify "should support the :all_patterns, :all_columns, and :case_insensitive options together" do
|
3237
|
-
@ds.grep([:title, :body], ['abc', 'def'], :all_patterns=>true, :all_columns=>true, :case_insensitive=>true).sql.should == "SELECT * FROM posts WHERE ((title
|
3242
|
+
@ds.grep([:title, :body], ['abc', 'def'], :all_patterns=>true, :all_columns=>true, :case_insensitive=>true).sql.should == "SELECT * FROM posts WHERE ((UPPER(title) LIKE UPPER('abc') ESCAPE '\\') AND (UPPER(body) LIKE UPPER('abc') ESCAPE '\\') AND (UPPER(title) LIKE UPPER('def') ESCAPE '\\') AND (UPPER(body) LIKE UPPER('def') ESCAPE '\\'))"
|
3238
3243
|
end
|
3239
3244
|
|
3240
3245
|
specify "should not support regexps if the database doesn't supports it" do
|
@@ -3245,11 +3250,11 @@ describe "Dataset#grep" do
|
|
3245
3250
|
specify "should support regexps if the database supports it" do
|
3246
3251
|
def @ds.supports_regexp?; true end
|
3247
3252
|
@ds.grep(:title, /ruby/).sql.should == "SELECT * FROM posts WHERE ((title ~ 'ruby'))"
|
3248
|
-
@ds.grep(:title, [/^ruby/, 'ruby']).sql.should == "SELECT * FROM posts WHERE ((title ~ '^ruby') OR (title LIKE 'ruby'))"
|
3253
|
+
@ds.grep(:title, [/^ruby/, 'ruby']).sql.should == "SELECT * FROM posts WHERE ((title ~ '^ruby') OR (title LIKE 'ruby' ESCAPE '\\'))"
|
3249
3254
|
end
|
3250
3255
|
|
3251
3256
|
specify "should support searching against other columns" do
|
3252
|
-
@ds.grep(:title, :body).sql.should == "SELECT * FROM posts WHERE ((title LIKE body))"
|
3257
|
+
@ds.grep(:title, :body).sql.should == "SELECT * FROM posts WHERE ((title LIKE body ESCAPE '\\'))"
|
3253
3258
|
end
|
3254
3259
|
end
|
3255
3260
|
|
@@ -4608,3 +4613,13 @@ describe "Dataset#paged_each" do
|
|
4608
4613
|
end
|
4609
4614
|
end
|
4610
4615
|
|
4616
|
+
describe "Dataset#escape_like" do
|
4617
|
+
before do
|
4618
|
+
@ds = Sequel.mock[:test]
|
4619
|
+
end
|
4620
|
+
|
4621
|
+
it "should escape % and _ and \\ characters" do
|
4622
|
+
@ds.escape_like("foo\\%_bar").should == "foo\\\\\\%\\_bar"
|
4623
|
+
end
|
4624
|
+
end
|
4625
|
+
|
@@ -96,9 +96,9 @@ describe "Blockless Ruby Filters" do
|
|
96
96
|
@d.lit(Sequel.expr(:x) - ~Sequel.expr(:y)).should == '(x - NOT y)'
|
97
97
|
@d.lit(Sequel.expr(:x) / (Sequel.expr(:y) & :z)).should == '(x / (y AND z))'
|
98
98
|
@d.lit(Sequel.expr(:x) * (Sequel.expr(:y) | :z)).should == '(x * (y OR z))'
|
99
|
-
@d.lit(Sequel.expr(:x) + Sequel.expr(:y).like('a')).should == "(x + (y LIKE 'a'))"
|
100
|
-
@d.lit(Sequel.expr(:x) - ~Sequel.expr(:y).like('a')).should == "(x - (y NOT LIKE 'a'))"
|
101
|
-
@d.lit(Sequel.join([:x, ~Sequel.expr(:y).like('a')])).should == "(x || (y NOT LIKE 'a'))"
|
99
|
+
@d.lit(Sequel.expr(:x) + Sequel.expr(:y).like('a')).should == "(x + (y LIKE 'a' ESCAPE '\\'))"
|
100
|
+
@d.lit(Sequel.expr(:x) - ~Sequel.expr(:y).like('a')).should == "(x - (y NOT LIKE 'a' ESCAPE '\\'))"
|
101
|
+
@d.lit(Sequel.join([:x, ~Sequel.expr(:y).like('a')])).should == "(x || (y NOT LIKE 'a' ESCAPE '\\'))"
|
102
102
|
end
|
103
103
|
|
104
104
|
it "should support AND conditions via &" do
|
@@ -143,7 +143,7 @@ describe "Blockless Ruby Filters" do
|
|
143
143
|
@d.l(Sequel.expr(Sequel.lit('y') => Sequel.lit('z')) & Sequel.lit('x')).should == '((y = z) AND x)'
|
144
144
|
@d.l((Sequel.lit('x') > 200) & (Sequel.lit('y') < 200)).should == '((x > 200) AND (y < 200))'
|
145
145
|
@d.l(~(Sequel.lit('x') + 1 > 100)).should == '((x + 1) <= 100)'
|
146
|
-
@d.l(Sequel.lit('x').like('a')).should == '(x LIKE \'a\')'
|
146
|
+
@d.l(Sequel.lit('x').like('a')).should == '(x LIKE \'a\' ESCAPE \'\\\')'
|
147
147
|
@d.l(Sequel.lit('x') + 1 > 100).should == '((x + 1) > 100)'
|
148
148
|
@d.l((Sequel.lit('x') * :y) < 100.01).should == '((x * y) < 100.01)'
|
149
149
|
@d.l((Sequel.lit('x') - Sequel.expr(:y)/2) >= 100000000000000000000000000000000000).should == '((x - (y / 2)) >= 100000000000000000000000000000000000)'
|
@@ -380,8 +380,8 @@ describe "Blockless Ruby Filters" do
|
|
380
380
|
@d.lit(d.asc).should == '(SELECT a FROM items) ASC'
|
381
381
|
@d.lit(d.desc).should == '(SELECT a FROM items) DESC'
|
382
382
|
|
383
|
-
@d.lit(d.like(:b)).should == '((SELECT a FROM items) LIKE b)'
|
384
|
-
@d.lit(d.ilike(:b)).should == '((SELECT a FROM items)
|
383
|
+
@d.lit(d.like(:b)).should == '((SELECT a FROM items) LIKE b ESCAPE \'\\\')'
|
384
|
+
@d.lit(d.ilike(:b)).should == '(UPPER((SELECT a FROM items)) LIKE UPPER(b) ESCAPE \'\\\')'
|
385
385
|
end
|
386
386
|
|
387
387
|
it "should handled emulated char_length function" do
|
@@ -781,17 +781,17 @@ describe "Sequel core extension replacements" do
|
|
781
781
|
end
|
782
782
|
|
783
783
|
it "Sequel.like should use a LIKE expression" do
|
784
|
-
l(Sequel.like('a', 'b'), "('a' LIKE 'b')")
|
785
|
-
l(Sequel.like(:a, :b), "(a LIKE b)")
|
784
|
+
l(Sequel.like('a', 'b'), "('a' LIKE 'b' ESCAPE '\\')")
|
785
|
+
l(Sequel.like(:a, :b), "(a LIKE b ESCAPE '\\')")
|
786
786
|
l(Sequel.like(:a, /b/), "(a ~ 'b')")
|
787
|
-
l(Sequel.like(:a, 'c', /b/), "((a LIKE 'c') OR (a ~ 'b'))")
|
787
|
+
l(Sequel.like(:a, 'c', /b/), "((a LIKE 'c' ESCAPE '\\') OR (a ~ 'b'))")
|
788
788
|
end
|
789
789
|
|
790
790
|
it "Sequel.ilike should use an ILIKE expression" do
|
791
|
-
l(Sequel.ilike('a', 'b'), "('a'
|
792
|
-
l(Sequel.ilike(:a, :b), "(a
|
791
|
+
l(Sequel.ilike('a', 'b'), "(UPPER('a') LIKE UPPER('b') ESCAPE '\\')")
|
792
|
+
l(Sequel.ilike(:a, :b), "(UPPER(a) LIKE UPPER(b) ESCAPE '\\')")
|
793
793
|
l(Sequel.ilike(:a, /b/), "(a ~* 'b')")
|
794
|
-
l(Sequel.ilike(:a, 'c', /b/), "((a
|
794
|
+
l(Sequel.ilike(:a, 'c', /b/), "((UPPER(a) LIKE UPPER('c') ESCAPE '\\') OR (a ~* 'b'))")
|
795
795
|
end
|
796
796
|
|
797
797
|
it "Sequel.subscript should use an SQL subscript" do
|
@@ -913,7 +913,7 @@ describe "Sequel::SQL::Wrapper" do
|
|
913
913
|
@ds.literal(s & true).should == "(foo AND 't')"
|
914
914
|
@ds.literal(s < 1).should == "(foo < 1)"
|
915
915
|
@ds.literal(s.sql_subscript(1)).should == "foo[1]"
|
916
|
-
@ds.literal(s.like('a')).should == "(foo LIKE 'a')"
|
916
|
+
@ds.literal(s.like('a')).should == "(foo LIKE 'a' ESCAPE '\\')"
|
917
917
|
@ds.literal(s.as(:a)).should == "foo AS a"
|
918
918
|
@ds.literal(s.cast(Integer)).should == "CAST(foo AS integer)"
|
919
919
|
@ds.literal(s.desc).should == "foo DESC"
|
@@ -1009,3 +1009,45 @@ describe "Sequel.delay" do
|
|
1009
1009
|
proc{Sequel.delay}.should raise_error(Sequel::Error)
|
1010
1010
|
end
|
1011
1011
|
end
|
1012
|
+
|
1013
|
+
describe "Sequel.parse_json" do
|
1014
|
+
before do
|
1015
|
+
Sequel::JSON = Object.new
|
1016
|
+
def (Sequel::JSON).parse(json, opts={})
|
1017
|
+
[json, opts]
|
1018
|
+
end
|
1019
|
+
end
|
1020
|
+
after do
|
1021
|
+
Sequel.send(:remove_const, :JSON)
|
1022
|
+
end
|
1023
|
+
|
1024
|
+
specify "should parse json correctly" do
|
1025
|
+
Sequel.parse_json('[]').should == ['[]', {:create_additions=>false}]
|
1026
|
+
end
|
1027
|
+
end
|
1028
|
+
|
1029
|
+
describe "Sequel::LiteralString" do
|
1030
|
+
before do
|
1031
|
+
@s = Sequel::LiteralString.new("? = ?")
|
1032
|
+
end
|
1033
|
+
|
1034
|
+
specify "should have lit return self if no arguments" do
|
1035
|
+
@s.lit.should equal(@s)
|
1036
|
+
end
|
1037
|
+
|
1038
|
+
specify "should have lit return self if return a placeholder literal string if arguments" do
|
1039
|
+
@s.lit(1, 2).should be_a_kind_of(Sequel::SQL::PlaceholderLiteralString)
|
1040
|
+
Sequel.mock.literal(@s.lit(1, :a)).should == '1 = a'
|
1041
|
+
end
|
1042
|
+
|
1043
|
+
specify "should have to_sequel_blob convert to blob" do
|
1044
|
+
@s.to_sequel_blob.should == @s
|
1045
|
+
@s.to_sequel_blob.should be_a_kind_of(Sequel::SQL::Blob)
|
1046
|
+
end
|
1047
|
+
end
|
1048
|
+
|
1049
|
+
describe "Sequel core extensions" do
|
1050
|
+
specify "should have Sequel.core_extensions? be false by default" do
|
1051
|
+
Sequel.core_extensions?.should be_false
|
1052
|
+
end
|
1053
|
+
end
|
@@ -448,6 +448,10 @@ describe "Sequel Mock Adapter" do
|
|
448
448
|
Sequel.mock(:host=>'postgres').primary_key(:t).should == :id
|
449
449
|
end
|
450
450
|
|
451
|
+
specify "should stub out the bound_variable_arg method for postgres" do
|
452
|
+
Sequel.mock(:host=>'postgres').bound_variable_arg(:t, nil).should == :t
|
453
|
+
end
|
454
|
+
|
451
455
|
specify "should handle creating tables on oracle" do
|
452
456
|
proc{Sequel.mock(:host=>'oracle').create_table(:a){String :b}}.should_not raise_error
|
453
457
|
end
|
data/spec/core/schema_spec.rb
CHANGED
@@ -1393,7 +1393,6 @@ describe "Schema Parser" do
|
|
1393
1393
|
@db.schema(:"number(10,0)").first.last[:type].should == :integer
|
1394
1394
|
@db.schema(:"numeric(10, 10)").first.last[:type].should == :decimal
|
1395
1395
|
@db.schema(:"decimal(10,1)").first.last[:type].should == :decimal
|
1396
|
-
@db.schema(:money).first.last[:type].should == :decimal
|
1397
1396
|
@db.schema(:bytea).first.last[:type].should == :blob
|
1398
1397
|
@db.schema(:blob).first.last[:type].should == :blob
|
1399
1398
|
@db.schema(:image).first.last[:type].should == :blob
|
@@ -1401,7 +1400,6 @@ describe "Schema Parser" do
|
|
1401
1400
|
@db.schema(:nvarchar).first.last[:type].should == :string
|
1402
1401
|
@db.schema(:ntext).first.last[:type].should == :string
|
1403
1402
|
@db.schema(:smalldatetime).first.last[:type].should == :datetime
|
1404
|
-
@db.schema(:smallmoney).first.last[:type].should == :decimal
|
1405
1403
|
@db.schema(:binary).first.last[:type].should == :blob
|
1406
1404
|
@db.schema(:varbinary).first.last[:type].should == :blob
|
1407
1405
|
@db.schema(:enum).first.last[:type].should == :enum
|
data/spec/core/spec_helper.rb
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
|
3
|
+
if ENV['COVERAGE']
|
4
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "../sequel_coverage")
|
5
|
+
SimpleCov.sequel_coverage(:filter=>%r{lib/sequel/(\w+\.rb|(dataset|database|model|connection_pool)/\w+\.rb|adapters/mock\.rb)\z})
|
6
|
+
end
|
7
|
+
|
3
8
|
unless Object.const_defined?('Sequel')
|
4
9
|
$:.unshift(File.join(File.dirname(File.expand_path(__FILE__)), "../../lib/"))
|
5
10
|
SEQUEL_NO_CORE_EXTENSIONS = true
|
@@ -1,5 +1,10 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
|
3
|
+
if ENV['COVERAGE']
|
4
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), "sequel_coverage")
|
5
|
+
SimpleCov.sequel_coverage(:filter=>%r{lib/sequel/extensions/core_extensions\.rb\z})
|
6
|
+
end
|
7
|
+
|
3
8
|
unless Object.const_defined?('Sequel') && Sequel.const_defined?('Model')
|
4
9
|
$:.unshift(File.join(File.dirname(File.expand_path(__FILE__)), "../../lib/"))
|
5
10
|
require 'sequel/no_core_ext'
|
@@ -85,16 +90,16 @@ describe "Core extensions" do
|
|
85
90
|
end
|
86
91
|
|
87
92
|
it "should support LIKE via Symbol#like" do
|
88
|
-
@d.l(:x.like('a')).should == '(x LIKE \'a\')'
|
93
|
+
@d.l(:x.like('a')).should == '(x LIKE \'a\' ESCAPE \'\\\')'
|
89
94
|
@d.l(:x.like(/a/)).should == '(x ~ \'a\')'
|
90
|
-
@d.l(:x.like('a', 'b')).should == '((x LIKE \'a\') OR (x LIKE \'b\'))'
|
95
|
+
@d.l(:x.like('a', 'b')).should == '((x LIKE \'a\' ESCAPE \'\\\') OR (x LIKE \'b\' ESCAPE \'\\\'))'
|
91
96
|
@d.l(:x.like(/a/, /b/i)).should == '((x ~ \'a\') OR (x ~* \'b\'))'
|
92
|
-
@d.l(:x.like('a', /b/)).should == '((x LIKE \'a\') OR (x ~ \'b\'))'
|
97
|
+
@d.l(:x.like('a', /b/)).should == '((x LIKE \'a\' ESCAPE \'\\\') OR (x ~ \'b\'))'
|
93
98
|
|
94
|
-
@d.l('a'.like(:x)).should == "('a' LIKE x)"
|
95
|
-
@d.l('a'.like(:x, 'b')).should == "(('a' LIKE x) OR ('a' LIKE 'b'))"
|
96
|
-
@d.l('a'.like(:x, /b/)).should == "(('a' LIKE x) OR ('a' ~ 'b'))"
|
97
|
-
@d.l('a'.like(:x, /b/i)).should == "(('a' LIKE x) OR ('a' ~* 'b'))"
|
99
|
+
@d.l('a'.like(:x)).should == "('a' LIKE x ESCAPE '\\')"
|
100
|
+
@d.l('a'.like(:x, 'b')).should == "(('a' LIKE x ESCAPE '\\') OR ('a' LIKE 'b' ESCAPE '\\'))"
|
101
|
+
@d.l('a'.like(:x, /b/)).should == "(('a' LIKE x ESCAPE '\\') OR ('a' ~ 'b'))"
|
102
|
+
@d.l('a'.like(:x, /b/i)).should == "(('a' LIKE x ESCAPE '\\') OR ('a' ~* 'b'))"
|
98
103
|
|
99
104
|
@d.l(/a/.like(:x)).should == "('a' ~ x)"
|
100
105
|
@d.l(/a/.like(:x, 'b')).should == "(('a' ~ x) OR ('a' ~ 'b'))"
|
@@ -108,16 +113,16 @@ describe "Core extensions" do
|
|
108
113
|
end
|
109
114
|
|
110
115
|
it "should support NOT LIKE via Symbol#like and Symbol#~" do
|
111
|
-
@d.l(~:x.like('a')).should == '(x NOT LIKE \'a\')'
|
116
|
+
@d.l(~:x.like('a')).should == '(x NOT LIKE \'a\' ESCAPE \'\\\')'
|
112
117
|
@d.l(~:x.like(/a/)).should == '(x !~ \'a\')'
|
113
|
-
@d.l(~:x.like('a', 'b')).should == '((x NOT LIKE \'a\') AND (x NOT LIKE \'b\'))'
|
118
|
+
@d.l(~:x.like('a', 'b')).should == '((x NOT LIKE \'a\' ESCAPE \'\\\') AND (x NOT LIKE \'b\' ESCAPE \'\\\'))'
|
114
119
|
@d.l(~:x.like(/a/, /b/i)).should == '((x !~ \'a\') AND (x !~* \'b\'))'
|
115
|
-
@d.l(~:x.like('a', /b/)).should == '((x NOT LIKE \'a\') AND (x !~ \'b\'))'
|
120
|
+
@d.l(~:x.like('a', /b/)).should == '((x NOT LIKE \'a\' ESCAPE \'\\\') AND (x !~ \'b\'))'
|
116
121
|
|
117
|
-
@d.l(~'a'.like(:x)).should == "('a' NOT LIKE x)"
|
118
|
-
@d.l(~'a'.like(:x, 'b')).should == "(('a' NOT LIKE x) AND ('a' NOT LIKE 'b'))"
|
119
|
-
@d.l(~'a'.like(:x, /b/)).should == "(('a' NOT LIKE x) AND ('a' !~ 'b'))"
|
120
|
-
@d.l(~'a'.like(:x, /b/i)).should == "(('a' NOT LIKE x) AND ('a' !~* 'b'))"
|
122
|
+
@d.l(~'a'.like(:x)).should == "('a' NOT LIKE x ESCAPE '\\')"
|
123
|
+
@d.l(~'a'.like(:x, 'b')).should == "(('a' NOT LIKE x ESCAPE '\\') AND ('a' NOT LIKE 'b' ESCAPE '\\'))"
|
124
|
+
@d.l(~'a'.like(:x, /b/)).should == "(('a' NOT LIKE x ESCAPE '\\') AND ('a' !~ 'b'))"
|
125
|
+
@d.l(~'a'.like(:x, /b/i)).should == "(('a' NOT LIKE x ESCAPE '\\') AND ('a' !~* 'b'))"
|
121
126
|
|
122
127
|
@d.l(~/a/.like(:x)).should == "('a' !~ x)"
|
123
128
|
@d.l(~/a/.like(:x, 'b')).should == "(('a' !~ x) AND ('a' !~ 'b'))"
|
@@ -131,16 +136,16 @@ describe "Core extensions" do
|
|
131
136
|
end
|
132
137
|
|
133
138
|
it "should support ILIKE via Symbol#ilike" do
|
134
|
-
@d.l(:x.ilike('a')).should == '(x
|
139
|
+
@d.l(:x.ilike('a')).should == '(UPPER(x) LIKE UPPER(\'a\') ESCAPE \'\\\')'
|
135
140
|
@d.l(:x.ilike(/a/)).should == '(x ~* \'a\')'
|
136
|
-
@d.l(:x.ilike('a', 'b')).should == '((x
|
141
|
+
@d.l(:x.ilike('a', 'b')).should == '((UPPER(x) LIKE UPPER(\'a\') ESCAPE \'\\\') OR (UPPER(x) LIKE UPPER(\'b\') ESCAPE \'\\\'))'
|
137
142
|
@d.l(:x.ilike(/a/, /b/i)).should == '((x ~* \'a\') OR (x ~* \'b\'))'
|
138
|
-
@d.l(:x.ilike('a', /b/)).should == '((x
|
143
|
+
@d.l(:x.ilike('a', /b/)).should == '((UPPER(x) LIKE UPPER(\'a\') ESCAPE \'\\\') OR (x ~* \'b\'))'
|
139
144
|
|
140
|
-
@d.l('a'.ilike(:x)).should == "('a'
|
141
|
-
@d.l('a'.ilike(:x, 'b')).should == "(('a'
|
142
|
-
@d.l('a'.ilike(:x, /b/)).should == "(('a'
|
143
|
-
@d.l('a'.ilike(:x, /b/i)).should == "(('a'
|
145
|
+
@d.l('a'.ilike(:x)).should == "(UPPER('a') LIKE UPPER(x) ESCAPE '\\')"
|
146
|
+
@d.l('a'.ilike(:x, 'b')).should == "((UPPER('a') LIKE UPPER(x) ESCAPE '\\') OR (UPPER('a') LIKE UPPER('b') ESCAPE '\\'))"
|
147
|
+
@d.l('a'.ilike(:x, /b/)).should == "((UPPER('a') LIKE UPPER(x) ESCAPE '\\') OR ('a' ~* 'b'))"
|
148
|
+
@d.l('a'.ilike(:x, /b/i)).should == "((UPPER('a') LIKE UPPER(x) ESCAPE '\\') OR ('a' ~* 'b'))"
|
144
149
|
|
145
150
|
@d.l(/a/.ilike(:x)).should == "('a' ~* x)"
|
146
151
|
@d.l(/a/.ilike(:x, 'b')).should == "(('a' ~* x) OR ('a' ~* 'b'))"
|
@@ -154,16 +159,16 @@ describe "Core extensions" do
|
|
154
159
|
end
|
155
160
|
|
156
161
|
it "should support NOT ILIKE via Symbol#ilike and Symbol#~" do
|
157
|
-
@d.l(~:x.ilike('a')).should == '(x NOT
|
162
|
+
@d.l(~:x.ilike('a')).should == '(UPPER(x) NOT LIKE UPPER(\'a\') ESCAPE \'\\\')'
|
158
163
|
@d.l(~:x.ilike(/a/)).should == '(x !~* \'a\')'
|
159
|
-
@d.l(~:x.ilike('a', 'b')).should == '((x NOT
|
164
|
+
@d.l(~:x.ilike('a', 'b')).should == '((UPPER(x) NOT LIKE UPPER(\'a\') ESCAPE \'\\\') AND (UPPER(x) NOT LIKE UPPER(\'b\') ESCAPE \'\\\'))'
|
160
165
|
@d.l(~:x.ilike(/a/, /b/i)).should == '((x !~* \'a\') AND (x !~* \'b\'))'
|
161
|
-
@d.l(~:x.ilike('a', /b/)).should == '((x NOT
|
166
|
+
@d.l(~:x.ilike('a', /b/)).should == '((UPPER(x) NOT LIKE UPPER(\'a\') ESCAPE \'\\\') AND (x !~* \'b\'))'
|
162
167
|
|
163
|
-
@d.l(~'a'.ilike(:x)).should == "('a' NOT
|
164
|
-
@d.l(~'a'.ilike(:x, 'b')).should == "(('a' NOT
|
165
|
-
@d.l(~'a'.ilike(:x, /b/)).should == "(('a' NOT
|
166
|
-
@d.l(~'a'.ilike(:x, /b/i)).should == "(('a' NOT
|
168
|
+
@d.l(~'a'.ilike(:x)).should == "(UPPER('a') NOT LIKE UPPER(x) ESCAPE '\\')"
|
169
|
+
@d.l(~'a'.ilike(:x, 'b')).should == "((UPPER('a') NOT LIKE UPPER(x) ESCAPE '\\') AND (UPPER('a') NOT LIKE UPPER('b') ESCAPE '\\'))"
|
170
|
+
@d.l(~'a'.ilike(:x, /b/)).should == "((UPPER('a') NOT LIKE UPPER(x) ESCAPE '\\') AND ('a' !~* 'b'))"
|
171
|
+
@d.l(~'a'.ilike(:x, /b/i)).should == "((UPPER('a') NOT LIKE UPPER(x) ESCAPE '\\') AND ('a' !~* 'b'))"
|
167
172
|
|
168
173
|
@d.l(~/a/.ilike(:x)).should == "('a' !~* x)"
|
169
174
|
@d.l(~/a/.ilike(:x, 'b')).should == "(('a' !~* x) AND ('a' !~* 'b'))"
|