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
@@ -3,7 +3,7 @@ module Sequel
3
3
  MAJOR = 4
4
4
  # The minor version of Sequel. Bumped for every non-patch level
5
5
  # release, generally around once a month.
6
- MINOR = 10
6
+ MINOR = 11
7
7
  # The tiny version of Sequel. Usually 0, only bumped for bugfix
8
8
  # releases that fix regressions from previous versions.
9
9
  TINY = 0
@@ -117,8 +117,8 @@ end
117
117
  describe "PostgreSQL views" do
118
118
  before do
119
119
  @db = DB
120
- @db.drop_view(:items_view, :cascade=>true, :if_exists=>true)
121
- @db.create_table!(:items){Integer :number}
120
+ @db.drop_table?(:items, :cascade=>true)
121
+ @db.create_table(:items){Integer :number}
122
122
  @db[:items].insert(10)
123
123
  @db[:items].insert(20)
124
124
  end
@@ -152,6 +152,15 @@ describe "PostgreSQL views" do
152
152
  @db[:items_view].select_order_map(:number).should == [10, 15, 20]
153
153
  end if DB.server_version >= 90300
154
154
 
155
+ specify "should support refreshing materialized views concurrently" do
156
+ @opts = {:materialized=>true}
157
+ @db.create_view(:items_view, @db[:items].where{number >= 10}, @opts)
158
+ @db.refresh_view(:items_view)
159
+ proc{@db.refresh_view(:items_view, :concurrently=>true)}.should raise_error(Sequel::DatabaseError)
160
+ @db.add_index :items_view, :number, :unique=>true
161
+ proc{@db.refresh_view(:items_view, :concurrently=>true)}.should_not raise_error
162
+ end if DB.server_version >= 90400
163
+
155
164
  specify "should support :if_exists=>true for not raising an error if the view does not exist" do
156
165
  proc{@db.drop_view(:items_view, :if_exists=>true)}.should_not raise_error
157
166
  end
@@ -170,6 +179,14 @@ describe "A PostgreSQL database" do
170
179
  @db.server_version.should > 70000
171
180
  end
172
181
 
182
+ specify "should respect the :read_only option per-savepoint" do
183
+ proc{@db.transaction{@db.transaction(:savepoint=>true, :read_only=>true){@db[:public__testfk].insert}}}.should raise_error(Sequel::DatabaseError)
184
+ proc{@db.transaction(:auto_savepoint=>true, :read_only=>true){@db.transaction(:read_only=>false){@db[:public__testfk].insert}}}.should raise_error(Sequel::DatabaseError)
185
+ proc{@db.transaction{@db[:public__testfk].insert; @db.transaction(:savepoint=>true, :read_only=>true){@db[:public__testfk].all;}}}.should_not raise_error
186
+ proc{@db.transaction{@db.transaction(:savepoint=>true, :read_only=>true){}; @db[:public__testfk].insert}}.should_not raise_error
187
+ proc{@db.transaction{@db[:public__testfk].all; @db.transaction(:savepoint=>true, :read_only=>true){@db[:public__testfk].all;}}}.should_not raise_error
188
+ end
189
+
173
190
  specify "should support disable_insert_returning" do
174
191
  ds = @db[:public__testfk].disable_insert_returning
175
192
  ds.delete
@@ -326,6 +343,22 @@ describe "A PostgreSQL dataset" do
326
343
  @d.order(Sequel.asc(:value, :nulls=>:first), :name).reverse.select_map(:name).should == %w[bcd bcd abc]
327
344
  end
328
345
 
346
+ specify "should support selecting from LATERAL functions" do
347
+ @d.from{[generate_series(1,3,1).as(:a), pow(:a, 2).lateral.as(:b)]}.select_map([:a, :b])== [[1, 1], [2, 4], [3, 9]]
348
+ end if DB.server_version >= 90300
349
+
350
+ specify "should support ordered-set and hypothetical-set aggregate functions" do
351
+ @d.from{generate_series(1,3,1).as(:a)}.select{(a.sql_number % 2).as(:a)}.from_self.get{mode{}.within_group(:a)}.should == 1
352
+ end if DB.server_version >= 90400
353
+
354
+ specify "should support filtered aggregate functions" do
355
+ @d.from{generate_series(1,3,1).as(:a)}.select{(a.sql_number % 2).as(:a)}.from_self.get{count(:a).filter(:a=>1)}.should == 2
356
+ end if DB.server_version >= 90400
357
+
358
+ specify "should support functions with ordinality" do
359
+ @d.from{generate_series(1,10,3).with_ordinality}.select_map([:generate_series, :ordinality]).should == [[1, 1], [4, 2], [7, 3], [10, 4]]
360
+ end if DB.server_version >= 90400
361
+
329
362
  specify "#lock should lock tables and yield if a block is given" do
