sequel 4.3.0 → 4.4.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 (93) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +34 -0
  3. data/README.rdoc +7 -7
  4. data/Rakefile +2 -2
  5. data/doc/active_record.rdoc +2 -2
  6. data/doc/association_basics.rdoc +21 -7
  7. data/doc/bin_sequel.rdoc +2 -2
  8. data/doc/cheat_sheet.rdoc +2 -1
  9. data/doc/dataset_basics.rdoc +1 -1
  10. data/doc/dataset_filtering.rdoc +1 -1
  11. data/doc/migration.rdoc +2 -2
  12. data/doc/object_model.rdoc +2 -2
  13. data/doc/opening_databases.rdoc +13 -1
  14. data/doc/querying.rdoc +9 -4
  15. data/doc/release_notes/4.4.0.txt +92 -0
  16. data/doc/schema_modification.rdoc +1 -1
  17. data/doc/security.rdoc +2 -2
  18. data/doc/sql.rdoc +3 -3
  19. data/doc/thread_safety.rdoc +1 -1
  20. data/doc/validations.rdoc +1 -1
  21. data/doc/virtual_rows.rdoc +1 -1
  22. data/lib/sequel/adapters/jdbc.rb +85 -19
  23. data/lib/sequel/adapters/jdbc/db2.rb +1 -1
  24. data/lib/sequel/adapters/jdbc/derby.rb +1 -1
  25. data/lib/sequel/adapters/jdbc/h2.rb +2 -2
  26. data/lib/sequel/adapters/jdbc/hsqldb.rb +7 -0
  27. data/lib/sequel/adapters/jdbc/jtds.rb +1 -1
  28. data/lib/sequel/adapters/jdbc/oracle.rb +1 -1
  29. data/lib/sequel/adapters/jdbc/postgresql.rb +34 -3
  30. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +57 -0
  31. data/lib/sequel/adapters/jdbc/sqlserver.rb +2 -2
  32. data/lib/sequel/adapters/oracle.rb +1 -1
  33. data/lib/sequel/adapters/shared/db2.rb +5 -0
  34. data/lib/sequel/adapters/shared/oracle.rb +41 -4
  35. data/lib/sequel/adapters/shared/sqlanywhere.rb +458 -0
  36. data/lib/sequel/adapters/sqlanywhere.rb +177 -0
  37. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +11 -3
  38. data/lib/sequel/core.rb +4 -4
  39. data/lib/sequel/database/connecting.rb +1 -1
  40. data/lib/sequel/database/query.rb +1 -1
  41. data/lib/sequel/database/schema_generator.rb +1 -1
  42. data/lib/sequel/database/schema_methods.rb +2 -2
  43. data/lib/sequel/dataset.rb +1 -1
  44. data/lib/sequel/dataset/actions.rb +2 -0
  45. data/lib/sequel/dataset/graph.rb +1 -1
  46. data/lib/sequel/dataset/prepared_statements.rb +1 -1
  47. data/lib/sequel/dataset/query.rb +37 -16
  48. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  49. data/lib/sequel/extensions/date_arithmetic.rb +2 -2
  50. data/lib/sequel/extensions/migration.rb +1 -1
  51. data/lib/sequel/extensions/mssql_emulate_lateral_with_apply.rb +5 -4
  52. data/lib/sequel/extensions/pg_array.rb +2 -2
  53. data/lib/sequel/extensions/pg_array_ops.rb +2 -2
  54. data/lib/sequel/extensions/pg_hstore.rb +2 -2
  55. data/lib/sequel/extensions/pg_hstore_ops.rb +2 -2
  56. data/lib/sequel/extensions/pg_json.rb +2 -2
  57. data/lib/sequel/extensions/pg_json_ops.rb +2 -2
  58. data/lib/sequel/extensions/pg_range.rb +2 -2
  59. data/lib/sequel/extensions/pg_range_ops.rb +2 -2
  60. data/lib/sequel/extensions/pg_row.rb +2 -2
  61. data/lib/sequel/extensions/pg_row_ops.rb +3 -3
  62. data/lib/sequel/model.rb +1 -1
  63. data/lib/sequel/model/associations.rb +106 -17
  64. data/lib/sequel/model/base.rb +23 -19
  65. data/lib/sequel/plugins/json_serializer.rb +1 -1
  66. data/lib/sequel/plugins/many_through_many.rb +14 -6
  67. data/lib/sequel/plugins/pg_array_associations.rb +28 -0
  68. data/lib/sequel/plugins/rcte_tree.rb +1 -1
  69. data/lib/sequel/plugins/serialization.rb +11 -0
  70. data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
  71. data/lib/sequel/plugins/table_select.rb +41 -0
  72. data/lib/sequel/plugins/tree.rb +1 -1
  73. data/lib/sequel/sql.rb +2 -2
  74. data/lib/sequel/version.rb +1 -1
  75. data/spec/adapters/oracle_spec.rb +22 -1
  76. data/spec/adapters/postgres_spec.rb +31 -48
  77. data/spec/adapters/sqlanywhere_spec.rb +170 -0
  78. data/spec/core/dataset_spec.rb +109 -0
  79. data/spec/core/object_graph_spec.rb +7 -0
  80. data/spec/extensions/constraint_validations_spec.rb +7 -0
  81. data/spec/extensions/core_refinements_spec.rb +1 -1
  82. data/spec/extensions/many_through_many_spec.rb +65 -0
  83. data/spec/extensions/pg_array_associations_spec.rb +44 -0
  84. data/spec/extensions/rcte_tree_spec.rb +3 -3
  85. data/spec/extensions/spec_helper.rb +1 -1
  86. data/spec/extensions/table_select_spec.rb +71 -0
  87. data/spec/integration/associations_test.rb +279 -7
  88. data/spec/integration/dataset_test.rb +13 -4
  89. data/spec/integration/schema_test.rb +12 -14
  90. data/spec/model/associations_spec.rb +472 -3
  91. data/spec/model/class_dataset_methods_spec.rb +1 -0
  92. data/spec/model/model_spec.rb +10 -0
  93. metadata +10 -2
