sequel 4.10.0 → 4.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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