330
363
  @d.lock('EXCLUSIVE'){@d.insert(:name=>'a')}
331
364
  end
@@ -848,28 +881,14 @@ describe "A PostgreSQL database" do
848
881
 
849
882
  specify "should support fulltext indexes and searching" do
850
883
  @db.create_table(:posts){text :title; text :body; full_text_index [:title, :body]; full_text_index :title, :language => 'french', :index_type=>:gist}
851
- check_sqls do
852
- @db.sqls.should == [
853
- %{CREATE TABLE "posts" ("title" text, "body" text)},
854
- %{CREATE INDEX "posts_title_body_index" ON "posts" USING gin (to_tsvector('simple'::regconfig, (COALESCE("title", '') || ' ' || COALESCE("body", ''))))},
855
- %{CREATE INDEX "posts_title_index" ON "posts" USING gist (to_tsvector('french'::regconfig, (COALESCE("title", ''))))}
856
- ]
857
- end
858
884
 
859
885
  @db[:posts].insert(:title=>'ruby rails', :body=>'yowsa')
860
886
  @db[:posts].insert(:title=>'sequel', :body=>'ruby')
861
887
  @db[:posts].insert(:title=>'ruby scooby', :body=>'x')
862
- @db.sqls.clear
863
888
 
864
889
  @db[:posts].full_text_search(:title, 'rails').all.should == [{:title=>'ruby rails', :body=>'yowsa'}]
865
890
  @db[:posts].full_text_search([:title, :body], ['yowsa', 'rails']).all.should == [:title=>'ruby rails', :body=>'yowsa']
866
891
  @db[:posts].full_text_search(:title, 'scooby', :language => 'french').all.should == [{:title=>'ruby scooby', :body=>'x'}]
867
- check_sqls do
868
- @db.sqls.should == [
869
- %{SELECT * FROM "posts" WHERE (to_tsvector('simple'::regconfig, (COALESCE("title", ''))) @@ to_tsquery('simple'::regconfig, 'rails'))},
870
- %{SELECT * FROM "posts" WHERE (to_tsvector('simple'::regconfig, (COALESCE("title", '') || ' ' || COALESCE("body", ''))) @@ to_tsquery('simple'::regconfig, 'yowsa | rails'))},
871
- %{SELECT * FROM "posts" WHERE (to_tsvector('french'::regconfig, (COALESCE("title", ''))) @@ to_tsquery('french'::regconfig, 'scooby'))}]
872
- end
873
892
 
874
893
  @db[:posts].full_text_search(:title, :$n).call(:select, :n=>'rails').should == [{:title=>'ruby rails', :body=>'yowsa'}]
875
894
  @db[:posts].full_text_search(:title, :$n).prepare(:select, :fts_select).call(:n=>'rails').should == [{:title=>'ruby rails', :body=>'yowsa'}]
@@ -880,6 +899,13 @@ describe "A PostgreSQL database" do
880
899
  @db[:posts].full_text_search(:title, 'jruby maglev', :phrase=>true).select_order_map(:title).should == ['ruby jruby maglev mri rubinius iron']
881
900
  @db[:posts].full_text_search(:title, 'rubinius ruby', :plain=>true).select_order_map(:title).should == ['jruby rubinius ruby maglev mri iron', 'ruby jruby maglev mri rubinius iron']
882
901
  @db[:posts].full_text_search(:title, 'jruby maglev', :plain=>true).select_order_map(:title).should == ['jruby rubinius ruby maglev mri iron', 'ruby jruby maglev mri rubinius iron']
902
+
903
+ @db[:posts].delete
904
+ t1 = "bork " * 1000 + "ruby sequel"
905
+ t2 = "ruby sequel " * 1000
906
+ @db[:posts].insert(:title=>t1)
907
+ @db[:posts].insert(:title=>t2)
908
+ @db[:posts].full_text_search(:title, 'ruby & sequel', :rank=>true).select_map(:title).should == [t1, t2]
883
909
  end
884
910
 
885
911
  specify "should support spatial indexes" do
@@ -2237,6 +2263,13 @@ describe 'PostgreSQL array handling' do
2237
2263
  @ds.get(Sequel.pg_array(:i5).replace(1, 4).contains([1])).should == false
2238
2264
  @ds.get(Sequel.pg_array(:i5).replace(1, 4).contains([4])).should == true
2239
2265
  end
2266
+ if @db.server_version >= 90400
2267
+ @ds.get(Sequel.pg_array(:i).cardinality).should == 3
2268
+ @ds.get(Sequel.pg_array(:i4).cardinality).should == 4
2269
+ @ds.get(Sequel.pg_array(:i5).cardinality).should == 3
2270
+
2271
+ @ds.from{Sequel.pg_array([1,2,3]).op.unnest([4,5,6], [7,8]).as(:t1, [:a, :b, :c])}.select_order_map([:a, :b, :c]).should == [[1, 4, 7], [2, 5, 8], [3, 6, nil]]
2272
+ end
2240
2273
 