@@ -0,0 +1,170 @@
1
+ SEQUEL_ADAPTER_TEST = :sqlanywhere
2
+
3
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper.rb')
4
+
5
+ if DB.table_exists?(:test)
6
+ DB.drop_table(:test)
7
+ end
8
+
9
+ describe "Convert smallint to boolean" do
10
+ before do
11
+ @db = DB
12
+ end
13
+ after do
14
+ Sequel::SqlAnywhere.convert_smallint_to_bool = true
15
+ @db.convert_smallint_to_bool = true
16
+ end
17
+
18
+ describe "Sequel::SqlAnywhere.convert_smallint_to_bool" do
19
+ before do
20
+ @db.create_table!(:booltest){column :b, 'smallint'; column :i, 'integer'}
21
+ @ds = @db[:booltest]
22
+ end
23
+ after do
24
+ @db.drop_table(:booltest)
25
+ end
26
+
27
+ specify "should consider smallint datatypes as boolean if set, but if not, as larger smallints" do
28
+ @db.create_table!(:booltest){column :b, 'smallint'; column :i, 'integer'}
29
+ @db.schema(:booltest, :reload=>true).first.last[:type].should == :boolean
30
+ @db.schema(:booltest, :reload=>true).first.last[:db_type].should match /smallint/i
31
+
32
+ Sequel::SqlAnywhere.convert_smallint_to_bool = false
33
+ @db2 = Sequel.connect(DB.url)
34
+ @db2.schema(:booltest, :reload=>true).first.last[:type].should == :integer
35
+ @db2.schema(:booltest, :reload=>true).first.last[:db_type].should match /smallint/i
36
+
37
+ @db.schema(:booltest, :reload=>true).first.last[:type].should == :boolean
38
+ @db.schema(:booltest, :reload=>true).first.last[:db_type].should match /smallint/i
39
+
40
+ @db2.disconnect
41
+ end
42
+
43
+ describe "datasets" do
44
+ specify "should return smallints as bools and integers as integers when set" do
45
+ @ds.delete
46
+ @ds << {:b=>true, :i=>10}
47
+ @ds.all.should == [{:b=>true, :i=>10}]
48
+ @ds.delete
49
+ @ds << {:b=>false, :i=>0}
50
+ @ds.all.should == [{:b=>false, :i=>0}]
51
+ @ds.delete
52
+ @ds << {:b=>true, :i=>1}
53
+ @ds.all.should == [{:b=>true, :i=>1}]
54
+ end
55
+
56
+ specify "should return all smallints as integers when unset" do
57
+ Sequel::SqlAnywhere.convert_smallint_to_bool = false
58
+ @db2 = Sequel.connect(DB.url)
59
+ @ds2 = @db2[:booltest]
60
+ @ds2.delete
61
+ @ds2 << {:b=>true, :i=>10}
62
+ @ds2.all.should == [{:b=>1, :i=>10}]
63
+ @ds2.delete
64
+ @ds2 << {:b=>false, :i=>0}
65
+ @ds2.all.should == [{:b=>0, :i=>0}]
66
+
67
+ @ds2.delete
68
+ @ds2 << {:b=>1, :i=>10}
69
+ @ds2.all.should == [{:b=>1, :i=>10}]
70
+ @ds2.delete
71
+ @ds2 << {:b=>0, :i=>0}
72
+ @ds2.all.should == [{:b=>0, :i=>0}]
73
+
74
+ @db2.disconnect
75
+ end
76
+ end
77
+ end
78
+
79
+ describe "Database#convert_smallint_to_bool" do
80
+ before do
81
+ @db.create_table!(:booltest){column :b, 'smallint'; column :i, 'integer'}
82
+ end
83
+ after do
84
+ @db.drop_table(:booltest)
85
+ end
86
+
87
+ specify "should consider smallint datatypes as boolean if set, but not larger smallints" do
88
+ @db.schema(:booltest, :reload=>true).first.last[:type].should == :boolean
89
+ @db.schema(:booltest, :reload=>true).first.last[:db_type].should match /smallint/i
90
+ @db.convert_smallint_to_bool = false
91
+ @db.schema(:booltest, :reload=>true).first.last[:type].should == :integer
92
+ @db.schema(:booltest, :reload=>true).first.last[:db_type].should match /smallint/i
93
+ end
94
+
95
+ describe "datasets" do
96
+ specify "should return smallints as bools and integers as integers when set" do
97
+ @ds = @db[:booltest]
98
+ @ds.delete
99
+ @ds << {:b=>true, :i=>10}
100
+ @ds.all.should == [{:b=>true, :i=>10}]
101
+ @ds.delete
102
+ @ds << {:b=>false, :i=>0}
103
+ @ds.all.should == [{:b=>false, :i=>0}]
104
+ @ds.delete
105
+ @ds << {:b=>true, :i=>1}
106
+ @ds.all.should == [{:b=>true, :i=>1}]
107
+ end
108
+
109
+ specify "should return all smallints as integers when unset" do
110
+ @db2 = Sequel.connect(DB.url)
111
+ @db2.convert_smallint_to_bool = false
112
+ @ds2 = @db2[:booltest]
113
+ @ds2.delete
114
+ @ds2 << {:b=>true, :i=>10}
115
+ @ds2.all.should == [{:b=>1, :i=>10}]
116
+ @ds2.delete
117
+ @ds2 << {:b=>false, :i=>0}
118
+ @ds2.all.should == [{:b=>0, :i=>0}]
119
+
120
+ @ds2.delete
121
+ @ds2 << {:b=>1, :i=>10}
122
+ @ds2.all.should == [{:b=>1, :i=>10}]
123
+ @ds2.delete
124
+ @ds2 << {:b=>0, :i=>0}
125
+ @ds2.all.should == [{:b=>0, :i=>0}]
126
+
127
+ @db2.disconnect
128
+ end
129
+ end
130
+ end
131
+
132
+ describe "Dataset#convert_smallint_to_bool" do
133
+ before do
134
+ @db.create_table!(:booltest){column :b, 'smallint'; column :i, 'integer'}
135
+ @ds = @db[:booltest]
136
+ end
137
+ after do
138
+ @db.drop_table(:booltest)
139
+ end
140
+
141
+ specify "should return smallints as bools and integers as integers when set" do
142
+ @ds.delete
143
+ @ds << {:b=>true, :i=>10}
144
+ @ds.all.should == [{:b=>true, :i=>10}]
145
+ @ds.delete
146
+ @ds << {:b=>false, :i=>0}
147
+ @ds.all.should == [{:b=>false, :i=>0}]
148
+ @ds.delete
149
+ @ds << {:b=>true, :i=>1}
150
+ @ds.all.should == [{:b=>true, :i=>1}]
151
+ end
152
+
153
+ specify "should return all smallints as integers when unset" do
154
+ @ds.convert_smallint_to_bool = false
155
+ @ds.delete
156
+ @ds << {:b=>true, :i=>10}
157
+ @ds.all.should == [{:b=>1, :i=>10}]
158
+ @ds.delete
159
+ @ds << {:b=>false, :i=>0}
160
+ @ds.all.should == [{:b=>0, :i=>0}]
161
+
162
+ @ds.delete
163
+ @ds << {:b=>1, :i=>10}
164
+ @ds.all.should == [{:b=>1, :i=>10}]
165
+ @ds.delete
166
+ @ds << {:b=>0, :i=>0}
167
+ @ds.all.should == [{:b=>0, :i=>0}]
168
+ end
169
+ end
170
+ end
@@ -1625,6 +1625,49 @@ describe "Dataset#limit" do
1625
1625
  end
1626
1626
  end
1627
1627
 
