sequel 4.10.0 → 4.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +58 -0
  3. data/doc/association_basics.rdoc +1 -1
  4. data/doc/cheat_sheet.rdoc +0 -1
  5. data/doc/core_extensions.rdoc +2 -2
  6. data/doc/dataset_filtering.rdoc +5 -5
  7. data/doc/model_hooks.rdoc +9 -0
  8. data/doc/object_model.rdoc +7 -13
  9. data/doc/opening_databases.rdoc +3 -1
  10. data/doc/querying.rdoc +8 -8
  11. data/doc/release_notes/4.11.0.txt +147 -0
  12. data/doc/sql.rdoc +11 -7
  13. data/doc/virtual_rows.rdoc +4 -5
  14. data/lib/sequel/adapters/ibmdb.rb +24 -16
  15. data/lib/sequel/adapters/jdbc/h2.rb +5 -0
  16. data/lib/sequel/adapters/jdbc/hsqldb.rb +5 -0
  17. data/lib/sequel/adapters/mock.rb +14 -2
  18. data/lib/sequel/adapters/shared/access.rb +6 -9
  19. data/lib/sequel/adapters/shared/cubrid.rb +5 -0
  20. data/lib/sequel/adapters/shared/db2.rb +5 -0
  21. data/lib/sequel/adapters/shared/firebird.rb +5 -0
  22. data/lib/sequel/adapters/shared/mssql.rb +23 -16
  23. data/lib/sequel/adapters/shared/mysql.rb +12 -2
  24. data/lib/sequel/adapters/shared/oracle.rb +31 -15
  25. data/lib/sequel/adapters/shared/postgres.rb +28 -4
  26. data/lib/sequel/adapters/shared/sqlanywhere.rb +5 -0
  27. data/lib/sequel/adapters/shared/sqlite.rb +12 -1
  28. data/lib/sequel/ast_transformer.rb +9 -7
  29. data/lib/sequel/connection_pool.rb +10 -4
  30. data/lib/sequel/database/features.rb +15 -0
  31. data/lib/sequel/database/schema_generator.rb +2 -2
  32. data/lib/sequel/database/schema_methods.rb +21 -3
  33. data/lib/sequel/database/transactions.rb +8 -4
  34. data/lib/sequel/dataset/actions.rb +13 -7
  35. data/lib/sequel/dataset/features.rb +7 -0
  36. data/lib/sequel/dataset/query.rb +28 -11
  37. data/lib/sequel/dataset/sql.rb +90 -14
  38. data/lib/sequel/extensions/constraint_validations.rb +2 -2
  39. data/lib/sequel/extensions/date_arithmetic.rb +1 -1
  40. data/lib/sequel/extensions/eval_inspect.rb +12 -6
  41. data/lib/sequel/extensions/pg_array_ops.rb +11 -2
  42. data/lib/sequel/extensions/pg_json.rb +130 -23
  43. data/lib/sequel/extensions/pg_json_ops.rb +196 -28
  44. data/lib/sequel/extensions/to_dot.rb +5 -7
  45. data/lib/sequel/model/associations.rb +0 -50
  46. data/lib/sequel/plugins/class_table_inheritance.rb +49 -21
  47. data/lib/sequel/plugins/many_through_many.rb +10 -11
  48. data/lib/sequel/plugins/serialization.rb +4 -1
  49. data/lib/sequel/plugins/sharding.rb +0 -9
  50. data/lib/sequel/plugins/single_table_inheritance.rb +4 -2
  51. data/lib/sequel/plugins/timestamps.rb +2 -2
  52. data/lib/sequel/sql.rb +166 -44
  53. data/lib/sequel/version.rb +1 -1
  54. data/spec/adapters/postgres_spec.rb +199 -133
  55. data/spec/core/connection_pool_spec.rb +6 -0
  56. data/spec/core/database_spec.rb +12 -0
  57. data/spec/core/dataset_spec.rb +58 -3
  58. data/spec/core/expression_filters_spec.rb +67 -5
  59. data/spec/core/mock_adapter_spec.rb +8 -4
  60. data/spec/core/schema_spec.rb +7 -0
  61. data/spec/core_extensions_spec.rb +14 -0
  62. data/spec/extensions/class_table_inheritance_spec.rb +23 -3
  63. data/spec/extensions/core_refinements_spec.rb +14 -0
  64. data/spec/extensions/eval_inspect_spec.rb +8 -4
  65. data/spec/extensions/pg_array_ops_spec.rb +6 -0
  66. data/spec/extensions/pg_json_ops_spec.rb +99 -0
  67. data/spec/extensions/pg_json_spec.rb +104 -4
  68. data/spec/extensions/serialization_spec.rb +19 -0
  69. data/spec/extensions/single_table_inheritance_spec.rb +11 -3
  70. data/spec/extensions/timestamps_spec.rb +10 -0
  71. data/spec/extensions/to_dot_spec.rb +8 -4
  72. data/spec/integration/database_test.rb +1 -1
  73. data/spec/integration/dataset_test.rb +9 -0
  74. data/spec/integration/schema_test.rb +27 -0
  75. metadata +4 -2