2241
2274
  if @native
2242
2275
  @ds.get(Sequel.pg_array(:i).push(4)).should == [1, 2, 3, 4]
@@ -2515,133 +2548,166 @@ describe 'PostgreSQL json type' do
2515
2548
  @db.drop_table?(:items)
2516
2549
  end
2517
2550
 
2518
- specify 'insert and retrieve json values' do
2519
- @db.create_table!(:items){json :j}
2520
- @ds.insert(Sequel.pg_json(@h))
2521
- @ds.count.should == 1
2522
- if @native
2523
- rs = @ds.all
2524
- v = rs.first[:j]
2525
- v.should_not be_a_kind_of(Hash)
2526
- v.to_hash.should be_a_kind_of(Hash)
2527
- v.should == @h
2528
- v.to_hash.should == @h
2551
+ json_types = [:json]
2552
+ json_types << :jsonb if DB.server_version >= 90400
2553
+ json_types.each do |json_type|
2554
+ json_array_type = "#{json_type}[]"
2555
+ pg_json = lambda{|v| Sequel.send(:"pg_#{json_type}", v)}
2556
+
2557
+ specify 'insert and retrieve json values' do
2558
+ @db.create_table!(:items){column :j, json_type}
2559
+ @ds.insert(pg_json.call(@h))
2560
+ @ds.count.should == 1
2561
+ if @native
2562
+ rs = @ds.all
2563
+ v = rs.first[:j]
2564
+ v.should_not be_a_kind_of(Hash)
2565
+ v.to_hash.should be_a_kind_of(Hash)
2566
+ v.should == @h
2567
+ v.to_hash.should == @h
2568
+ @ds.delete
2569
+ @ds.insert(rs.first)
2570
+ @ds.all.should == rs
2571
+ end
2572
+
2529
2573
  @ds.delete
2530
- @ds.insert(rs.first)
2531
- @ds.all.should == rs
2574
+ @ds.insert(pg_json.call(@a))
2575
+ @ds.count.should == 1
2576
+ if @native
2577
+ rs = @ds.all
2578
+ v = rs.first[:j]
2579
+ v.should_not be_a_kind_of(Array)
2580
+ v.to_a.should be_a_kind_of(Array)
2581
+ v.should == @a
2582
+ v.to_a.should == @a
2583
+ @ds.delete
2584
+ @ds.insert(rs.first)
2585
+ @ds.all.should == rs
2586
+ end
2532
2587
  end
2533
2588
 
2534
- @ds.delete
2535
- @ds.insert(Sequel.pg_json(@a))
2536
- @ds.count.should == 1
2537
- if @native
2538
- rs = @ds.all
2539
- v = rs.first[:j]
2540
- v.should_not be_a_kind_of(Array)
2541
- v.to_a.should be_a_kind_of(Array)
2542
- v.should == @a
2543
- v.to_a.should == @a
2544
- @ds.delete
2545
- @ds.insert(rs.first)
2546
- @ds.all.should == rs
2589
+ specify 'insert and retrieve json[] values' do
2590
+ @db.create_table!(:items){column :j, json_array_type}
2591
+ j = Sequel.pg_array([pg_json.call('a'=>1), pg_json.call(['b', 2])])
2592
+ @ds.insert(j)
2593
+ @ds.count.should == 1
2594
+ if @native
2595
+ rs = @ds.all
2596
+ v = rs.first[:j]
2597
+ v.should_not be_a_kind_of(Array)
2598
+ v.to_a.should be_a_kind_of(Array)
2599
+ v.should == j
2600
+ v.to_a.should == j
2601
+ @ds.delete
2602
+ @ds.insert(rs.first)
2603
+ @ds.all.should == rs
2604
+ end
2547
2605
  end
2548
- end
2549
2606
 
2550
- specify 'insert and retrieve json[] values' do
2551
- @db.create_table!(:items){column :j, 'json[]'}
2552
- j = Sequel.pg_array([Sequel.pg_json('a'=>1), Sequel.pg_json(['b', 2])])
2553
- @ds.insert(j)
2554
- @ds.count.should == 1
2555
- if @native
2556
- rs = @ds.all
2557
- v = rs.first[:j]
2558
- v.should_not be_a_kind_of(Array)
2559
- v.to_a.should be_a_kind_of(Array)
2560
- v.should == j
2561
- v.to_a.should == j
2562
- @ds.delete
2563
- @ds.insert(rs.first)
2564
- @ds.all.should == rs
2607
+ specify 'with models' do
2608
+ @db.create_table!(:items) do
2609
+ primary_key :id
2610
+ column :h, json_type
2611
+ end
2612
+ c = Class.new(Sequel::Model(@db[:items]))
2613
+ c.plugin :pg_typecast_on_load, :h unless @native
2614
+ c.create(:h=>pg_json.call(@h)).h.should == @h
2615
+ c.create(:h=>pg_json.call(@a)).h.should == @a
2565
2616
  end