1628
+ describe "Dataset#offset" do
1629
+ before do
1630
+ @dataset = Sequel.mock.dataset.from(:test)
1631
+ end
1632
+
1633
+ specify "should include an OFFSET clause in the select statement" do
1634
+ @dataset.offset(10).sql.should == 'SELECT * FROM test OFFSET 10'
1635
+ end
1636
+
1637
+ specify "should convert regular strings to integers" do
1638
+ @dataset.offset('a() - 1').sql.should == 'SELECT * FROM test OFFSET 0'
1639
+ end
1640
+
1641
+ specify "should raise an error if a negative offset is used" do
1642
+ proc{@dataset.offset(-1)}.should raise_error(Sequel::Error)
1643
+ end
1644
+
1645
+ specify "should be able to reset offset with nil values" do
1646
+ @dataset.offset(6).offset(nil).sql.should == 'SELECT * FROM test'
1647
+ end
1648
+
1649
+ specify "should not convert literal strings to integers" do
1650
+ @dataset.offset(Sequel.lit('a() - 1')).sql.should == 'SELECT * FROM test OFFSET a() - 1'
1651
+ end
1652
+
1653
+ specify "should not convert other objects" do
1654
+ @dataset.offset(Sequel.function(:a) - 1).sql.should == 'SELECT * FROM test OFFSET (a() - 1)'
1655
+ end
1656
+
1657
+ specify "should override offset given to limit" do
1658
+ @dataset.limit(nil, 5).offset(6).sql.should == 'SELECT * FROM test OFFSET 6'
1659
+ end
1660
+
1661
+ specify "should not be overridable by limit if limit is not given an offset" do
1662
+ @dataset.offset(6).limit(nil).sql.should == 'SELECT * FROM test OFFSET 6'
1663
+ end
1664
+
1665
+ specify "should be overridable by limit if limit is given an offset" do
1666
+ @dataset.offset(6).limit(nil, nil).sql.should == 'SELECT * FROM test'
1667
+ @dataset.offset(6).limit(nil, 5).sql.should == 'SELECT * FROM test OFFSET 5'
1668
+ end
1669
+ end
1670
+
1628
1671
  describe "Dataset#naked" do
1629
1672
  specify "should returned clone dataset without row_proc" do
1630
1673
  d = Sequel.mock.dataset
@@ -4584,3 +4627,69 @@ describe "Frozen Datasets" do
4584
4627
  @ds.select(:a).sql.should == 'SELECT a FROM test'
4585
4628
  end
4586
4629
  end
4630
+
4631
+ describe "Dataset mutation methods" do
4632
+ def m(&block)
4633
+ ds = Sequel.mock[:t]
4634
+ ds.instance_exec(&block)
4635
+ ds.sql
4636
+ end
4637
+
4638
+ it "should modify the dataset in place" do
4639
+ dsc = Sequel.mock[:u]
4640
+ dsc.instance_variable_set(:@columns, [:v])
4641
+
4642
+ m{and!(:a=>1).or!(:b=>2)}.should == "SELECT * FROM t WHERE ((a = 1) OR (b = 2))"
4643
+ m{select!(:f).graph!(dsc, :b=>:c).set_graph_aliases!(:e=>[:m, :n]).add_graph_aliases!(:d=>[:g, :c])}.should == "SELECT m.n AS e, g.c AS d FROM t LEFT OUTER JOIN u ON (u.b = t.c)"
4644
+ m{cross_join!(:a)}.should == "SELECT * FROM t CROSS JOIN a"
4645
+ m{distinct!}.should == "SELECT DISTINCT * FROM t"
4646
+ m{except!(dsc)}.should == "SELECT * FROM (SELECT * FROM t EXCEPT SELECT * FROM u) AS t1"
4647
+ m{exclude!(:a=>1)}.should == "SELECT * FROM t WHERE (a != 1)"
4648
+ m{exclude_having!(:a=>1)}.should == "SELECT * FROM t HAVING (a != 1)"
4649
+ m{exclude_where!(:a=>1)}.should == "SELECT * FROM t WHERE (a != 1)"
4650
+ m{filter!(:a=>1)}.should == "SELECT * FROM t WHERE (a = 1)"
4651
+ m{for_update!}.should == "SELECT * FROM t FOR UPDATE"
4652
+ m{from!(:p)}.should == "SELECT * FROM p"
4653
+ m{full_join!(:a, [:b])}.should == "SELECT * FROM t FULL JOIN a USING (b)"
4654
+ m{full_outer_join!(:a, [:b])}.should == "SELECT * FROM t FULL OUTER JOIN a USING (b)"
4655
+ m{grep!(:a, 'b')}.should == "SELECT * FROM t WHERE ((a LIKE 'b' ESCAPE '\\'))"
4656
+ m{group!(:a)}.should == "SELECT * FROM t GROUP BY a"
4657
+ m{group_and_count!(:a)}.should == "SELECT a, count(*) AS count FROM t GROUP BY a"
4658
+ m{group_by!(:a)}.should == "SELECT * FROM t GROUP BY a"
4659
+ m{having!(:a)}.should == "SELECT * FROM t HAVING a"
4660
+ m{inner_join!(:a, [:b])}.should == "SELECT * FROM t INNER JOIN a USING (b)"
4661
+ m{intersect!(dsc)}.should == "SELECT * FROM (SELECT * FROM t INTERSECT SELECT * FROM u) AS t1"
4662
+ m{where!(:a).invert!}.should == "SELECT * FROM t WHERE NOT a"
4663
+ m{join!(:a, [:b])}.should == "SELECT * FROM t INNER JOIN a USING (b)"
4664
+ m{join_table!(:inner, :a, [:b])}.should == "SELECT * FROM t INNER JOIN a USING (b)"
4665
+ m{left_join!(:a, [:b])}.should == "SELECT * FROM t LEFT JOIN a USING (b)"
4666
+ m{left_outer_join!(:a, [:b])}.should == "SELECT * FROM t LEFT OUTER JOIN a USING (b)"
4667
+ m{limit!(1)}.should == "SELECT * FROM t LIMIT 1"
4668
+ m{lock_style!(:update)}.should == "SELECT * FROM t FOR UPDATE"
4669
+ m{natural_full_join!(:a)}.should == "SELECT * FROM t NATURAL FULL JOIN a"
4670
+ m{natural_join!(:a)}.should == "SELECT * FROM t NATURAL JOIN a"
4671
+ m{natural_left_join!(:a)}.should == "SELECT * FROM t NATURAL LEFT JOIN a"
4672
+ m{natural_right_join!(:a)}.should == "SELECT * FROM t NATURAL RIGHT JOIN a"
4673
+ m{offset!(1)}.should == "SELECT * FROM t OFFSET 1"
4674
+ m{order!(:a).reverse_order!}.should == "SELECT * FROM t ORDER BY a DESC"
4675
+ m{order_by!(:a).order_more!(:b).order_append!(:c).order_prepend!(:d).reverse!}.should == "SELECT * FROM t ORDER BY d DESC, a DESC, b DESC, c DESC"
4676
+ m{qualify!}.should == "SELECT t.* FROM t"
4677
+ m{right_join!(:a, [:b])}.should == "SELECT * FROM t RIGHT JOIN a USING (b)"
4678
+ m{right_outer_join!(:a, [:b])}.should == "SELECT * FROM t RIGHT OUTER JOIN a USING (b)"
4679
+ m{select!(:a)}.should == "SELECT a FROM t"
4680
+ m{select_all!(:t).select_more!(:b).select_append!(:c)}.should == "SELECT t.*, b, c FROM t"
4681
+ m{select_group!(:a)}.should == "SELECT a FROM t GROUP BY a"
4682
+ m{where!(:a).unfiltered!}.should == "SELECT * FROM t"
4683
+ m{group!(:a).ungrouped!}.should == "SELECT * FROM t"
4684
+ m{limit!(1).unlimited!}.should == "SELECT * FROM t"
4685
+ m{order!(:a).unordered!}.should == "SELECT * FROM t"
4686
+ m{union!(dsc)}.should == "SELECT * FROM (SELECT * FROM t UNION SELECT * FROM u) AS t1"
4687
+ m{with!(:a, dsc)}.should == "WITH a AS (SELECT * FROM u) SELECT * FROM t"
4688
+ m{with_recursive!(:a, dsc, dsc)}.should == "WITH a AS (SELECT * FROM u UNION ALL SELECT * FROM u) SELECT * FROM t"
4689
+ m{with_sql!('SELECT foo')}.should == "SELECT foo"
4690
+
4691
+ dsc.server!(:a)
4692
+ dsc.opts[:server].should == :a
4693
+ dsc.graph!(dsc, {:b=>:c}, :table_alias=>:foo).ungraphed!.opts[:graph].should be_nil
4694
+ end
4695
+ end
@@ -61,9 +61,16 @@ describe Sequel::Dataset, " graphing" do
61
61
  proc{@ds1.select(1).graph(@ds2, :x=>:id)}.should raise_error(Sequel::Error)