@@ -390,6 +390,22 @@ describe "Blockless Ruby Filters" do
390
390
  it "should handled emulated trim function" do
391
391
  @d.lit(Sequel.trim(:a)).should == 'trim(a)'
392
392
  end
393
+
394
+ it "should handled emulated function where only name is emulated" do
395
+ dsc = Class.new(Sequel::Dataset)
396
+ dsc::EMULATED_FUNCTION_MAP[:trim] = :foo
397
+ dsc.new(@d.db).literal(Sequel.trim(:a)).should == 'foo(a)'
398
+ end
399
+
400
+ it "should handled emulated function needing full emulation" do
401
+ dsc = Class.new(Sequel::Dataset) do
402
+ def emulate_function?(n) n == :trim end
403
+ def emulate_function_sql_append(sql, f)
404
+ sql << "#{f.name}FOO(lower(#{f.args.first}))"
405
+ end
406
+ end
407
+ dsc.new(@d.db).literal(Sequel.trim(:a)).should == 'trimFOO(lower(a))'
408
+ end
393
409
  end
394
410
 
395
411
  describe Sequel::SQL::VirtualRow do
@@ -419,15 +435,17 @@ describe Sequel::SQL::VirtualRow do
419
435
  @d.l{version{}}.should == 'version()'
420
436
  end
421
437
 
422
- it "should treat methods with a block and a leading argument :* as a function call starting with the SQL wildcard" do
438
+ it "should treat methods with a block and a leading argument :* as a function call with the SQL wildcard" do
423
439
  @d.l{count(:*){}}.should == 'count(*)'
424
- @d.l{count(:*, 1){}}.should == 'count(*, 1)'
425
440
  end
426
441
 
427
- it "should support * method on functions to add * as the first argument" do
442
+ it "should support * method on functions to raise error if function already has an argument" do
443
+ proc{@d.l{count(1).*}}.should raise_error(Sequel::Error)
444
+ end
445
+
446
+ it "should support * method on functions to use * as the argument" do
428
447
  @d.l{count{}.*}.should == 'count(*)'
429
- @d.l{count(1).*}.should == 'count(*, 1)'
430
- @d.literal(Sequel.expr{count(1) * 2}).should == '(count(1) * 2)'
448
+ @d.literal(Sequel.expr{sum(1) * 2}).should == '(sum(1) * 2)'
431
449
  end
432
450
 
433
451
  it "should treat methods with a block and a leading argument :distinct as a function call with DISTINCT and the additional method arguments" do
@@ -496,6 +514,14 @@ describe Sequel::SQL::VirtualRow do
496
514
  @d.l{sum(c).over(:partition=>a, :order=>b, :window=>:win, :frame=>:rows)}.should == 'sum("c") OVER ("win" PARTITION BY "a" ORDER BY "b" ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)'
497
515
  end
498
516
 
517
+ it "should support over method with a Window argument" do
518
+ @d.l{sum(c).over(Sequel::SQL::Window.new(:partition=>a, :order=>b, :window=>:win, :frame=>:rows))}.should == 'sum("c") OVER ("win" PARTITION BY "a" ORDER BY "b" ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)'
519
+ end
520
+
521
+ it "should raise error if over is called on a function that already has a window " do
522
+ proc{@d.l{rank{}.over.over}}.should raise_error(Sequel::Error)
523
+ end
524
+
499
525
  it "should raise an error if window functions are not supported" do
500
526
  class << @d; remove_method :supports_window_functions? end
501
527
  meta_def(@d, :supports_window_functions?){false}
@@ -503,6 +529,30 @@ describe Sequel::SQL::VirtualRow do
503
529
  proc{Sequel.mock.dataset.filter{count(:over, :* =>true, :partition=>a, :order=>b, :window=>:win, :frame=>:rows){}}.sql}.should raise_error(Sequel::Error)
504
530
  end
505
531
 