2566
- end
2567
2617
 
2568
- specify 'use json in bound variables' do
2569
- @db.create_table!(:items){json :i}
2570
- @ds.call(:insert, {:i=>Sequel.pg_json(@h)}, {:i=>:$i})
2571
- @ds.get(:i).should == @h
2572
- @ds.filter(Sequel.cast(:i, String)=>:$i).call(:first, :i=>Sequel.pg_json(@h)).should == {:i=>@h}
2573
- @ds.filter(Sequel.cast(:i, String)=>:$i).call(:first, :i=>Sequel.pg_json({})).should == nil
2574
- @ds.filter(Sequel.cast(:i, String)=>:$i).call(:delete, :i=>Sequel.pg_json(@h)).should == 1
2618
+ specify 'use json in bound variables' do
2619
+ @db.create_table!(:items){column :i, json_type}
2620
+ @ds.call(:insert, {:i=>pg_json.call(@h)}, {:i=>:$i})
2621
+ @ds.get(:i).should == @h
2575
2622
 
2576
- @ds.call(:insert, {:i=>Sequel.pg_json(@a)}, {:i=>:$i})
2577
- @ds.get(:i).should == @a
2578
- @ds.filter(Sequel.cast(:i, String)=>:$i).call(:first, :i=>Sequel.pg_json(@a)).should == {:i=>@a}
2579
- @ds.filter(Sequel.cast(:i, String)=>:$i).call(:first, :i=>Sequel.pg_json([])).should == nil
2623
+ @ds.delete
2624
+ @ds.call(:insert, {:i=>pg_json.call('a'=>nil)}, {:i=>:$i})
2625
+ @ds.get(:i).should == pg_json.call('a'=>nil)
2580
2626
 
2581
- @ds.delete
2582
- @ds.call(:insert, {:i=>Sequel.pg_json('a'=>nil)}, {:i=>:$i})
2583
- @ds.get(:i).should == Sequel.pg_json('a'=>nil)
2584
-
2585
- @db.create_table!(:items){column :i, 'json[]'}
2586
- j = Sequel.pg_array([Sequel.pg_json('a'=>1), Sequel.pg_json(['b', 2])], :text)
2587
- @ds.call(:insert, {:i=>j}, {:i=>:$i})
2588
- @ds.get(:i).should == j
2589
- @ds.filter(Sequel.cast(:i, 'text[]')=>:$i).call(:first, :i=>j).should == {:i=>j}
2590
- @ds.filter(Sequel.cast(:i, 'text[]')=>:$i).call(:first, :i=>Sequel.pg_array([])).should == nil
2591
- end if DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG
2627
+ @db.create_table!(:items){column :i, json_array_type}
2628
+ j = Sequel.pg_array([pg_json.call('a'=>1), pg_json.call(['b', 2])], json_type)
2629
+ @ds.call(:insert, {:i=>j}, {:i=>:$i})
2630
+ @ds.get(:i).should == j
2631
+ end if DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG
2592
2632
 