62
62
  end
63
63
 
64
+ it "#graph should accept a complex dataset and pass it directly to join" do
65
+ ds = @ds1.graph(@ds2.select_all(:lines), {:x=>:id})
66
+ ds.sql.should == 'SELECT points.id, points.x, points.y, lines.id AS lines_id, lines.x AS lines_x, lines.y AS lines_y, lines.graph_id FROM points LEFT OUTER JOIN lines ON (lines.x = points.id)'
67
+ end
68
+
64
69
  it "#graph should accept a complex dataset and pass it directly to join" do
65
70
  ds = @ds1.graph(@ds2.filter(:x=>1), {:x=>:id})
66
71
  ds.sql.should == 'SELECT points.id, points.x, points.y, t1.id AS t1_id, t1.x AS t1_x, t1.y AS t1_y, t1.graph_id FROM points LEFT OUTER JOIN (SELECT * FROM lines WHERE (x = 1)) AS t1 ON (t1.x = points.id)'
72
+ ds = @ds1.graph(@ds2.select_all(:lines).filter(:x=>1), {:x=>:id})
73
+ ds.sql.should == 'SELECT points.id, points.x, points.y, t1.id AS t1_id, t1.x AS t1_x, t1.y AS t1_y, t1.graph_id FROM points LEFT OUTER JOIN (SELECT lines.* FROM lines WHERE (x = 1)) AS t1 ON (t1.x = points.id)'
67
74
  end
68
75
 
69
76
  it "#graph should work on from_self datasets" do
@@ -276,6 +276,13 @@ describe "constraint_validations extension" do
276
276
  @db.sqls.should == ["DELETE FROM sequel_constraint_validations WHERE ((table, constraint_name) IN (('foo', 'bar')))", "ALTER TABLE foo DROP CONSTRAINT bar"]
277
277
  end
278
278
 
279
+ it "should drop constraints and validations before adding new ones" do
280
+ @db.alter_table(:foo){String :name; validate{unique :name; drop :bar}}
281
+ sqls = @db.sqls
282
+ parse_insert(sqls.slice!(2)).should == {:validation_type=>"unique", :column=>"name", :table=>"foo"}
283
+ sqls.should == ["DELETE FROM sequel_constraint_validations WHERE ((table, constraint_name) IN (('foo', 'bar')))", "BEGIN", "COMMIT", "ALTER TABLE foo ADD UNIQUE (name)", "ALTER TABLE foo DROP CONSTRAINT bar"]
284
+ end
285
+
279
286
  it "should raise an error if attempting to validate inclusion with a range of non-integers" do
280
287
  proc{@db.create_table(:foo){String :name; validate{includes 'a'..'z', :name}}}.should raise_error(Sequel::Error)
281
288
  end
@@ -1,6 +1,6 @@
1
1
  require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
2
 
3
- if RUBY_VERSION >= '2.0.0'
3
+ if RUBY_VERSION >= '2.0.0' && respond_to?(:using)
4
4
  Sequel.extension :core_refinements, :pg_array, :pg_hstore, :pg_row, :pg_range, :pg_row_ops, :pg_range_ops, :pg_array_ops, :pg_hstore_ops, :pg_json, :pg_json_ops
5
5
  using Sequel::CoreRefinements
6
6
 
@@ -154,6 +154,21 @@ describe Sequel::Model, "many_through_many" do
154
154
  @c1.filter(:tags=>@c2.load(:h1=>1234, :h2=>85)).sql.should == 'SELECT * FROM artists WHERE ((artists.id, artists.yyy) IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE ((albums_tags.g1 = 1234) AND (albums_tags.g2 = 85) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL))))'
155
155
  end
156
156
 
157
+ it "should allowing filtering by many_through_many associations with :conditions" do
158
+ @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :conditions=>{:name=>'A'}
159
+ @c1.filter(:tags=>@c2.load(:id=>1234)).sql.should == "SELECT * FROM artists WHERE ((artists.id IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id = 1234) AND (albums_artists.artist_id IS NOT NULL)))) AND (artists.id IN (SELECT albums_artists.artist_id FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON (albums_artists.album_id = albums.id) WHERE ((name = 'A') AND (tags.id = 1234)))))"
160
+ end
161
+
162
+ it "should allowing filtering by many_through_many associations with :conditions with a single through table" do
163
+ @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id]], :conditions=>{:name=>'A'}
164
+ @c1.filter(:tags=>@c2.load(:id=>1234)).sql.should == "SELECT * FROM artists WHERE ((artists.id IN (SELECT albums_artists.artist_id FROM albums_artists WHERE ((albums_artists.album_id = 1234) AND (albums_artists.artist_id IS NOT NULL)))) AND (artists.id IN (SELECT albums_artists.artist_id FROM tags INNER JOIN albums_artists ON (albums_artists.album_id = tags.id) WHERE ((name = 'A') AND (tags.id = 1234)))))"
165
+ end
166
+
167
+ it "should allowing filtering by many_through_many associations with :conditions and composite keys" do
168
+ @c1.many_through_many :tags, [[:albums_artists, [:b1, :b2], [:c1, :c2]], [:albums, [:d1, :d2], [:e1, :e2]], [:albums_tags, [:f1, :f2], [:g1, :g2]]], :right_primary_key=>[:h1, :h2], :left_primary_key=>[:id, :yyy], :conditions=>{:name=>'A'}
169
+ @c1.filter(:tags=>@c2.load(:id=>1, :h1=>1234, :h2=>85)).sql.should == "SELECT * FROM artists WHERE (((artists.id, artists.yyy) IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE ((albums_tags.g1 = 1234) AND (albums_tags.g2 = 85) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL)))) AND ((artists.id, artists.yyy) IN (SELECT albums_artists.b1, albums_artists.b2 FROM tags INNER JOIN albums_tags ON ((albums_tags.g1 = tags.h1) AND (albums_tags.g2 = tags.h2)) INNER JOIN albums ON ((albums.e1 = albums_tags.f1) AND (albums.e2 = albums_tags.f2)) INNER JOIN albums_artists ON ((albums_artists.c1 = albums.d1) AND (albums_artists.c2 = albums.d2)) WHERE ((name = 'A') AND (tags.id = 1)))))"
170
+ end
171
+
157
172
  it "should allowing excluding by many_through_many associations" do
