sequel 3.44.0 → 3.45.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. data/CHANGELOG +44 -0
  2. data/Rakefile +12 -4
  3. data/doc/reflection.rdoc +3 -3
  4. data/doc/release_notes/3.45.0.txt +179 -0
  5. data/doc/schema_modification.rdoc +1 -1
  6. data/doc/transactions.rdoc +23 -0
  7. data/lib/sequel/adapters/db2.rb +1 -0
  8. data/lib/sequel/adapters/ibmdb.rb +19 -3
  9. data/lib/sequel/adapters/jdbc.rb +15 -0
  10. data/lib/sequel/adapters/jdbc/derby.rb +1 -5
  11. data/lib/sequel/adapters/jdbc/h2.rb +1 -0
  12. data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -1
  13. data/lib/sequel/adapters/jdbc/jtds.rb +5 -0
  14. data/lib/sequel/adapters/jdbc/mysql.rb +5 -0
  15. data/lib/sequel/adapters/jdbc/oracle.rb +7 -1
  16. data/lib/sequel/adapters/jdbc/sqlite.rb +1 -1
  17. data/lib/sequel/adapters/jdbc/transactions.rb +28 -1
  18. data/lib/sequel/adapters/mysql.rb +4 -0
  19. data/lib/sequel/adapters/mysql2.rb +5 -1
  20. data/lib/sequel/adapters/oracle.rb +18 -0
  21. data/lib/sequel/adapters/postgres.rb +11 -1
  22. data/lib/sequel/adapters/shared/access.rb +14 -2
  23. data/lib/sequel/adapters/shared/cubrid.rb +1 -11
  24. data/lib/sequel/adapters/shared/db2.rb +11 -6
  25. data/lib/sequel/adapters/shared/mssql.rb +10 -10
  26. data/lib/sequel/adapters/shared/mysql.rb +11 -1
  27. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +17 -1
  28. data/lib/sequel/adapters/shared/oracle.rb +16 -15
  29. data/lib/sequel/adapters/shared/postgres.rb +91 -59
  30. data/lib/sequel/adapters/shared/sqlite.rb +1 -4
  31. data/lib/sequel/adapters/tinytds.rb +15 -0
  32. data/lib/sequel/connection_pool/threaded.rb +1 -1
  33. data/lib/sequel/core.rb +10 -0
  34. data/lib/sequel/database/connecting.rb +2 -0
  35. data/lib/sequel/database/misc.rb +46 -4
  36. data/lib/sequel/database/query.rb +33 -14
  37. data/lib/sequel/database/schema_methods.rb +0 -5
  38. data/lib/sequel/dataset/misc.rb +9 -0
  39. data/lib/sequel/dataset/mutation.rb +9 -7
  40. data/lib/sequel/dataset/sql.rb +13 -0
  41. data/lib/sequel/exceptions.rb +3 -0
  42. data/lib/sequel/extensions/connection_validator.rb +1 -1
  43. data/lib/sequel/extensions/date_arithmetic.rb +0 -8
  44. data/lib/sequel/extensions/eval_inspect.rb +2 -0
  45. data/lib/sequel/extensions/named_timezones.rb +18 -2
  46. data/lib/sequel/extensions/pg_array.rb +5 -1
  47. data/lib/sequel/extensions/pg_array_ops.rb +2 -0
  48. data/lib/sequel/extensions/pg_hstore.rb +2 -0
  49. data/lib/sequel/extensions/pg_hstore_ops.rb +2 -0
  50. data/lib/sequel/extensions/pg_json.rb +3 -1
  51. data/lib/sequel/extensions/pg_range.rb +2 -0
  52. data/lib/sequel/extensions/pg_range_ops.rb +2 -0
  53. data/lib/sequel/extensions/pg_row.rb +2 -0
  54. data/lib/sequel/extensions/pg_row_ops.rb +2 -0
  55. data/lib/sequel/extensions/query.rb +18 -22
  56. data/lib/sequel/model/associations.rb +3 -4
  57. data/lib/sequel/model/base.rb +2 -0
  58. data/lib/sequel/plugins/force_encoding.rb +2 -0
  59. data/lib/sequel/plugins/json_serializer.rb +155 -39
  60. data/lib/sequel/plugins/serialization.rb +14 -2
  61. data/lib/sequel/plugins/unlimited_update.rb +31 -0
  62. data/lib/sequel/plugins/validation_class_methods.rb +6 -4
  63. data/lib/sequel/plugins/xml_serializer.rb +133 -30
  64. data/lib/sequel/sql.rb +2 -0
  65. data/lib/sequel/timezones.rb +4 -0
  66. data/lib/sequel/version.rb +1 -1
  67. data/spec/adapters/mysql_spec.rb +0 -11
  68. data/spec/adapters/postgres_spec.rb +86 -54
  69. data/spec/adapters/spec_helper.rb +6 -0
  70. data/spec/core/connection_pool_spec.rb +16 -0
  71. data/spec/core/database_spec.rb +77 -1
  72. data/spec/core/dataset_spec.rb +30 -15
  73. data/spec/core/expression_filters_spec.rb +55 -13
  74. data/spec/core/mock_adapter_spec.rb +4 -0
  75. data/spec/core/schema_spec.rb +0 -2
  76. data/spec/core/spec_helper.rb +5 -0
  77. data/spec/core_extensions_spec.rb +33 -28
  78. data/spec/extensions/constraint_validations_spec.rb +2 -2
  79. data/spec/extensions/core_refinements_spec.rb +12 -12
  80. data/spec/extensions/json_serializer_spec.rb +137 -31
  81. data/spec/extensions/named_timezones_spec.rb +10 -0
  82. data/spec/extensions/pg_auto_parameterize_spec.rb +5 -0
  83. data/spec/extensions/pg_json_spec.rb +14 -0
  84. data/spec/extensions/pg_row_spec.rb +11 -0
  85. data/spec/extensions/pretty_table_spec.rb +2 -2
  86. data/spec/extensions/query_spec.rb +11 -8
  87. data/spec/extensions/serialization_spec.rb +20 -0
  88. data/spec/extensions/spec_helper.rb +8 -2
  89. data/spec/extensions/sql_expr_spec.rb +1 -1
  90. data/spec/extensions/unlimited_update_spec.rb +20 -0
  91. data/spec/extensions/xml_serializer_spec.rb +68 -16
  92. data/spec/integration/dataset_test.rb +28 -0
  93. data/spec/integration/spec_helper.rb +6 -0
  94. data/spec/integration/transaction_test.rb +39 -0
  95. data/spec/model/model_spec.rb +1 -1
  96. data/spec/sequel_coverage.rb +15 -0
  97. 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
