sequel 4.10.0 → 4.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +58 -0
  3. data/doc/association_basics.rdoc +1 -1
  4. data/doc/cheat_sheet.rdoc +0 -1
  5. data/doc/core_extensions.rdoc +2 -2
  6. data/doc/dataset_filtering.rdoc +5 -5
  7. data/doc/model_hooks.rdoc +9 -0
  8. data/doc/object_model.rdoc +7 -13
  9. data/doc/opening_databases.rdoc +3 -1
  10. data/doc/querying.rdoc +8 -8
  11. data/doc/release_notes/4.11.0.txt +147 -0
  12. data/doc/sql.rdoc +11 -7
  13. data/doc/virtual_rows.rdoc +4 -5
  14. data/lib/sequel/adapters/ibmdb.rb +24 -16
  15. data/lib/sequel/adapters/jdbc/h2.rb +5 -0
  16. data/lib/sequel/adapters/jdbc/hsqldb.rb +5 -0
  17. data/lib/sequel/adapters/mock.rb +14 -2
  18. data/lib/sequel/adapters/shared/access.rb +6 -9
  19. data/lib/sequel/adapters/shared/cubrid.rb +5 -0
  20. data/lib/sequel/adapters/shared/db2.rb +5 -0
  21. data/lib/sequel/adapters/shared/firebird.rb +5 -0
  22. data/lib/sequel/adapters/shared/mssql.rb +23 -16
  23. data/lib/sequel/adapters/shared/mysql.rb +12 -2
  24. data/lib/sequel/adapters/shared/oracle.rb +31 -15
  25. data/lib/sequel/adapters/shared/postgres.rb +28 -4
  26. data/lib/sequel/adapters/shared/sqlanywhere.rb +5 -0
  27. data/lib/sequel/adapters/shared/sqlite.rb +12 -1
  28. data/lib/sequel/ast_transformer.rb +9 -7
  29. data/lib/sequel/connection_pool.rb +10 -4
  30. data/lib/sequel/database/features.rb +15 -0
  31. data/lib/sequel/database/schema_generator.rb +2 -2
  32. data/lib/sequel/database/schema_methods.rb +21 -3
  33. data/lib/sequel/database/transactions.rb +8 -4
  34. data/lib/sequel/dataset/actions.rb +13 -7
  35. data/lib/sequel/dataset/features.rb +7 -0
  36. data/lib/sequel/dataset/query.rb +28 -11
  37. data/lib/sequel/dataset/sql.rb +90 -14
  38. data/lib/sequel/extensions/constraint_validations.rb +2 -2
  39. data/lib/sequel/extensions/date_arithmetic.rb +1 -1
  40. data/lib/sequel/extensions/eval_inspect.rb +12 -6
  41. data/lib/sequel/extensions/pg_array_ops.rb +11 -2
  42. data/lib/sequel/extensions/pg_json.rb +130 -23
  43. data/lib/sequel/extensions/pg_json_ops.rb +196 -28
  44. data/lib/sequel/extensions/to_dot.rb +5 -7
  45. data/lib/sequel/model/associations.rb +0 -50
  46. data/lib/sequel/plugins/class_table_inheritance.rb +49 -21
  47. data/lib/sequel/plugins/many_through_many.rb +10 -11
  48. data/lib/sequel/plugins/serialization.rb +4 -1
  49. data/lib/sequel/plugins/sharding.rb +0 -9
  50. data/lib/sequel/plugins/single_table_inheritance.rb +4 -2
  51. data/lib/sequel/plugins/timestamps.rb +2 -2
  52. data/lib/sequel/sql.rb +166 -44
  53. data/lib/sequel/version.rb +1 -1
  54. data/spec/adapters/postgres_spec.rb +199 -133
  55. data/spec/core/connection_pool_spec.rb +6 -0
  56. data/spec/core/database_spec.rb +12 -0
  57. data/spec/core/dataset_spec.rb +58 -3
  58. data/spec/core/expression_filters_spec.rb +67 -5
  59. data/spec/core/mock_adapter_spec.rb +8 -4
  60. data/spec/core/schema_spec.rb +7 -0
  61. data/spec/core_extensions_spec.rb +14 -0
  62. data/spec/extensions/class_table_inheritance_spec.rb +23 -3
  63. data/spec/extensions/core_refinements_spec.rb +14 -0
  64. data/spec/extensions/eval_inspect_spec.rb +8 -4
  65. data/spec/extensions/pg_array_ops_spec.rb +6 -0
  66. data/spec/extensions/pg_json_ops_spec.rb +99 -0
  67. data/spec/extensions/pg_json_spec.rb +104 -4
  68. data/spec/extensions/serialization_spec.rb +19 -0
  69. data/spec/extensions/single_table_inheritance_spec.rb +11 -3
  70. data/spec/extensions/timestamps_spec.rb +10 -0
  71. data/spec/extensions/to_dot_spec.rb +8 -4
  72. data/spec/integration/database_test.rb +1 -1
  73. data/spec/integration/dataset_test.rb +9 -0
  74. data/spec/integration/schema_test.rb +27 -0
  75. metadata +4 -2
@@ -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