158
173
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
159
174
  @c1.exclude(:tags=>@c2.load(:id=>1234)).sql.should == 'SELECT * FROM artists WHERE ((artists.id NOT IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id = 1234) AND (albums_artists.artist_id IS NOT NULL)))) OR (artists.id IS NULL))'
@@ -164,6 +179,16 @@ describe Sequel::Model, "many_through_many" do
164
179
  @c1.exclude(:tags=>@c2.load(:h1=>1234, :h2=>85)).sql.should == 'SELECT * FROM artists WHERE (((artists.id, artists.yyy) NOT IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE ((albums_tags.g1 = 1234) AND (albums_tags.g2 = 85) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL)))) OR (artists.id IS NULL) OR (artists.yyy IS NULL))'
165
180
  end
166
181
 
182
+ it "should allowing excluding by many_through_many associations with :conditions" do
183
+ @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :conditions=>{:name=>'A'}
184
+ @c1.exclude(:tags=>@c2.load(:id=>1234)).sql.should == "SELECT * FROM artists WHERE ((artists.id NOT IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id = 1234) AND (albums_artists.artist_id IS NOT NULL)))) OR (artists.id NOT IN (SELECT albums_artists.artist_id FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON (albums_artists.album_id = albums.id) WHERE ((name = 'A') AND (tags.id = 1234)))) OR (artists.id IS NULL))"
185
+ end
186
+
187
+ it "should allowing excluding by many_through_many associations with :conditions and composite keys" do
188
+ @c1.many_through_many :tags, [[:albums_artists, [:b1, :b2], [:c1, :c2]], [:albums, [:d1, :d2], [:e1, :e2]], [:albums_tags, [:f1, :f2], [:g1, :g2]]], :right_primary_key=>[:h1, :h2], :left_primary_key=>[:id, :yyy], :conditions=>{:name=>'A'}
189
+ @c1.exclude(:tags=>@c2.load(:id=>1, :h1=>1234, :h2=>85)).sql.should == "SELECT * FROM artists WHERE (((artists.id, artists.yyy) NOT IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE ((albums_tags.g1 = 1234) AND (albums_tags.g2 = 85) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL)))) OR ((artists.id, artists.yyy) NOT IN (SELECT albums_artists.b1, albums_artists.b2 FROM tags INNER JOIN albums_tags ON ((albums_tags.g1 = tags.h1) AND (albums_tags.g2 = tags.h2)) INNER JOIN albums ON ((albums.e1 = albums_tags.f1) AND (albums.e2 = albums_tags.f2)) INNER JOIN albums_artists ON ((albums_artists.c1 = albums.d1) AND (albums_artists.c2 = albums.d2)) WHERE ((name = 'A') AND (tags.id = 1)))) OR (artists.id IS NULL) OR (artists.yyy IS NULL))"
190
+ end
191
+
167
192
  it "should allowing filtering by multiple many_through_many associations" do
168
193
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
169
194
  @c1.filter(:tags=>[@c2.load(:id=>1234), @c2.load(:id=>2345)]).sql.should == 'SELECT * FROM artists WHERE (artists.id IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id IN (1234, 2345)) AND (albums_artists.artist_id IS NOT NULL))))'
@@ -174,6 +199,16 @@ describe Sequel::Model, "many_through_many" do
174
199
  @c1.filter(:tags=>[@c2.load(:h1=>1234, :h2=>85), @c2.load(:h1=>2345, :h2=>95)]).sql.should == 'SELECT * FROM artists WHERE ((artists.id, artists.yyy) IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE (((albums_tags.g1, albums_tags.g2) IN ((1234, 85), (2345, 95))) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL))))'
175
200
  end
176
201
 
202
+ it "should allowing filtering by multiple many_through_many associations with :conditions" do
203
+ @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :conditions=>{:name=>'A'}
204
+ @c1.filter(:tags=>[@c2.load(:id=>1234), @c2.load(:id=>2345)]).sql.should == "SELECT * FROM artists WHERE ((artists.id IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id IN (1234, 2345)) AND (albums_artists.artist_id IS NOT NULL)))) AND (artists.id IN (SELECT albums_artists.artist_id FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON (albums_artists.album_id = albums.id) WHERE ((name = 'A') AND (tags.id IN (1234, 2345))))))"
205
+ end
206
+
207
+ it "should allowing filtering by multiple many_through_many associations with :conditions and composite keys" do
208
+ @c1.many_through_many :tags, [[:albums_artists, [:b1, :b2], [:c1, :c2]], [:albums, [:d1, :d2], [:e1, :e2]], [:albums_tags, [:f1, :f2], [:g1, :g2]]], :right_primary_key=>[:h1, :h2], :left_primary_key=>[:id, :yyy], :conditions=>{:name=>'A'}
209
+ @c1.filter(:tags=>[@c2.load(:id=>1, :h1=>1234, :h2=>85), @c2.load(:id=>2, :h1=>2345, :h2=>95)]).sql.should == "SELECT * FROM artists WHERE (((artists.id, artists.yyy) IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE (((albums_tags.g1, albums_tags.g2) IN ((1234, 85), (2345, 95))) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL)))) AND ((artists.id, artists.yyy) IN (SELECT albums_artists.b1, albums_artists.b2 FROM tags INNER JOIN albums_tags ON ((albums_tags.g1 = tags.h1) AND (albums_tags.g2 = tags.h2)) INNER JOIN albums ON ((albums.e1 = albums_tags.f1) AND (albums.e2 = albums_tags.f2)) INNER JOIN albums_artists ON ((albums_artists.c1 = albums.d1) AND (albums_artists.c2 = albums.d2)) WHERE ((name = 'A') AND (tags.id IN (1, 2))))))"
210
+ end
211
+
177
212
  it "should allowing excluding by multiple many_through_many associations" do
178
213
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
179
214
  @c1.exclude(:tags=>[@c2.load(:id=>1234), @c2.load(:id=>2345)]).sql.should == 'SELECT * FROM artists WHERE ((artists.id NOT IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id IN (1234, 2345)) AND (albums_artists.artist_id IS NOT NULL)))) OR (artists.id IS NULL))'
