sequel 4.10.0 → 4.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +58 -0
- data/doc/association_basics.rdoc +1 -1
- data/doc/cheat_sheet.rdoc +0 -1
- data/doc/core_extensions.rdoc +2 -2
- data/doc/dataset_filtering.rdoc +5 -5
- data/doc/model_hooks.rdoc +9 -0
- data/doc/object_model.rdoc +7 -13
- data/doc/opening_databases.rdoc +3 -1
- data/doc/querying.rdoc +8 -8
- data/doc/release_notes/4.11.0.txt +147 -0
- data/doc/sql.rdoc +11 -7
- data/doc/virtual_rows.rdoc +4 -5
- data/lib/sequel/adapters/ibmdb.rb +24 -16
- data/lib/sequel/adapters/jdbc/h2.rb +5 -0
- data/lib/sequel/adapters/jdbc/hsqldb.rb +5 -0
- data/lib/sequel/adapters/mock.rb +14 -2
- data/lib/sequel/adapters/shared/access.rb +6 -9
- data/lib/sequel/adapters/shared/cubrid.rb +5 -0
- data/lib/sequel/adapters/shared/db2.rb +5 -0
- data/lib/sequel/adapters/shared/firebird.rb +5 -0
- data/lib/sequel/adapters/shared/mssql.rb +23 -16
- data/lib/sequel/adapters/shared/mysql.rb +12 -2
- data/lib/sequel/adapters/shared/oracle.rb +31 -15
- data/lib/sequel/adapters/shared/postgres.rb +28 -4
- data/lib/sequel/adapters/shared/sqlanywhere.rb +5 -0
- data/lib/sequel/adapters/shared/sqlite.rb +12 -1
- data/lib/sequel/ast_transformer.rb +9 -7
- data/lib/sequel/connection_pool.rb +10 -4
- data/lib/sequel/database/features.rb +15 -0
- data/lib/sequel/database/schema_generator.rb +2 -2
- data/lib/sequel/database/schema_methods.rb +21 -3
- data/lib/sequel/database/transactions.rb +8 -4
- data/lib/sequel/dataset/actions.rb +13 -7
- data/lib/sequel/dataset/features.rb +7 -0
- data/lib/sequel/dataset/query.rb +28 -11
- data/lib/sequel/dataset/sql.rb +90 -14
- data/lib/sequel/extensions/constraint_validations.rb +2 -2
- data/lib/sequel/extensions/date_arithmetic.rb +1 -1
- data/lib/sequel/extensions/eval_inspect.rb +12 -6
- data/lib/sequel/extensions/pg_array_ops.rb +11 -2
- data/lib/sequel/extensions/pg_json.rb +130 -23
- data/lib/sequel/extensions/pg_json_ops.rb +196 -28
- data/lib/sequel/extensions/to_dot.rb +5 -7
- data/lib/sequel/model/associations.rb +0 -50
- data/lib/sequel/plugins/class_table_inheritance.rb +49 -21
- data/lib/sequel/plugins/many_through_many.rb +10 -11
- data/lib/sequel/plugins/serialization.rb +4 -1
- data/lib/sequel/plugins/sharding.rb +0 -9
- data/lib/sequel/plugins/single_table_inheritance.rb +4 -2
- data/lib/sequel/plugins/timestamps.rb +2 -2
- data/lib/sequel/sql.rb +166 -44
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +199 -133
- data/spec/core/connection_pool_spec.rb +6 -0
- data/spec/core/database_spec.rb +12 -0
- data/spec/core/dataset_spec.rb +58 -3
- data/spec/core/expression_filters_spec.rb +67 -5
- data/spec/core/mock_adapter_spec.rb +8 -4
- data/spec/core/schema_spec.rb +7 -0
- data/spec/core_extensions_spec.rb +14 -0
- data/spec/extensions/class_table_inheritance_spec.rb +23 -3
- data/spec/extensions/core_refinements_spec.rb +14 -0
- data/spec/extensions/eval_inspect_spec.rb +8 -4
- data/spec/extensions/pg_array_ops_spec.rb +6 -0
- data/spec/extensions/pg_json_ops_spec.rb +99 -0
- data/spec/extensions/pg_json_spec.rb +104 -4
- data/spec/extensions/serialization_spec.rb +19 -0
- data/spec/extensions/single_table_inheritance_spec.rb +11 -3
- data/spec/extensions/timestamps_spec.rb +10 -0
- data/spec/extensions/to_dot_spec.rb +8 -4
- data/spec/integration/database_test.rb +1 -1
- data/spec/integration/dataset_test.rb +9 -0
- data/spec/integration/schema_test.rb +27 -0
- 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
|
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
|
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.
|
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'
|
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
|
443
|
-
Sequel.mock(:host=>'postgres').server_version.should ==
|
444
|
-
Sequel.mock(:host=>'mssql').server_version.should ==
|
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
|
data/spec/core/schema_spec.rb
CHANGED
@@ -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
|
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
|
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
|
32
|
-
Sequel::SQL::JoinOnClause.new({:d=>:a}, :inner, :b
|
33
|
-
Sequel::SQL::JoinUsingClause.new([:a], :inner, :b
|
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::
|
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
|