@@ -656,9 +656,58 @@ shared_examples_for "Database#transaction" do
656
656
  a.should == [1, 1]
657
657
  end
658
658
 
659
- specify "should raise an error if attempting to use :disconnect=>:retry inside another transaction" do
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
@@ -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 ILIKE 'abc') OR (title ILIKE 'def') OR (body ILIKE 'abc') OR (body ILIKE 'def'))"
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 ILIKE 'abc') OR (body ILIKE 'abc')) AND ((title ILIKE 'def') OR (body ILIKE 'def')))"
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 ILIKE 'abc') OR (title ILIKE 'def')) AND ((body ILIKE 'abc') OR (body ILIKE 'def')))"
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 ILIKE 'abc') AND (body ILIKE 'abc') AND (title ILIKE 'def') AND (body ILIKE 'def'))"
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) ILIKE b)'
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' ILIKE 'b')")
792
- l(Sequel.ilike(:a, :b), "(a ILIKE b)")
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 ILIKE 'c') OR (a ~* 'b'))")
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
@@ -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
@@ -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 ILIKE \'a\')'
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 ILIKE \'a\') OR (x ILIKE \'b\'))'
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 ILIKE \'a\') OR (x ~* \'b\'))'
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' ILIKE x)"
141
- @d.l('a'.ilike(:x, 'b')).should == "(('a' ILIKE x) OR ('a' ILIKE 'b'))"
142
- @d.l('a'.ilike(:x, /b/)).should == "(('a' ILIKE x) OR ('a' ~* 'b'))"
143
- @d.l('a'.ilike(:x, /b/i)).should == "(('a' ILIKE x) OR ('a' ~* 'b'))"
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 ILIKE \'a\')'
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 ILIKE \'a\') AND (x NOT ILIKE \'b\'))'
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 ILIKE \'a\') AND (x !~* \'b\'))'
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 ILIKE x)"
164
- @d.l(~'a'.ilike(:x, 'b')).should == "(('a' NOT ILIKE x) AND ('a' NOT ILIKE 'b'))"
165
- @d.l(~'a'.ilike(:x, /b/)).should == "(('a' NOT ILIKE x) AND ('a' !~* 'b'))"
166
- @d.l(~'a'.ilike(:x, /b/i)).should == "(('a' NOT ILIKE x) AND ('a' !~* 'b'))"
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'))"