532
+ it "should handle lateral function calls" do
533
+ @d.l{rank{}.lateral}.should == 'LATERAL rank()'
534
+ end
535
+
536
+ it "should handle ordered-set and hypothetical-set function calls" do
537
+ @d.l{mode{}.within_group(:a)}.should == 'mode() WITHIN GROUP (ORDER BY "a")'
538
+ @d.l{mode{}.within_group(:a, :b)}.should == 'mode() WITHIN GROUP (ORDER BY "a", "b")'
539
+ end
540
+
541
+ it "should handle filtered aggregate function calls" do
542
+ @d.l{count{}.*.filter(:a, :b)}.should == 'count(*) FILTER (WHERE ("a" AND "b"))'
543
+ @d.l{count{}.*.filter(:a=>1)}.should == 'count(*) FILTER (WHERE ("a" = 1))'
544
+ @d.l{count{}.*.filter{b > 1}}.should == 'count(*) FILTER (WHERE ("b" > 1))'
545
+ @d.l{count{}.*.filter(:a=>1){b > 1}}.should == 'count(*) FILTER (WHERE (("a" = 1) AND ("b" > 1)))'
546
+ end
547
+
548
+ it "should handle fitlered ordered-set and hypothetical-set function calls" do
549
+ @d.l{mode{}.within_group(:a).filter(:a=>1)}.should == 'mode() WITHIN GROUP (ORDER BY "a") FILTER (WHERE ("a" = 1))'
550
+ end
551
+
552
+ it "should handle function calls with ordinality" do
553
+ @d.l{foo{}.with_ordinality}.should == 'foo() WITH ORDINALITY'
554
+ end
555
+
506
556
  it "should support function method on identifiers to create functions" do
507
557
  @d.l{rank.function}.should == 'rank()'
508
558
  @d.l{sum.function(c)}.should == 'sum("c")'
@@ -522,6 +572,18 @@ describe Sequel::SQL::VirtualRow do
522
572
  @d.l{sch__rank.function}.should == '"sch"."rank"()'
523
573
  end
524
574
 
575
+ it "should quote function names if a quoted function is used and database supports quoted function names" do
576
+ def @d.supports_quoted_function_names?; true; end
577
+ @d.l{rank{}.quoted}.should == '"rank"()'
578
+ @d.l{sch__rank{}.quoted}.should == '"sch__rank"()'
579
+ end
580
+
581
+ it "should not quote function names if an unquoted function is used" do
582
+ def @d.supports_quoted_function_names?; true; end
583
+ @d.l{rank.function.unquoted}.should == 'rank()'
584
+ @d.l{sch__rank.function.unquoted}.should == 'sch.rank()'
585
+ end
586
+
525
587
  it "should deal with classes without requiring :: prefix" do
526
588
  @d.l{date < Date.today}.should == "(\"date\" < '#{Date.today}')"
527
589
  @d.l{date < Sequel::CURRENT_DATE}.should == "(\"date\" < CURRENT_DATE)"
@@ -424,13 +424,15 @@ describe "Sequel Mock Adapter" do
424
424
  class Sequel::Database; @identifier_input_method=nil; end
425
425
  class Sequel::Database; @identifier_output_method=nil; end
426
426
  Sequel.mock(:host=>'access').select(Date.new(2011, 12, 13)).sql.should == 'SELECT #2011-12-13#'
427
+ Sequel.mock(:host=>'cubrid').from(:a).offset(1).sql.should == 'SELECT * FROM "a" LIMIT 1,4294967295'
427
428
  Sequel.mock(:host=>'db2').select(1).sql.should == 'SELECT 1 FROM "SYSIBM"."SYSDUMMY1"'
428
429
  Sequel.mock(:host=>'firebird')[:a].distinct.limit(1, 2).sql.should == 'SELECT DISTINCT FIRST 1 SKIP 2 * FROM "A"'
429
430
  Sequel.mock(:host=>'informix')[:a].distinct.limit(1, 2).sql.should == 'SELECT SKIP 2 FIRST 1 DISTINCT * FROM A'
430
431
  Sequel.mock(:host=>'mssql')[:a].full_text_search(:b, 'c').sql.should == "SELECT * FROM [A] WHERE (CONTAINS ([B], 'c'))"
431
432
  Sequel.mock(:host=>'mysql')[:a].full_text_search(:b, 'c').sql.should == "SELECT * FROM `a` WHERE (MATCH (`b`) AGAINST ('c'))"
432
433
  Sequel.mock(:host=>'oracle')[:a].limit(1).sql.should == 'SELECT * FROM (SELECT * FROM "A") "T1" WHERE (ROWNUM <= 1)'
433
- Sequel.mock(:host=>'postgres')[:a].full_text_search(:b, 'c').sql.should == "SELECT * FROM \"a\" WHERE (to_tsvector('simple'::regconfig, (COALESCE(\"b\", ''))) @@ to_tsquery('simple'::regconfig, 'c'))"
434
+ Sequel.mock(:host=>'postgres')[:a].full_text_search(:b, 'c').sql.should == "SELECT * FROM \"a\" WHERE (to_tsvector(CAST('simple' AS regconfig), (COALESCE(\"b\", ''))) @@ to_tsquery(CAST('simple' AS regconfig), 'c'))"
435
+ Sequel.mock(:host=>'sqlanywhere').from(:a).offset(1).sql.should == 'SELECT TOP 2147483647 START AT (1 + 1) * FROM "A"'
434
436
  Sequel.mock(:host=>'sqlite')[:a___b].sql.should == "SELECT * FROM `a` AS 'b'"
