sequel 4.3.0 → 4.4.0

Sign up to get free protection for your applications and to get access to all the features.
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