2593
- specify 'with models' do
2594
- @db.create_table!(:items) do
2595
- primary_key :id
2596
- json :h
2597
- end
2598
- c = Class.new(Sequel::Model(@db[:items]))
2599
- c.plugin :pg_typecast_on_load, :h unless @native
2600
- c.create(:h=>Sequel.pg_json(@h)).h.should == @h
2601
- c.create(:h=>Sequel.pg_json(@a)).h.should == @a
2602
- end
2603
-
2604
- specify 'operations/functions with pg_json_ops' do
2605
- Sequel.extension :pg_json_ops
2606
- jo = Sequel.pg_json('a'=>1, 'b'=>{'c'=>2, 'd'=>{'e'=>3}}).op
2607
- ja = Sequel.pg_json([2, 3, %w'a b']).op
2608
-
2609
- @db.get(jo['a']).should == 1
2610
- @db.get(jo['b']['c']).should == 2
2611
- @db.get(jo[%w'b c']).should == 2
2612
- @db.get(jo['b'].get_text(%w'd e')).should == "3"
2613
- @db.get(jo[%w'b d'].get_text('e')).should == "3"
2614
- @db.get(ja[1]).should == 3
2615
- @db.get(ja[%w'2 1']).should == 'b'
2616
-
2617
- @db.get(jo.extract('a')).should == 1
2618
- @db.get(jo.extract('b').extract('c')).should == 2
2619
- @db.get(jo.extract('b', 'c')).should == 2
2620
- @db.get(jo.extract('b', 'd', 'e')).should == 3
2621
- @db.get(jo.extract_text('b', 'd')).should == '{"e":3}'
2622
- @db.get(jo.extract_text('b', 'd', 'e')).should == '3'
2623
-
2624
- @db.get(ja.array_length).should == 3
2625
- @db.from(ja.array_elements.as(:v)).select_map(:v).should == [2, 3, %w'a b']
2626
-
2627
- @db.from(jo.keys.as(:k)).select_order_map(:k).should == %w'a b'
2628
- @db.from(jo.each).select_order_map(:key).should == %w'a b'
2629
- @db.from(jo.each).order(:key).select_map(:value).should == [1, {'c'=>2, 'd'=>{'e'=>3}}]
2630
- @db.from(jo.each_text).select_order_map(:key).should == %w'a b'
2631
- @db.from(jo.each_text).order(:key).where(:key=>'b').get(:value).should =~ /\{"d":\{"e":3\},"c":2\}|\{"c":2,"d":\{"e":3\}\}/
2632
-
2633
- Sequel.extension :pg_row_ops
2634
- @db.create_table!(:items) do
2635
- Integer :a
2636
- String :b
2637
- end
2638
- j = Sequel.pg_json('a'=>1, 'b'=>'c').op
2639
- @db.get(j.populate(Sequel.cast(nil, :items)).pg_row[:a]).should == 1
2640
- @db.get(j.populate(Sequel.cast(nil, :items)).pg_row[:b]).should == 'c'
2641
- j = Sequel.pg_json([{'a'=>1, 'b'=>'c'}, {'a'=>2, 'b'=>'d'}]).op
2642
- @db.from(j.populate_set(Sequel.cast(nil, :items))).select_order_map(:a).should == [1, 2]
2643
- @db.from(j.populate_set(Sequel.cast(nil, :items))).select_order_map(:b).should == %w'c d'
2644
- end if DB.server_version >= 90300 && (DB.adapter_scheme == :postgres || DB.adapter_scheme == :jdbc)
2633
+ specify 'operations/functions with pg_json_ops' do
2634
+ Sequel.extension :pg_json_ops
2635
+ jo = pg_json.call('a'=>1, 'b'=>{'c'=>2, 'd'=>{'e'=>3}}).op
2636
+ ja = pg_json.call([2, 3, %w'a b']).op
2637
+
2638
+ @db.get(jo['a']).should == 1
2639
+ @db.get(jo['b']['c']).should == 2
2640
+ @db.get(jo[%w'b c']).should == 2
2641
+ @db.get(jo['b'].get_text(%w'd e')).should == "3"
2642
+ @db.get(jo[%w'b d'].get_text('e')).should == "3"
2643
+ @db.get(ja[1]).should == 3
2644
+ @db.get(ja[%w'2 1']).should == 'b'
2645
+
2646
+ @db.get(jo.extract('a')).should == 1
2647
+ @db.get(jo.extract('b').extract('c')).should == 2
2648
+ @db.get(jo.extract('b', 'c')).should == 2
2649
+ @db.get(jo.extract('b', 'd', 'e')).should == 3
2650
+ @db.get(jo.extract_text('b', 'd')).gsub(' ', '').should == '{"e":3}'
2651
+ @db.get(jo.extract_text('b', 'd', 'e')).should == '3'
2652
+
2653
+ @db.get(ja.array_length).should == 3
2654
+ @db.from(ja.array_elements.as(:v)).select_map(:v).should == [2, 3, %w'a b']
2655
+
2656
+ if DB.server_version >= 90400
2657
+ @db.get(jo.typeof).should == 'object'
2658
+ @db.get(ja.typeof).should == 'array'
2659
+ @db.from(ja.array_elements_text.as(:v)).select_map(:v).map{|s| s.gsub(' ', '')}.should == ['2', '3', '["a","b"]']
2660
+ @db.from(jo.to_record(true).as(:v, [Sequel.lit('a integer'), Sequel.lit('b text')])).select_map(:a).should == [1]
2661
+ @db.from(pg_json.call([{'a'=>1, 'b'=>1}]).op.to_recordset.as(:v, [Sequel.lit('a integer'), Sequel.lit('b integer')])).select_map(:a).should == [1]
2662
+
2663
+ if json_type == :jsonb
2664
+ @db.get(jo.has_key?('a')).should == true
2665
+ @db.get(jo.has_key?('c')).should == false
2666
+ @db.get(pg_json.call(['2', '3', %w'a b']).op.include?('2')).should == true
2667
+ @db.get(pg_json.call(['2', '3', %w'a b']).op.include?('4')).should == false
2668
+
2669
+ @db.get(jo.contain_all(['a', 'b'])).should == true
2670
+ @db.get(jo.contain_all(['a', 'c'])).should == false
2671
+ @db.get(jo.contain_all(['d', 'c'])).should == false
2672
+ @db.get(jo.contain_any(['a', 'b'])).should == true
2673
+ @db.get(jo.contain_any(['a', 'c'])).should == true
2674
+ @db.get(jo.contain_any(['d', 'c'])).should == false
2675
+
2676
+ @db.get(jo.contains(jo)).should == true
2677
+ @db.get(jo.contained_by(jo)).should == true
2678
+ @db.get(jo.contains('a'=>1)).should == true
2679
+ @db.get(jo.contained_by('a'=>1)).should == false
2680
+ @db.get(pg_json.call('a'=>1).op.contains(jo)).should == false
2681
+ @db.get(pg_json.call('a'=>1).op.contained_by(jo)).should == true
2682
+
2683
+ @db.get(ja.contains(ja)).should == true
2684
+ @db.get(ja.contained_by(ja)).should == true
2685
+ @db.get(ja.contains([2,3])).should == true
2686
+ @db.get(ja.contained_by([2,3])).should == false
2687
+ @db.get(pg_json.call([2,3]).op.contains(ja)).should == false
2688
+ @db.get(pg_json.call([2,3]).op.contained_by(ja)).should == true
2689
+ end
2690
+ end
2691
+
2692
+ @db.from(jo.keys.as(:k)).select_order_map(:k).should == %w'a b'
2693
+ @db.from(jo.each).select_order_map(:key).should == %w'a b'
2694
+ @db.from(jo.each).order(:key).select_map(:value).should == [1, {'c'=>2, 'd'=>{'e'=>3}}]
2695
+ @db.from(jo.each_text).select_order_map(:key).should == %w'a b'
2696
+ @db.from(jo.each_text).order(:key).where(:key=>'b').get(:value).gsub(' ', '').should =~ /\{"d":\{"e":3\},"c":2\}|\{"c":2,"d":\{"e":3\}\}/
2697
+
2698
+ Sequel.extension :pg_row_ops
2699
+ @db.create_table!(:items) do
2700
+ Integer :a
2701
+ String :b
2702
+ end
2703
+ j = Sequel.pg_json('a'=>1, 'b'=>'c').op
2704
+ @db.get(j.populate(Sequel.cast(nil, :items)).pg_row[:a]).should == 1
2705
+ @db.get(j.populate(Sequel.cast(nil, :items)).pg_row[:b]).should == 'c'
2706
+ j = Sequel.pg_json([{'a'=>1, 'b'=>'c'}, {'a'=>2, 'b'=>'d'}]).op
2707
+ @db.from(j.populate_set(Sequel.cast(nil, :items))).select_order_map(:a).should == [1, 2]
2708
+ @db.from(j.populate_set(Sequel.cast(nil, :items))).select_order_map(:b).should == %w'c d'
2709
+ end if DB.server_version >= 90300 && (DB.adapter_scheme == :postgres || DB.adapter_scheme == :jdbc)
2710
+ end
2645
2711
  end if DB.server_version >= 90200