435
437
  ensure
436
438
  Sequel.quote_identifiers = qi
@@ -439,9 +441,11 @@ describe "Sequel Mock Adapter" do
439
441
  end
440
442
  end
441
443
 
442
- specify "should automatically set version for postgres and mssql" do
443
- Sequel.mock(:host=>'postgres').server_version.should == 90103
444
- Sequel.mock(:host=>'mssql').server_version.should == 10000000
444
+ specify "should automatically set version for adapters nedding versions" do
445
+ Sequel.mock(:host=>'postgres').server_version.should == 90400
446
+ Sequel.mock(:host=>'mssql').server_version.should == 11000000
447
+ Sequel.mock(:host=>'mysql').server_version.should == 50617
448
+ Sequel.mock(:host=>'sqlite').sqlite_version.should == 30804
445
449
  end
446
450
 
447
451
  specify "should stub out the primary_key method for postgres" do
@@ -1399,6 +1399,13 @@ describe "Database#create_view" do
1399
1399
  @db.sqls.should == ['CREATE VIEW test (d, e) AS SELECT a, b FROM items ORDER BY c']
1400
1400
  end
1401
1401
 
1402
+ specify "should handle :check option" do
1403
+ @db.create_view :test, @db[:items].select(:a, :b).order(:c), :check=>true
1404
+ @db.sqls.should == ['CREATE VIEW test AS SELECT a, b FROM items ORDER BY c WITH CHECK OPTION']
1405
+ @db.create_view :test, @db[:items].select(:a, :b).order(:c), :check=>:local
1406
+ @db.sqls.should == ['CREATE VIEW test AS SELECT a, b FROM items ORDER BY c WITH LOCAL CHECK OPTION']
1407
+ end
1408
+
1402
1409
  specify "should handle create_or_replace_view" do
1403
1410
  @db.create_or_replace_view :sch__test, "SELECT * FROM xyz"
1404
1411
  @db.sqls.should == ['DROP VIEW sch.test', 'CREATE VIEW sch.test AS SELECT * FROM xyz']
@@ -647,6 +647,12 @@ describe "Postgres extensions integration" do
647
647
 
648
648
  it "Symbol#pg_json should return an JSONOp" do
649
649
  @db.literal(:a.pg_json[%w'a b']).should == "(a #> ARRAY['a','b'])"
650
+ @db.literal(:a.pg_json.extract('a')).should == "json_extract_path(a, 'a')"
651
+ end
652
+
653
+ it "Symbol#pg_jsonb should return an JSONBOp" do
654
+ @db.literal(:a.pg_jsonb[%w'a b']).should == "(a #> ARRAY['a','b'])"
655
+ @db.literal(:a.pg_jsonb.extract('a')).should == "jsonb_extract_path(a, 'a')"
650
656
  end
651
657
 
652
658
  it "Symbol#pg_range should return a RangeOp" do
@@ -662,6 +668,10 @@ describe "Postgres extensions integration" do
662
668
  @db.literal([1].pg_json).should == "'[1]'::json"
663
669
  end
664
670
 
671
+ it "Array#pg_jsonb should return a JSONBArray" do
672
+ @db.literal([1].pg_jsonb).should == "'[1]'::jsonb"
673
+ end
674
+
665
675
  it "Array#pg_row should return a ArrayRow" do
666
676
  @db.literal([1].pg_row).should == "ROW(1)"
667
677
  end
@@ -674,6 +684,10 @@ describe "Postgres extensions integration" do
674
684
  @db.literal({'a'=>'b'}.pg_json).should == "'{\"a\":\"b\"}'::json"
675
685
  end
676
686
 
687
+ it "Hash#pg_jsonb should return an JSONBHash" do
688
+ @db.literal({'a'=>'b'}.pg_jsonb).should == "'{\"a\":\"b\"}'::jsonb"
689
+ end
690
+
677
691
  it "Range#pg_range should return an PGRange" do
678
692
  @db.literal((1..2).pg_range).should == "'[1,2]'"
679
693
  @db.literal((1..2).pg_range(:int4range)).should == "'[1,2]'::int4range"
@@ -94,6 +94,18 @@ describe "class_table_inheritance plugin" do
94
94
  Manager.all.collect{|x| x.class}.should == [Manager, Manager]
95
95
  end
96
96
 