@@ -184,6 +219,16 @@ describe Sequel::Model, "many_through_many" do
184
219
  @c1.exclude(:tags=>[@c2.load(:h1=>1234, :h2=>85), @c2.load(:h1=>2345, :h2=>95)]).sql.should == 'SELECT * FROM artists WHERE (((artists.id, artists.yyy) NOT IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE (((albums_tags.g1, albums_tags.g2) IN ((1234, 85), (2345, 95))) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL)))) OR (artists.id IS NULL) OR (artists.yyy IS NULL))'
185
220
  end
186
221
 
222
+ it "should allowing excluding by multiple many_through_many associations with :conditions" do
223
+ @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :conditions=>{:name=>'A'}
224
+ @c1.exclude(:tags=>[@c2.load(:id=>1234), @c2.load(:id=>2345)]).sql.should == "SELECT * FROM artists WHERE ((artists.id NOT IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id IN (1234, 2345)) AND (albums_artists.artist_id IS NOT NULL)))) OR (artists.id NOT IN (SELECT albums_artists.artist_id FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON (albums_artists.album_id = albums.id) WHERE ((name = 'A') AND (tags.id IN (1234, 2345))))) OR (artists.id IS NULL))"
225
+ end
226
+
227
+ it "should allowing excluding by multiple many_through_many associations with :conditions and composite keys" do
228
+ @c1.many_through_many :tags, [[:albums_artists, [:b1, :b2], [:c1, :c2]], [:albums, [:d1, :d2], [:e1, :e2]], [:albums_tags, [:f1, :f2], [:g1, :g2]]], :right_primary_key=>[:h1, :h2], :left_primary_key=>[:id, :yyy], :conditions=>{:name=>'A'}
229
+ @c1.exclude(:tags=>[@c2.load(:id=>1, :h1=>1234, :h2=>85), @c2.load(:id=>2, :h1=>2345, :h2=>95)]).sql.should == "SELECT * FROM artists WHERE (((artists.id, artists.yyy) NOT IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE (((albums_tags.g1, albums_tags.g2) IN ((1234, 85), (2345, 95))) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL)))) OR ((artists.id, artists.yyy) NOT IN (SELECT albums_artists.b1, albums_artists.b2 FROM tags INNER JOIN albums_tags ON ((albums_tags.g1 = tags.h1) AND (albums_tags.g2 = tags.h2)) INNER JOIN albums ON ((albums.e1 = albums_tags.f1) AND (albums.e2 = albums_tags.f2)) INNER JOIN albums_artists ON ((albums_artists.c1 = albums.d1) AND (albums_artists.c2 = albums.d2)) WHERE ((name = 'A') AND (tags.id IN (1, 2))))) OR (artists.id IS NULL) OR (artists.yyy IS NULL))"
230
+ end
231
+
187
232
  it "should allowing filtering/excluding many_through_many associations with NULL values" do
188
233
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
189
234
  @c1.filter(:tags=>@c2.new).sql.should == 'SELECT * FROM artists WHERE \'f\''
@@ -200,6 +245,16 @@ describe Sequel::Model, "many_through_many" do
200
245
  @c1.filter(:tags=>@c2.filter(:x=>1)).sql.should == 'SELECT * FROM artists WHERE ((artists.id, artists.yyy) IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE (((albums_tags.g1, albums_tags.g2) IN (SELECT tags.h1, tags.h2 FROM tags WHERE ((x = 1) AND (tags.h1 IS NOT NULL) AND (tags.h2 IS NOT NULL)))) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL))))'
201
246
  end
202
247
 
248
+ it "should allowing filtering by many_through_many association datasets with :conditions" do
249
+ @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :conditions=>{:name=>'A'}
250
+ @c1.filter(:tags=>@c2.filter(:x=>1)).sql.should == "SELECT * FROM artists WHERE ((artists.id IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id IN (SELECT tags.id FROM tags WHERE ((x = 1) AND (tags.id IS NOT NULL)))) AND (albums_artists.artist_id IS NOT NULL)))) AND (artists.id IN (SELECT albums_artists.artist_id FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON (albums_artists.album_id = albums.id) WHERE ((name = 'A') AND (tags.id IN (SELECT tags.id FROM tags WHERE (x = 1)))))))"
251
+ end
252
+
253
+ it "should allowing filtering by many_through_many association datasets with :conditions and composite keys" do
254
+ @c1.many_through_many :tags, [[:albums_artists, [:b1, :b2], [:c1, :c2]], [:albums, [:d1, :d2], [:e1, :e2]], [:albums_tags, [:f1, :f2], [:g1, :g2]]], :right_primary_key=>[:h1, :h2], :left_primary_key=>[:id, :yyy], :conditions=>{:name=>'A'}
255
+ @c1.filter(:tags=>@c2.filter(:x=>1)).sql.should == "SELECT * FROM artists WHERE (((artists.id, artists.yyy) IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE (((albums_tags.g1, albums_tags.g2) IN (SELECT tags.h1, tags.h2 FROM tags WHERE ((x = 1) AND (tags.h1 IS NOT NULL) AND (tags.h2 IS NOT NULL)))) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL)))) AND ((artists.id, artists.yyy) IN (SELECT albums_artists.b1, albums_artists.b2 FROM tags INNER JOIN albums_tags ON ((albums_tags.g1 = tags.h1) AND (albums_tags.g2 = tags.h2)) INNER JOIN albums ON ((albums.e1 = albums_tags.f1) AND (albums.e2 = albums_tags.f2)) INNER JOIN albums_artists ON ((albums_artists.c1 = albums.d1) AND (albums_artists.c2 = albums.d2)) WHERE ((name = 'A') AND (tags.id IN (SELECT tags.id FROM tags WHERE (x = 1)))))))"
256
+ end
257
+
203
258
  it "should allowing excluding by many_through_many association datasets" do
204
259
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
205
260
  @c1.exclude(:tags=>@c2.filter(:x=>1)).sql.should == 'SELECT * FROM artists WHERE ((artists.id NOT IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id IN (SELECT tags.id FROM tags WHERE ((x = 1) AND (tags.id IS NOT NULL)))) AND (albums_artists.artist_id IS NOT NULL)))) OR (artists.id IS NULL))'
@@ -210,6 +265,16 @@ describe Sequel::Model, "many_through_many" do
210
265
  @c1.exclude(:tags=>@c2.filter(:x=>1)).sql.should == 'SELECT * FROM artists WHERE (((artists.id, artists.yyy) NOT IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE (((albums_tags.g1, albums_tags.g2) IN (SELECT tags.h1, tags.h2 FROM tags WHERE ((x = 1) AND (tags.h1 IS NOT NULL) AND (tags.h2 IS NOT NULL)))) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL)))) OR (artists.id IS NULL) OR (artists.yyy IS NULL))'
211
266
  end
212
267
 