2646
2712
 
2647
2713
  describe 'PostgreSQL inet/cidr types' do
@@ -911,6 +911,12 @@ shared_examples_for "All connection pools classes" do
911
911
  x = nil
912
912
  @class.new(mock_db.call{123}, :after_connect=>proc{|c| x = [c, c]}).hold{}
913
913
  x.should == [123, 123]
914
+ @class.new(mock_db.call{123}, :after_connect=>lambda{|c| x = [c, c]}).hold{}
915
+ x.should == [123, 123]
916
+ @class.new(mock_db.call{123}, :after_connect=>proc{|c, s| x = [c, s]}).hold{}
917
+ x.should == [123, :default]
918
+ @class.new(mock_db.call{123}, :after_connect=>lambda{|c, s| x = [c, s]}).hold{}
919
+ x.should == [123, :default]
914
920
  end
915
921
 
916
922
  specify "should raise a DatabaseConnectionError if the connection raises an exception" do
@@ -2184,6 +2184,18 @@ describe "Database#supports_savepoints?" do
2184
2184
  end
2185
2185
  end
2186
2186
 
2187
+ describe "Database#supports_views_with_check_option?" do
2188
+ specify "should be false by default" do
2189
+ Sequel::Database.new.supports_views_with_check_option?.should == false
2190
+ end
2191
+ end
2192
+
2193
+ describe "Database#supports_views_with_local_check_option?" do
2194
+ specify "should be false by default" do
2195
+ Sequel::Database.new.supports_views_with_local_check_option?.should == false
2196
+ end
2197
+ end
2198
+
2187
2199
  describe "Database#supports_savepoints_in_prepared_transactions?" do
2188
2200
  specify "should be false by default" do
2189
2201
  Sequel::Database.new.supports_savepoints_in_prepared_transactions?.should == false
@@ -870,6 +870,7 @@ describe "Dataset#as" do
870
870
  specify "should set up an alias" do
871
871
  dataset = Sequel.mock.dataset.from(:test)