97
+ it "should handle a model map with integer values" do
98
+ Employee.plugin(:class_table_inheritance, :key=>:kind, :model_map=>{0=>:Employee, 1=>:Manager, 2=>:Executive})
99
+ Object.send(:remove_const, :Executive)
100
+ Object.send(:remove_const, :Manager)
101
+ class ::Manager < Employee; end
102
+ class ::Executive < Manager; end
103
+ Employee.dataset._fetch = [{:kind=>nil},{:kind=>0},{:kind=>1}, {:kind=>2}]
104
+ Employee.all.collect{|x| x.class}.should == [Employee, Employee, Manager, Executive]
105
+ Manager.dataset._fetch = [{:kind=>nil},{:kind=>0},{:kind=>1}, {:kind=>2}]
106
+ Manager.all.collect{|x| x.class}.should == [Manager, Employee, Manager, Executive]
107
+ end
108
+
97
109
  it "should fallback to the main class if the given class does not exist" do
98
110
  @ds._fetch = [{:kind=>'Employee'}, {:kind=>'Manager'}, {:kind=>'Blah'}, {:kind=>'Staff'}]
99
111
  Employee.all.collect{|x| x.class}.should == [Employee, Manager, Employee, Staff]
@@ -104,19 +116,19 @@ describe "class_table_inheritance plugin" do
104
116
  Manager.all.collect{|x| x.class}.should == [Manager, Executive, Manager]
105
117
  end
106
118
 
107
- it "should add a before_create hook that sets the model class name for the key" do
119
+ it "should sets the model class name for the key when creating new parent class records" do
108
120
  Employee.create
109
121
  @db.sqls.should == ["INSERT INTO employees (kind) VALUES ('Employee')"]
110
122
  end
111
123
 
112
- it "should add a before_create hook that sets the model class name for the key in subclasses" do
124
+ it "should sets the model class name for the key when creating new subclass records" do
113
125
  Executive.create
114
126
  @db.sqls.should == ["INSERT INTO employees (kind) VALUES ('Executive')",
115
127
  "INSERT INTO managers (id) VALUES (1)",
116
128
  "INSERT INTO executives (id) VALUES (1)"]
117
129
  end
118
130
 
119
- it "should ignore existing cti_key value" do
131
+ it "should ignore existing cti_key value when creating new records" do
120
132
  Employee.create(:kind=>'Manager')
121
133
  @db.sqls.should == ["INSERT INTO employees (kind) VALUES ('Employee')"]
122
134
  end
@@ -127,6 +139,14 @@ describe "class_table_inheritance plugin" do
127
139
  "INSERT INTO managers (id) VALUES (1)"]
128
140
  end
129
141
 
142
+ it "should handle validations on the type column field" do
143
+ o = Employee.new
144
+ def o.validate
145
+ errors.add(:kind, 'not present') unless kind
146
+ end
147
+ o.valid?.should == true
148
+ end
149
+
130
150
  it "should raise an error if attempting to create an anonymous subclass" do
131
151
  proc{Class.new(Manager)}.should raise_error(Sequel::Error)
132
152
  end
@@ -468,6 +468,12 @@ describe "Postgres extensions integration" do
468
468
 
469
469
  it "Symbol#pg_json should return an JSONOp" do
470
470
  @db.literal(:a.pg_json[%w'a b']).should == "(a #> ARRAY['a','b'])"
471
+ @db.literal(:a.pg_json.extract('a')).should == "json_extract_path(a, 'a')"
472
+ end
473
+
474
+ it "Symbol#pg_jsonb should return an JSONBOp" do
475
+ @db.literal(:a.pg_jsonb[%w'a b']).should == "(a #> ARRAY['a','b'])"
476
+ @db.literal(:a.pg_jsonb.extract('a')).should == "jsonb_extract_path(a, 'a')"
471
477
  end
472
478
 
473
479
  it "Symbol#pg_range should return a RangeOp" do
@@ -483,6 +489,10 @@ describe "Postgres extensions integration" do
483
489
  @db.literal([1].pg_json).should == "'[1]'::json"
484
490
  end
485
491
 
492
+ it "Array#pg_jsonb should return a JSONBArray" do
493
+ @db.literal([1].pg_jsonb).should == "'[1]'::jsonb"
494
+ end
495
+
486
496
  it "Array#pg_row should return a ArrayRow" do
487
497
  @db.literal([1].pg_row).should == "ROW(1)"
488
498
  end
@@ -495,6 +505,10 @@ describe "Postgres extensions integration" do
495
505
  @db.literal({'a'=>'b'}.pg_json).should == "'{\"a\":\"b\"}'::json"
496
506
  end
497
507
 
508
+ it "Hash#pg_jsonb should return an JSONBHash" do
509
+ @db.literal({'a'=>'b'}.pg_jsonb).should == "'{\"a\":\"b\"}'::jsonb"
510
+ end
511
+
498
512
  it "Range#pg_range should return an PGRange" do
499
513
  @db.literal((1..2).pg_range).should == "'[1,2]'"
500
514
  @db.literal((1..2).pg_range(:int4range)).should == "'[1,2]'::int4range"