268
+ it "should allowing excluding by many_through_many association datasets with :conditions" do
269
+ @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :conditions=>{:name=>'A'}
270
+ @c1.exclude(:tags=>@c2.filter(:x=>1)).sql.should == "SELECT * FROM artists WHERE ((artists.id NOT IN (SELECT albums_artists.artist_id FROM albums_artists INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) WHERE ((albums_tags.tag_id IN (SELECT tags.id FROM tags WHERE ((x = 1) AND (tags.id IS NOT NULL)))) AND (albums_artists.artist_id IS NOT NULL)))) OR (artists.id NOT IN (SELECT albums_artists.artist_id FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON (albums_artists.album_id = albums.id) WHERE ((name = 'A') AND (tags.id IN (SELECT tags.id FROM tags WHERE (x = 1)))))) OR (artists.id IS NULL))"
271
+ end
272
+
273
+ it "should allowing excluding by many_through_many association datasets with :conditions and composite keys" do
274
+ @c1.many_through_many :tags, [[:albums_artists, [:b1, :b2], [:c1, :c2]], [:albums, [:d1, :d2], [:e1, :e2]], [:albums_tags, [:f1, :f2], [:g1, :g2]]], :right_primary_key=>[:h1, :h2], :left_primary_key=>[:id, :yyy], :conditions=>{:name=>'A'}
275
+ @c1.exclude(:tags=>@c2.filter(:x=>1)).sql.should == "SELECT * FROM artists WHERE (((artists.id, artists.yyy) NOT IN (SELECT albums_artists.b1, albums_artists.b2 FROM albums_artists INNER JOIN albums ON ((albums.d1 = albums_artists.c1) AND (albums.d2 = albums_artists.c2)) INNER JOIN albums_tags ON ((albums_tags.f1 = albums.e1) AND (albums_tags.f2 = albums.e2)) WHERE (((albums_tags.g1, albums_tags.g2) IN (SELECT tags.h1, tags.h2 FROM tags WHERE ((x = 1) AND (tags.h1 IS NOT NULL) AND (tags.h2 IS NOT NULL)))) AND (albums_artists.b1 IS NOT NULL) AND (albums_artists.b2 IS NOT NULL)))) OR ((artists.id, artists.yyy) NOT IN (SELECT albums_artists.b1, albums_artists.b2 FROM tags INNER JOIN albums_tags ON ((albums_tags.g1 = tags.h1) AND (albums_tags.g2 = tags.h2)) INNER JOIN albums ON ((albums.e1 = albums_tags.f1) AND (albums.e2 = albums_tags.f2)) INNER JOIN albums_artists ON ((albums_artists.c1 = albums.d1) AND (albums_artists.c2 = albums.d2)) WHERE ((name = 'A') AND (tags.id IN (SELECT tags.id FROM tags WHERE (x = 1)))))) OR (artists.id IS NULL) OR (artists.yyy IS NULL))"
276
+ end
277
+
213
278
  it "should support a :conditions option" do
214
279
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :conditions=>{:a=>32}
215
280
  n = @c1.load(:id => 1234)
@@ -7,11 +7,13 @@ describe Sequel::Model, "pg_array_associations" do
7
7
  columns :id, :tag_ids
8
8
  plugin :pg_array_associations
9
9
  pg_array_to_many :tags
10
+ pg_array_to_many :a_tags, :clone=>:tags, :conditions=>{:name=>'A'}, :key=>:tag_ids
10
11
  end
11
12
  class ::Tag < Sequel::Model
12
13
  columns :id
13
14
  plugin :pg_array_associations
14
15
  many_to_pg_array :artists
16
+ many_to_pg_array :a_artists, :clone=>:artists, :conditions=>{:name=>'A'}
15
17
  def id3
16
18
  id*3
17
19
  end
@@ -66,21 +68,41 @@ describe Sequel::Model, "pg_array_associations" do
66
68
  @c2.filter(:artists=>@o1).sql.should == "SELECT * FROM tags WHERE (tags.id IN (1, 2, 3))"
67
69
  end
68
70
 
71
+ it "should allowing filtering by associations with :conditions" do
72
+ @c1.filter(:a_tags=>@o2).sql.should == "SELECT * FROM artists WHERE ((artists.tag_ids @> ARRAY[2]) AND coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE ((name = 'A') AND (tags.id IS NOT NULL) AND (tags.id = 2)))), 'f'))"
73
+ @c2.filter(:a_artists=>@o1).sql.should == "SELECT * FROM tags WHERE ((tags.id IN (1, 2, 3)) AND (tags.id IN (SELECT unnest(artists.tag_ids) FROM artists WHERE ((name = 'A') AND (artists.tag_ids IS NOT NULL) AND (artists.id = 1)))))"
74
+ end
75
+
69
76
  it "should allowing excluding by associations" do
70
77
  @c1.exclude(:tags=>@o2).sql.should == "SELECT * FROM artists WHERE (NOT (artists.tag_ids @> ARRAY[2]) OR (artists.tag_ids IS NULL))"
71
78
  @c2.exclude(:artists=>@o1).sql.should == "SELECT * FROM tags WHERE ((tags.id NOT IN (1, 2, 3)) OR (tags.id IS NULL))"
72
79
  end
73
80
 
81
+ it "should allowing excluding by associations with :conditions" do
82
+ @c1.exclude(:a_tags=>@o2).sql.should == "SELECT * FROM artists WHERE (NOT (artists.tag_ids @> ARRAY[2]) OR NOT coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE ((name = 'A') AND (tags.id IS NOT NULL) AND (tags.id = 2)))), 'f') OR (artists.tag_ids IS NULL))"
83
+ @c2.exclude(:a_artists=>@o1).sql.should == "SELECT * FROM tags WHERE ((tags.id NOT IN (1, 2, 3)) OR (tags.id NOT IN (SELECT unnest(artists.tag_ids) FROM artists WHERE ((name = 'A') AND (artists.tag_ids IS NOT NULL) AND (artists.id = 1)))) OR (tags.id IS NULL))"
84
+ end
85
+
74
86
  it "should allowing filtering by multiple associations" do
75
87
  @c1.filter(:tags=>[@c2.load(:id=>1), @c2.load(:id=>2)]).sql.should == "SELECT * FROM artists WHERE (artists.tag_ids && ARRAY[1,2])"
76
88
  @c2.filter(:artists=>[@c1.load(:tag_ids=>Sequel.pg_array([3, 4])), @c1.load(:tag_ids=>Sequel.pg_array([4, 5]))]).sql.should == "SELECT * FROM tags WHERE (tags.id IN (3, 4, 5))"
77
89
  end
78
90
 