872
872
  dataset.select(dataset.limit(1).select(:name).as(:n)).sql.should == 'SELECT (SELECT name FROM test LIMIT 1) AS n FROM test'
873
+ dataset.select(dataset.limit(1).select(:name).as(:n, [:nm])).sql.should == 'SELECT (SELECT name FROM test LIMIT 1) AS n(nm) FROM test'
873
874
  end
874
875
  end
875
876
 
@@ -2046,6 +2047,10 @@ describe "Dataset#from_self" do
2046
2047
  @ds.from_self(:alias=>:some_name).sql.should == 'SELECT * FROM (SELECT name FROM test LIMIT 1) AS some_name'
2047
2048
  end
2048
2049
 
2050
+ specify "should use the user-specified column aliases" do
2051
+ @ds.from_self(:alias=>:some_name, :column_aliases=>[:c1, :c2]).sql.should == 'SELECT * FROM (SELECT name FROM test LIMIT 1) AS some_name(c1, c2)'
2052
+ end
2053
+
2049
2054
  specify "should use the user-specified alias for joins" do
2050
2055
  @ds.from_self(:alias=>:some_name).inner_join(:posts, :alias=>:name).sql.should == \
2051
2056
  'SELECT * FROM (SELECT name FROM test LIMIT 1) AS some_name INNER JOIN posts ON (posts.alias = some_name.name)'
@@ -2154,6 +2159,10 @@ describe "Dataset#join_table" do
2154
2159
  @d.from('stats').join(Sequel.expr(:players).as(:p), {:id => :player_id}).sql.should == 'SELECT * FROM "stats" INNER JOIN "players" AS "p" ON ("p"."id" = "stats"."player_id")'
2155
2160
  end
2156
2161
 
2162
+ specify "should support aliased tables with an implicit column aliases" do
2163
+ @d.from('stats').join(Sequel.expr(:players).as(:p, [:c1, :c2]), {:id => :player_id}).sql.should == 'SELECT * FROM "stats" INNER JOIN "players" AS "p"("c1", "c2") ON ("p"."id" = "stats"."player_id")'
2164
+ end
2165
+
2157
2166
  specify "should support using an alias for the FROM when doing the first join with unqualified condition columns" do
2158
2167
  @d.from(Sequel.as(:foo, :f)).join_table(:inner, :bar, :id => :bar_id).sql.should == 'SELECT * FROM "foo" AS "f" INNER JOIN "bar" ON ("bar"."id" = "f"."bar_id")'
2159
2168
  end
@@ -2894,11 +2903,32 @@ describe "Dataset#import" do
2894
2903
  'COMMIT']
2895
2904
  end
2896
2905
 
2906
+ specify "should slice based on the default_import_slice option" do
2907
+ def @ds.default_import_slice; 2 end
2908
+ @ds.import([:x, :y], [[1, 2], [3, 4], [5, 6]])
2909
+ @db.sqls.should == ['BEGIN',
2910
+ "INSERT INTO items (x, y) VALUES (1, 2)",
2911
+ "INSERT INTO items (x, y) VALUES (3, 4)",
2912
+ 'COMMIT',
2913
+ 'BEGIN',
2914
+ "INSERT INTO items (x, y) VALUES (5, 6)",
2915
+ 'COMMIT']
2916
+
2917
+ @ds.import([:x, :y], [[1, 2], [3, 4], [5, 6]], :slice=>nil)
2918
+ @db.sqls.should == ['BEGIN',
2919
+ "INSERT INTO items (x, y) VALUES (1, 2)",
2920
+ "INSERT INTO items (x, y) VALUES (3, 4)",
2921
+ "INSERT INTO items (x, y) VALUES (5, 6)",
2922
+ 'COMMIT']
2923
+ end
2924
+
2897
2925
  specify "should accept a columns array and a values array with :commit_every option" do
2898
- @ds.import([:x, :y], [[1, 2], [3, 4], [5, 6]], :commit_every => 3)
2926
+ @ds.import([:x, :y], [[1, 2], [3, 4], [5, 6]], :commit_every => 2)
2899
2927
  @db.sqls.should == ['BEGIN',
2900
2928
  "INSERT INTO items (x, y) VALUES (1, 2)",
2901
2929
  "INSERT INTO items (x, y) VALUES (3, 4)",
2930
+ 'COMMIT',
2931
+ 'BEGIN',
2902
2932
  "INSERT INTO items (x, y) VALUES (5, 6)",
2903
2933
  'COMMIT']
2904
2934
  end
@@ -2920,6 +2950,14 @@ describe "Dataset#import" do
2920
2950
  @db.sqls.should == ['BEGIN',
2921
2951
  "INSERT INTO items (x, y) VALUES (1, 2), (3, 4), (5, 6)",
2922
2952
  'COMMIT']