@@ -13,6 +13,7 @@ describe "eval_inspect extension" do
13
13
  [
14
14
  # Objects with components where eval(inspect) == self
15
15
  Sequel::SQL::AliasedExpression.new(:b, :a),
16
+ Sequel::SQL::AliasedExpression.new(:b, :a, [:c, :d]),
16
17
  Sequel::SQL::CaseExpression.new({:b=>:a}, :c),
17
18
  Sequel::SQL::CaseExpression.new({:b=>:a}, :c, :d),
18
19
  Sequel::SQL::Cast.new(:a, :b),
@@ -28,9 +29,12 @@ describe "eval_inspect extension" do
28
29
  Sequel::NOTNULL,
29
30
  Sequel::SQL::Function.new(:a, :b, :c),
30
31
  Sequel::SQL::Identifier.new(:a),
31
- Sequel::SQL::JoinClause.new(:inner, :b, :c),
32
- Sequel::SQL::JoinOnClause.new({:d=>:a}, :inner, :b, :c),
33
- Sequel::SQL::JoinUsingClause.new([:a], :inner, :b, :c),
32
+ Sequel::SQL::JoinClause.new(:inner, :b),
33
+ Sequel::SQL::JoinOnClause.new({:d=>:a}, :inner, :b),
34
+ Sequel::SQL::JoinUsingClause.new([:a], :inner, :b),
35
+ Sequel::SQL::JoinClause.new(:inner, Sequel.as(:b, :c, [:d, :e])),
36
+ Sequel::SQL::JoinOnClause.new({:d=>:a}, :inner, Sequel.as(:b, :c, [:d, :e])),
37
+ Sequel::SQL::JoinUsingClause.new([:a], :inner, Sequel.as(:b, :c, [:d, :e])),
34
38
  Sequel::SQL::PlaceholderLiteralString.new('? = ?', [:a, :b]),
35
39
  Sequel::SQL::PlaceholderLiteralString.new(':a = :b', [{:a=>:b, :b=>42}]),
36
40
  Sequel::SQL::OrderedExpression.new(:a),
@@ -40,7 +44,7 @@ describe "eval_inspect extension" do
40
44
  Sequel::SQL::QualifiedIdentifier.new(:b, :a),
41
45
  Sequel::SQL::Subscript.new(:a, [1, 2]),
42
46
  Sequel::SQL::Window.new(:order=>:a, :partition=>:b),
43
- Sequel::SQL::WindowFunction.new(Sequel::SQL::Function.new(:a, :b, :c), Sequel::SQL::Window.new(:order=>:a, :partition=>:b)),
47
+ Sequel::SQL::Function.new(:a, :b, :c).over(:order=>:a, :partition=>:b),
44
48
  Sequel::SQL::Wrapper.new(:a),
45
49
 
46
50
  # Objects with components where eval(inspect) != self
@@ -63,6 +63,10 @@ describe "Sequel::Postgres::ArrayOp" do
63
63
  @db.literal(@a.unshift(:b)).should == "(b || a)"
64
64
  end
65
65
 
66
+ it "#cardinality should use the cardinality function" do
67
+ @db.literal(@a.cardinality).should == "cardinality(a)"
68
+ end
69
+
66
70
  it "#dims should use the array_dims function" do
67
71
  @db.literal(@a.dims).should == "array_dims(a)"
68
72
  end
@@ -95,6 +99,8 @@ describe "Sequel::Postgres::ArrayOp" do
95
99
 
96
100
  it "#unnest should use the unnest function" do
97
101
  @db.literal(@a.unnest).should == "unnest(a)"
102
+ @db.literal(@a.unnest(:b, :c)).should == "unnest(a, b, c)"
103
+ @db.literal(@a.unnest([1])).should == "unnest(a, ARRAY[1])"
98
104
  end
99
105
 
100
106
  it "#pg_array should return self" do
@@ -6,6 +6,7 @@ describe "Sequel::Postgres::JSONOp" do
6
6
  before do
7
7
  @db = Sequel.connect('mock://postgres', :quote_identifiers=>false)
8
8
  @j = Sequel.pg_json_op(:j)
9
+ @jb = Sequel.pg_jsonb_op(:j)
9
10
  @l = proc{|o| @db.literal(o)}
10
11
  end
11
12
 
@@ -48,77 +49,167 @@ describe "Sequel::Postgres::JSONOp" do
48
49
 
49
50
  it "should have #array_length use the json_array_length function" do
50
51
  @l[@j.array_length].should == "json_array_length(j)"
52
+ @l[@jb.array_length].should == "jsonb_array_length(j)"
51
53
  end
52
54
 
53
55
  it "should have #array_length return a numeric expression" do
54
56
  @l[@j.array_length & 1].should == "(json_array_length(j) & 1)"
57
+ @l[@jb.array_length & 1].should == "(jsonb_array_length(j) & 1)"
55
58
  end
56
59
 
57
60
  it "should have #each use the json_each function" do
58
61
  @l[@j.each].should == "json_each(j)"
62
+ @l[@jb.each].should == "jsonb_each(j)"
59
63
  end
60
64
 
61
65
  it "should have #each_text use the json_each_text function" do
62
66
  @l[@j.each_text].should == "json_each_text(j)"
67
+ @l[@jb.each_text].should == "jsonb_each_text(j)"
63
68
  end
64
69
 
65
70
  it "should have #extract use the json_extract_path function" do
66
71
  @l[@j.extract('a')].should == "json_extract_path(j, 'a')"
67
72
  @l[@j.extract('a', 'b')].should == "json_extract_path(j, 'a', 'b')"
73
+ @l[@jb.extract('a')].should == "jsonb_extract_path(j, 'a')"
74
+ @l[@jb.extract('a', 'b')].should == "jsonb_extract_path(j, 'a', 'b')"
68
75
  end
69
76
 
70
77
  it "should have #extract return a JSONOp" do
71
78
  @l[@j.extract('a')[1]].should == "(json_extract_path(j, 'a') -> 1)"
79
+ @l[@jb.extract('a')[1]].should == "(jsonb_extract_path(j, 'a') -> 1)"
72
80
  end
73
81
 
74
82
  it "should have #extract_text use the json_extract_path_text function" do
75
83
  @l[@j.extract_text('a')].should == "json_extract_path_text(j, 'a')"
76
84
  @l[@j.extract_text('a', 'b')].should == "json_extract_path_text(j, 'a', 'b')"
85
+ @l[@jb.extract_text('a')].should == "jsonb_extract_path_text(j, 'a')"
86
+ @l[@jb.extract_text('a', 'b')].should == "jsonb_extract_path_text(j, 'a', 'b')"
77
87
  end
78
88
 
79
89
  it "should have #extract_text return an SQL::StringExpression" do
80
90
  @l[@j.extract_text('a') + 'a'].should == "(json_extract_path_text(j, 'a') || 'a')"
91
+ @l[@jb.extract_text('a') + 'a'].should == "(jsonb_extract_path_text(j, 'a') || 'a')"
81
92
  end
82
93
 
83
94
  it "should have #keys use the json_object_keys function" do
84
95
  @l[@j.keys].should == "json_object_keys(j)"
96
+ @l[@jb.keys].should == "jsonb_object_keys(j)"
85
97
  end
86
98
 
87
99
  it "should have #array_elements use the json_array_elements function" do
88
100
  @l[@j.array_elements].should == "json_array_elements(j)"
101
+ @l[@jb.array_elements].should == "jsonb_array_elements(j)"
102
+ end
103
+
104
+ it "should have #array_elements use the json_array_elements_text function" do
105
+ @l[@j.array_elements_text].should == "json_array_elements_text(j)"
106
+ @l[@jb.array_elements_text].should == "jsonb_array_elements_text(j)"
107
+ end
108
+
109
+ it "should have #typeof use the json_typeof function" do
110
+ @l[@j.typeof].should == "json_typeof(j)"
111
+ @l[@jb.typeof].should == "jsonb_typeof(j)"
112
+ end
113
+
114
+ it "should have #to_record use the json_to_record function" do
115
+ @l[@j.to_record].should == "json_to_record(j, false)"
116
+ @l[@jb.to_record].should == "jsonb_to_record(j, false)"
117
+ @l[@j.to_record(true)].should == "json_to_record(j, true)"
118
+ @l[@jb.to_record(true)].should == "jsonb_to_record(j, true)"
119
+ end
120
+
121
+ it "should have #to_recordset use the json_to_recordsetfunction" do
122
+ @l[@j.to_recordset].should == "json_to_recordset(j, false)"
123
+ @l[@jb.to_recordset].should == "jsonb_to_recordset(j, false)"
124
+ @l[@j.to_recordset(true)].should == "json_to_recordset(j, true)"
125
+ @l[@jb.to_recordset(true)].should == "jsonb_to_recordset(j, true)"
89
126
  end
90
127
 
91
128
  it "should have #populate use the json_populate_record function" do
92
129
  @l[@j.populate(:a)].should == "json_populate_record(a, j)"
130
+ @l[@jb.populate(:a)].should == "jsonb_populate_record(a, j)"
93
131
  end
94
132
 
95
133
  it "should have #populate_set use the json_populate_record function" do
96
134
  @l[@j.populate_set(:a)].should == "json_populate_recordset(a, j)"
135
+ @l[@jb.populate_set(:a)].should == "jsonb_populate_recordset(a, j)"
136
+ end
137
+
138
+ it "#contain_all should use the ?& operator" do
139
+ @l[@jb.contain_all(:h1)].should == "(j ?& h1)"
140
+ end
141
+
142
+ it "#contain_all handle arrays" do
143
+ @l[@jb.contain_all(%w'h1')].should == "(j ?& ARRAY['h1'])"
144
+ end
145
+
146
+ it "#contain_any should use the ?| operator" do
147
+ @l[@jb.contain_any(:h1)].should == "(j ?| h1)"
148
+ end
149
+
150
+ it "#contain_any should handle arrays" do
151
+ @l[@jb.contain_any(%w'h1')].should == "(j ?| ARRAY['h1'])"
152
+ end
153
+
154
+ it "#contains should use the @> operator" do
155
+ @l[@jb.contains(:h1)].should == "(j @> h1)"
156
+ end
157
+
158
+ it "#contains should handle hashes" do
159
+ @l[@jb.contains('a'=>'b')].should == "(j @> '{\"a\":\"b\"}'::jsonb)"
160
+ end
161
+
162
+ it "#contains should handle arrays" do
163
+ @l[@jb.contains([1, 2])].should == "(j @> '[1,2]'::jsonb)"
164
+ end
165
+
166
+ it "#contained_by should use the <@ operator" do
167
+ @l[@jb.contained_by(:h1)].should == "(j <@ h1)"
168
+ end
169
+
170
+ it "#contained_by should handle hashes" do
171
+ @l[@jb.contained_by('a'=>'b')].should == "(j <@ '{\"a\":\"b\"}'::jsonb)"
172
+ end
173
+
174
+ it "#contained_by should handle arrays" do
175
+ @l[@jb.contained_by([1, 2])].should == "(j <@ '[1,2]'::jsonb)"
176
+ end
177
+
178
+ it "#has_key? and aliases should use the ? operator" do
179
+ @l[@jb.has_key?('a')].should == "(j ? 'a')"
180
+ @l[@jb.include?('a')].should == "(j ? 'a')"
97
181
  end
98
182
 
99
183
  it "#pg_json should return self" do
100
184
  @j.pg_json.should equal(@j)
185
+ @jb.pg_jsonb.should equal(@jb)
101
186
  end
102
187
 
103
188
  it "Sequel.pg_json_op should return arg for JSONOp" do
104
189
  Sequel.pg_json_op(@j).should equal(@j)
190
+ Sequel.pg_jsonb_op(@jb).should equal(@jb)
105
191
  end
106
192
 
107
193
  it "should be able to turn expressions into json ops using pg_json" do
108
194
  @db.literal(Sequel.qualify(:b, :a).pg_json[1]).should == "(b.a -> 1)"
109
195
  @db.literal(Sequel.function(:a, :b).pg_json[1]).should == "(a(b) -> 1)"
196
+ @db.literal(Sequel.qualify(:b, :a).pg_jsonb[1]).should == "(b.a -> 1)"
197
+ @db.literal(Sequel.function(:a, :b).pg_jsonb[1]).should == "(a(b) -> 1)"
110
198
  end
111
199
 
112
200
  it "should be able to turn literal strings into json ops using pg_json" do
113
201
  @db.literal(Sequel.lit('a').pg_json[1]).should == "(a -> 1)"
202
+ @db.literal(Sequel.lit('a').pg_jsonb[1]).should == "(a -> 1)"
114
203
  end
115
204
 
116
205
  it "should be able to turn symbols into json ops using Sequel.pg_json_op" do
117
206
  @db.literal(Sequel.pg_json_op(:a)[1]).should == "(a -> 1)"
207
+ @db.literal(Sequel.pg_jsonb_op(:a)[1]).should == "(a -> 1)"
118
208
  end
119
209
 
120
210
  it "should be able to turn symbols into json ops using Sequel.pg_json" do
121
211
  @db.literal(Sequel.pg_json(:a)[1]).should == "(a -> 1)"
212
+ @db.literal(Sequel.pg_jsonb(:a)[1]).should == "(a -> 1)"
122
213
  end
123
214
 
124
215
  it "should allow transforming JSONArray instances into ArrayOp instances" do
@@ -128,4 +219,12 @@ describe "Sequel::Postgres::JSONOp" do
128
219
  it "should allow transforming JSONHash instances into ArrayOp instances" do
129
220
  @db.literal(Sequel.pg_json('a'=>1).op['a']).should == "('{\"a\":1}'::json -> 'a')"
130
221
  end
222
+
223
+ it "should allow transforming JSONBArray instances into ArrayOp instances" do
224
+ @db.literal(Sequel.pg_jsonb([1,2]).op[1]).should == "('[1,2]'::jsonb -> 1)"
225
+ end
226
+
227
+ it "should allow transforming JSONBHash instances into ArrayOp instances" do
228
+ @db.literal(Sequel.pg_jsonb('a'=>1).op['a']).should == "('{\"a\":1}'::jsonb -> 'a')"
229
+ end
131
230
  end