91
+ it "should allowing filtering by multiple associations with :conditions" do
92
+ @c1.filter(:a_tags=>[@c2.load(:id=>1), @c2.load(:id=>2)]).sql.should == "SELECT * FROM artists WHERE ((artists.tag_ids && ARRAY[1,2]) AND coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE ((name = 'A') AND (tags.id IS NOT NULL) AND (tags.id IN (1, 2))))), 'f'))"
93
+ @c2.filter(:a_artists=>[@c1.load(:id=>7, :tag_ids=>Sequel.pg_array([3, 4])), @c1.load(:id=>8, :tag_ids=>Sequel.pg_array([4, 5]))]).sql.should == "SELECT * FROM tags WHERE ((tags.id IN (3, 4, 5)) AND (tags.id IN (SELECT unnest(artists.tag_ids) FROM artists WHERE ((name = 'A') AND (artists.tag_ids IS NOT NULL) AND (artists.id IN (7, 8))))))"
94
+ end
95
+
79
96
  it "should allowing excluding by multiple associations" do
80
97
  @c1.exclude(:tags=>[@c2.load(:id=>1), @c2.load(:id=>2)]).sql.should == "SELECT * FROM artists WHERE (NOT (artists.tag_ids && ARRAY[1,2]) OR (artists.tag_ids IS NULL))"
81
98
  @c2.exclude(:artists=>[@c1.load(:tag_ids=>Sequel.pg_array([3, 4])), @c1.load(:tag_ids=>Sequel.pg_array([4, 5]))]).sql.should == "SELECT * FROM tags WHERE ((tags.id NOT IN (3, 4, 5)) OR (tags.id IS NULL))"
82
99
  end
83
100
 
101
+ it "should allowing excluding by multiple associations with :conditions" do
102
+ @c1.exclude(:a_tags=>[@c2.load(:id=>1), @c2.load(:id=>2)]).sql.should == "SELECT * FROM artists WHERE (NOT (artists.tag_ids && ARRAY[1,2]) OR NOT coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE ((name = 'A') AND (tags.id IS NOT NULL) AND (tags.id IN (1, 2))))), 'f') OR (artists.tag_ids IS NULL))"
103
+ @c2.exclude(:a_artists=>[@c1.load(:id=>7, :tag_ids=>Sequel.pg_array([3, 4])), @c1.load(:id=>8, :tag_ids=>Sequel.pg_array([4, 5]))]).sql.should == "SELECT * FROM tags WHERE ((tags.id NOT IN (3, 4, 5)) OR (tags.id NOT IN (SELECT unnest(artists.tag_ids) FROM artists WHERE ((name = 'A') AND (artists.tag_ids IS NOT NULL) AND (artists.id IN (7, 8))))) OR (tags.id IS NULL))"
104
+ end
105
+
84
106
  it "should allowing filtering/excluding associations with NULL or empty values" do
85
107
  @c1.filter(:tags=>@c2.new).sql.should == 'SELECT * FROM artists WHERE \'f\''
86
108
  @c1.exclude(:tags=>@c2.new).sql.should == 'SELECT * FROM artists WHERE \'t\''
@@ -99,11 +121,21 @@ describe Sequel::Model, "pg_array_associations" do
99
121
  @c2.filter(:artists=>@c1.where(:id=>1)).sql.should == "SELECT * FROM tags WHERE (tags.id IN (SELECT unnest(artists.tag_ids) FROM artists WHERE (id = 1)))"
100
122
  end
101
123
 
124
+ it "should allowing filtering by association datasets with :conditions" do
125
+ @c1.filter(:a_tags=>@c2.where(:id=>1)).sql.should == "SELECT * FROM artists WHERE (coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE (id = 1))), 'f') AND coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE ((name = 'A') AND (tags.id IS NOT NULL) AND (tags.id IN (SELECT tags.id FROM tags WHERE (id = 1)))))), 'f'))"
126
+ @c2.filter(:a_artists=>@c1.where(:id=>1)).sql.should == "SELECT * FROM tags WHERE ((tags.id IN (SELECT unnest(artists.tag_ids) FROM artists WHERE (id = 1))) AND (tags.id IN (SELECT unnest(artists.tag_ids) FROM artists WHERE ((name = 'A') AND (artists.tag_ids IS NOT NULL) AND (artists.id IN (SELECT artists.id FROM artists WHERE (id = 1)))))))"
127
+ end
128
+
102
129
  it "should allowing excluding by association datasets" do
103
130
  @c1.exclude(:tags=>@c2.where(:id=>1)).sql.should == "SELECT * FROM artists WHERE (NOT coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE (id = 1))), 'f') OR (artists.tag_ids IS NULL))"
104
131
  @c2.exclude(:artists=>@c1.where(:id=>1)).sql.should == "SELECT * FROM tags WHERE ((tags.id NOT IN (SELECT unnest(artists.tag_ids) FROM artists WHERE (id = 1))) OR (tags.id IS NULL))"
105
132
  end
106
133
 
134
+ it "should allowing excluding by association datasets with :conditions" do
135
+ @c1.exclude(:a_tags=>@c2.where(:id=>1)).sql.should == "SELECT * FROM artists WHERE (NOT coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE (id = 1))), 'f') OR NOT coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE ((name = 'A') AND (tags.id IS NOT NULL) AND (tags.id IN (SELECT tags.id FROM tags WHERE (id = 1)))))), 'f') OR (artists.tag_ids IS NULL))"
136
+ @c2.exclude(:a_artists=>@c1.where(:id=>1)).sql.should == "SELECT * FROM tags WHERE ((tags.id NOT IN (SELECT unnest(artists.tag_ids) FROM artists WHERE (id = 1))) OR (tags.id NOT IN (SELECT unnest(artists.tag_ids) FROM artists WHERE ((name = 'A') AND (artists.tag_ids IS NOT NULL) AND (artists.id IN (SELECT artists.id FROM artists WHERE (id = 1)))))) OR (tags.id IS NULL))"
137
+ end
138
+
107
139
  it "filter by associations should respect key options" do
108
140
  @c1.class_eval{def tag3_ids; tag_ids.map{|x| x*3} end}
109
141
  @c1.pg_array_to_many :tags, :clone=>:tags, :primary_key=>Sequel.*(:id, 3), :primary_key_method=>:id3, :key=>:tag3_ids, :key_column=>Sequel.pg_array(:tag_ids)[1..2]
@@ -513,6 +545,18 @@ describe Sequel::Model, "pg_array_associations" do
513
545
  DB.sqls.should == ["UPDATE artists SET tag_ids = array_remove(tag_ids, 2) WHERE (tag_ids @> ARRAY[2])"]
514
546
  end
515
547
 
548
+ it "should allow calling add_ and remove_ methods on new objects for pg_array_to_many associations" do
549
+ a = Artist.new
550
+ a.add_tag(@c2.load(:id=>4))
551
+ a.tag_ids.should == [4]
552
+ a.remove_tag(@c2.load(:id=>4))
553
+ a.tag_ids.should == []
554
+ a.add_tag(@c2.load(:id=>4))
555
+ a.tag_ids.should == [4]
556
+ a.remove_all_tags
557
+ a.tag_ids.should == []
558
+ end
559
+
516
560
  it "should have pg_array_to_many association modification methods save if :save_after_modify option is used" do
517
561
  @c1.pg_array_to_many :tags, :clone=>:tags, :save_after_modify=>true
518
562