2953
+
2954
+ @ds.import([:x, :y], [[1, 2], [3, 4], [5, 6]], :slice=>2)
2955
+ @db.sqls.should == ['BEGIN',
2956
+ "INSERT INTO items (x, y) VALUES (1, 2), (3, 4)",
2957
+ 'COMMIT',
2958
+ 'BEGIN',
2959
+ "INSERT INTO items (x, y) VALUES (5, 6)",
2960
+ 'COMMIT']
2923
2961
  end
2924
2962
 
2925
2963
  specify "should use correct sql for :union strategy" do
@@ -2928,6 +2966,14 @@ describe "Dataset#import" do
2928
2966
  @db.sqls.should == ['BEGIN',
2929
2967
  "INSERT INTO items (x, y) SELECT 1, 2 UNION ALL SELECT 3, 4 UNION ALL SELECT 5, 6",
2930
2968
  'COMMIT']
2969
+
2970
+ @ds.import([:x, :y], [[1, 2], [3, 4], [5, 6]], :slice=>2)
2971
+ @db.sqls.should == ['BEGIN',
2972
+ "INSERT INTO items (x, y) SELECT 1, 2 UNION ALL SELECT 3, 4",
2973
+ 'COMMIT',
2974
+ 'BEGIN',
2975
+ "INSERT INTO items (x, y) SELECT 5, 6",
2976
+ 'COMMIT']
2931
2977
  end
2932
2978
 
2933
2979
  specify "should use correct sql for :union strategy when FROM is required" do
@@ -2937,6 +2983,14 @@ describe "Dataset#import" do
2937
2983
  @db.sqls.should == ['BEGIN',
2938
2984
  "INSERT INTO items (x, y) SELECT 1, 2 FROM foo UNION ALL SELECT 3, 4 FROM foo UNION ALL SELECT 5, 6 FROM foo",
2939
2985
  'COMMIT']
2986
+
2987
+ @ds.import([:x, :y], [[1, 2], [3, 4], [5, 6]], :slice=>2)
2988
+ @db.sqls.should == ['BEGIN',
2989
+ "INSERT INTO items (x, y) SELECT 1, 2 FROM foo UNION ALL SELECT 3, 4 FROM foo",
2990
+ 'COMMIT',
2991
+ 'BEGIN',
2992
+ "INSERT INTO items (x, y) SELECT 5, 6 FROM foo",
2993
+ 'COMMIT']
2940
2994
  end
2941
2995
  end
2942
2996
 
@@ -3622,6 +3676,7 @@ describe "Sequel::Dataset#qualify" do
3622
3676
 
3623
3677
  specify "should handle SQL::AliasedExpressions" do
3624
3678
  @ds.select(Sequel.expr(:a).as(:b)).qualify.sql.should == 'SELECT t.a AS b FROM t'
3679
+ @ds.select(Sequel.expr(:a).as(:b, [:c, :d])).qualify.sql.should == 'SELECT t.a AS b(c, d) FROM t'
3625
3680
  end
3626
3681
 
3627
3682
  specify "should handle SQL::CaseExpressions" do
@@ -3660,7 +3715,7 @@ describe "Sequel::Dataset#qualify" do
3660
3715
  @ds.filter(Sequel::SQL::Wrapper.new(:a)).qualify.sql.should == 'SELECT t.* FROM t WHERE t.a'
3661
3716
  end
3662
3717
 
3663
- specify "should handle SQL::WindowFunctions" do
3718
+ specify "should handle SQL::Functions with windows" do
3664
3719
  meta_def(@ds, :supports_window_functions?){true}
3665
3720
  @ds.select{sum(:a).over(:partition=>:b, :order=>:c)}.qualify.sql.should == 'SELECT sum(t.a) OVER (PARTITION BY t.b ORDER BY t.c) FROM t'
3666
3721
  end
@@ -4345,7 +4400,7 @@ describe "Custom ASTTransformer" do
4345
4400
  end.new
4346
4401
  ds = Sequel.mock.dataset.from(:t).cross_join(:a___g).join(:b___h, [:c]).join(:d___i, :e=>:f)
4347
4402
  ds.sql.should == 'SELECT * FROM t CROSS JOIN a AS g INNER JOIN b AS h USING (c) INNER JOIN d AS i ON (i.e = h.f)'
4348
- ds.clone(:from=>c.transform(ds.opts[:from]), :join=>c.transform(ds.opts[:join])).sql.should == 'SELECT * FROM tt CROSS JOIN aa AS gg INNER JOIN bb AS hh USING (cc) INNER JOIN dd AS ii ON (ii.ee = hh.ff)'
4403
+ ds.clone(:from=>c.transform(ds.opts[:from]), :join=>c.transform(ds.opts[:join])).sql.should == 'SELECT * FROM tt CROSS JOIN aa AS g INNER JOIN bb AS h USING (cc) INNER JOIN dd AS i ON (ii.ee = hh.ff)'
4349
4404
  end
4350
4405
  end
4351
4406