sequel 4.12.0 → 4.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +64 -0
  3. data/Rakefile +3 -1
  4. data/bin/sequel +13 -5
  5. data/doc/release_notes/4.13.0.txt +169 -0
  6. data/doc/sql.rdoc +3 -3
  7. data/lib/sequel/adapters/do.rb +11 -23
  8. data/lib/sequel/adapters/do/mysql.rb +8 -0
  9. data/lib/sequel/adapters/do/postgres.rb +8 -0
  10. data/lib/sequel/adapters/do/{sqlite.rb → sqlite3.rb} +9 -0
  11. data/lib/sequel/adapters/jdbc.rb +16 -139
  12. data/lib/sequel/adapters/jdbc/as400.rb +9 -0
  13. data/lib/sequel/adapters/jdbc/cubrid.rb +9 -0
  14. data/lib/sequel/adapters/jdbc/db2.rb +9 -0
  15. data/lib/sequel/adapters/jdbc/derby.rb +9 -0
  16. data/lib/sequel/adapters/jdbc/{firebird.rb → firebirdsql.rb} +9 -0
  17. data/lib/sequel/adapters/jdbc/h2.rb +10 -0
  18. data/lib/sequel/adapters/jdbc/hsqldb.rb +9 -0
  19. data/lib/sequel/adapters/jdbc/{informix.rb → informix-sqli.rb} +9 -0
  20. data/lib/sequel/adapters/jdbc/{progress.rb → jdbcprogress.rb} +9 -0
  21. data/lib/sequel/adapters/jdbc/jtds.rb +10 -0
  22. data/lib/sequel/adapters/jdbc/mysql.rb +14 -0
  23. data/lib/sequel/adapters/jdbc/oracle.rb +9 -0
  24. data/lib/sequel/adapters/jdbc/postgresql.rb +9 -0
  25. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +23 -0
  26. data/lib/sequel/adapters/jdbc/sqlite.rb +10 -0
  27. data/lib/sequel/adapters/jdbc/sqlserver.rb +10 -0
  28. data/lib/sequel/adapters/odbc.rb +6 -14
  29. data/lib/sequel/adapters/odbc/db2.rb +9 -0
  30. data/lib/sequel/adapters/odbc/mssql.rb +8 -0
  31. data/lib/sequel/adapters/odbc/progress.rb +8 -0
  32. data/lib/sequel/adapters/oracle.rb +1 -1
  33. data/lib/sequel/adapters/postgres.rb +1 -1
  34. data/lib/sequel/adapters/shared/firebird.rb +8 -1
  35. data/lib/sequel/adapters/shared/mssql.rb +68 -27
  36. data/lib/sequel/adapters/shared/mysql.rb +3 -5
  37. data/lib/sequel/adapters/shared/oracle.rb +17 -3
  38. data/lib/sequel/adapters/shared/postgres.rb +9 -4
  39. data/lib/sequel/adapters/shared/sqlanywhere.rb +6 -6
  40. data/lib/sequel/database/connecting.rb +38 -17
  41. data/lib/sequel/dataset/actions.rb +6 -2
  42. data/lib/sequel/dataset/graph.rb +18 -20
  43. data/lib/sequel/dataset/misc.rb +37 -0
  44. data/lib/sequel/dataset/prepared_statements.rb +1 -2
  45. data/lib/sequel/dataset/query.rb +1 -0
  46. data/lib/sequel/dataset/sql.rb +17 -10
  47. data/lib/sequel/extensions/dataset_source_alias.rb +90 -0
  48. data/lib/sequel/extensions/pg_array.rb +14 -10
  49. data/lib/sequel/extensions/pg_enum.rb +135 -0
  50. data/lib/sequel/extensions/pg_hstore.rb +4 -6
  51. data/lib/sequel/extensions/pg_inet.rb +4 -5
  52. data/lib/sequel/extensions/pg_interval.rb +3 -3
  53. data/lib/sequel/extensions/pg_json.rb +16 -12
  54. data/lib/sequel/extensions/pg_range.rb +5 -3
  55. data/lib/sequel/extensions/pg_row.rb +2 -2
  56. data/lib/sequel/extensions/round_timestamps.rb +52 -0
  57. data/lib/sequel/model.rb +5 -2
  58. data/lib/sequel/model/associations.rb +29 -3
  59. data/lib/sequel/model/base.rb +68 -29
  60. data/lib/sequel/plugins/class_table_inheritance.rb +25 -16
  61. data/lib/sequel/plugins/column_select.rb +57 -0
  62. data/lib/sequel/plugins/composition.rb +14 -16
  63. data/lib/sequel/plugins/dirty.rb +9 -11
  64. data/lib/sequel/plugins/insert_returning_select.rb +70 -0
  65. data/lib/sequel/plugins/instance_filters.rb +7 -9
  66. data/lib/sequel/plugins/lazy_attributes.rb +16 -4
  67. data/lib/sequel/plugins/list.rb +9 -0
  68. data/lib/sequel/plugins/modification_detection.rb +90 -0
  69. data/lib/sequel/plugins/serialization.rb +13 -15
  70. data/lib/sequel/plugins/serialization_modification_detection.rb +9 -9
  71. data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
  72. data/lib/sequel/plugins/timestamps.rb +6 -6
  73. data/lib/sequel/version.rb +1 -1
  74. data/spec/adapters/mysql_spec.rb +7 -0
  75. data/spec/adapters/postgres_spec.rb +41 -0
  76. data/spec/bin_spec.rb +4 -1
  77. data/spec/core/database_spec.rb +6 -0
  78. data/spec/core/dataset_spec.rb +100 -90
  79. data/spec/core/object_graph_spec.rb +5 -0
  80. data/spec/extensions/class_table_inheritance_spec.rb +18 -13
  81. data/spec/extensions/column_select_spec.rb +108 -0
  82. data/spec/extensions/composition_spec.rb +20 -0
  83. data/spec/extensions/dataset_source_alias_spec.rb +51 -0
  84. data/spec/extensions/insert_returning_select_spec.rb +46 -0
  85. data/spec/extensions/lazy_attributes_spec.rb +24 -20
  86. data/spec/extensions/list_spec.rb +5 -0
  87. data/spec/extensions/modification_detection_spec.rb +80 -0
  88. data/spec/extensions/pg_enum_spec.rb +64 -0
  89. data/spec/extensions/pg_json_spec.rb +7 -13
  90. data/spec/extensions/prepared_statements_spec.rb +6 -4
  91. data/spec/extensions/round_timestamps_spec.rb +43 -0
  92. data/spec/extensions/serialization_modification_detection_spec.rb +10 -1
  93. data/spec/extensions/serialization_spec.rb +18 -0
  94. data/spec/extensions/single_table_inheritance_spec.rb +5 -0
  95. data/spec/extensions/timestamps_spec.rb +6 -0
  96. data/spec/integration/plugin_test.rb +14 -8
  97. data/spec/integration/prepared_statement_test.rb +12 -0
  98. data/spec/model/associations_spec.rb +24 -0
  99. data/spec/model/model_spec.rb +13 -3
  100. data/spec/model/record_spec.rb +24 -1
  101. metadata +22 -6
@@ -58,6 +58,11 @@ describe Sequel::Dataset, "graphing" do
58
58
  ds.sql.should == 'SELECT points.id, points.x AS y, lines.id AS lines_id, lines.x, lines.y AS lines_y, lines.graph_id FROM points LEFT OUTER JOIN lines ON (lines.x = points.id)'
59
59
  end
60
60
 
61
+ it "should requalify currently selected columns in new graph if current dataset joins tables" do
62
+ ds = @ds1.cross_join(:lines).select(:points__id, :lines__id___lid, :lines__x, :lines__y).graph(@ds3, :x=>:id)
63
+ ds.sql.should == 'SELECT points.id, points.lid, points.x, points.y, graphs.id AS graphs_id, graphs.name, graphs.x AS graphs_x, graphs.y AS graphs_y, graphs.lines_x FROM (SELECT points.id, lines.id AS lid, lines.x, lines.y FROM points CROSS JOIN lines) AS points LEFT OUTER JOIN graphs ON (graphs.x = points.id)'
64
+ end
65
+
61
66
  it "should raise error if currently selected expressions cannot be handled" do
62
67
  proc{@ds1.select(1).graph(@ds2, :x=>:id)}.should raise_error(Sequel::Error)
63
68
  end
@@ -48,8 +48,8 @@ describe "class_table_inheritance plugin" do
48
48
  Object.send(:remove_const, :Employee)
49
49
  end
50
50
 
51
- specify "should have simple_table = nil for subclasses" do
52
- Employee.simple_table.should == "employees"
51
+ specify "should have simple_table = nil for all classes" do
52
+ Employee.simple_table.should == nil
53
53
  Manager.simple_table.should == nil
54
54
  Executive.simple_table.should == nil
55
55
  Staff.simple_table.should == nil
@@ -62,10 +62,10 @@ describe "class_table_inheritance plugin" do
62
62
  end
63
63
 
64
64
  specify "should use a joined dataset in subclasses" do
65
- Employee.dataset.sql.should == 'SELECT * FROM employees'
66
- Manager.dataset.sql.should == 'SELECT * FROM employees INNER JOIN managers USING (id)'
67
- Executive.dataset.sql.should == 'SELECT * FROM employees INNER JOIN managers USING (id) INNER JOIN executives USING (id)'
68
- Staff.dataset.sql.should == 'SELECT * FROM employees INNER JOIN staff USING (id)'
65
+ Employee.dataset.sql.should == 'SELECT employees.id, employees.name, employees.kind FROM employees'
66
+ Manager.dataset.sql.should == 'SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id)'
67
+ Executive.dataset.sql.should == 'SELECT employees.id, employees.name, employees.kind, managers.num_staff, executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id)'
68
+ Staff.dataset.sql.should == 'SELECT employees.id, employees.name, employees.kind, staff.manager_id FROM employees INNER JOIN staff ON (staff.id = employees.id)'
69
69
  end
70
70
 
71
71
  it "should return rows with the correct class based on the polymorphic_key value" do
@@ -80,7 +80,7 @@ describe "class_table_inheritance plugin" do
80
80
 
81
81
  it "should return rows with the current class if cti_key is nil" do
82
82
  Employee.plugin(:class_table_inheritance)
83
- @ds._fetch = [{:kind=>'Employee'}, {:kind=>'Manager'}, {:kind=>'Executive'}, {:kind=>'Staff'}]
83
+ Employee.dataset._fetch = [{:kind=>'Employee'}, {:kind=>'Manager'}, {:kind=>'Executive'}, {:kind=>'Staff'}]
84
84
  Employee.all.collect{|x| x.class}.should == [Employee, Employee, Employee, Employee]
85
85
  end
86
86
 
@@ -147,22 +147,27 @@ describe "class_table_inheritance plugin" do
147
147
  o.valid?.should == true
148
148
  end
149
149
 
150
+ it "should set the type column field even when not validating" do
151
+ Employee.new.save(:validate=>false)
152
+ @db.sqls.should == ["INSERT INTO employees (kind) VALUES ('Employee')"]
153
+ end
154
+
150
155
  it "should raise an error if attempting to create an anonymous subclass" do
151
156
  proc{Class.new(Manager)}.should raise_error(Sequel::Error)
152
157
  end
153
158
 
154
159
  it "should allow specifying a map of names to tables to override implicit mapping" do
155
- Manager.dataset.sql.should == 'SELECT * FROM employees INNER JOIN managers USING (id)'
156
- Staff.dataset.sql.should == 'SELECT * FROM employees INNER JOIN staff USING (id)'
160
+ Manager.dataset.sql.should == 'SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id)'
161
+ Staff.dataset.sql.should == 'SELECT employees.id, employees.name, employees.kind, staff.manager_id FROM employees INNER JOIN staff ON (staff.id = employees.id)'
157
162
  end
158
163
 
159
164
  it "should lazily load attributes for columns in subclass tables" do
160
165
  Manager.instance_dataset._fetch = Manager.dataset._fetch = {:id=>1, :name=>'J', :kind=>'Executive', :num_staff=>2}
161
166
  m = Manager[1]
162
- @db.sqls.should == ['SELECT * FROM employees INNER JOIN managers USING (id) WHERE (id = 1) LIMIT 1']
167
+ @db.sqls.should == ['SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id) WHERE (managers.id = 1) LIMIT 1']
163
168
  Executive.instance_dataset._fetch = Executive.dataset._fetch = {:num_managers=>3}
164
169
  m.num_managers.should == 3
165
- @db.sqls.should == ['SELECT num_managers FROM employees INNER JOIN managers USING (id) INNER JOIN executives USING (id) WHERE (id = 1) LIMIT 1']
170
+ @db.sqls.should == ['SELECT executives.num_managers FROM employees INNER JOIN managers ON (managers.id = employees.id) INNER JOIN executives ON (executives.id = managers.id) WHERE (executives.id = 1) LIMIT 1']
166
171
  m.values.should == {:id=>1, :name=>'J', :kind=>'Executive', :num_staff=>2, :num_managers=>3}
167
172
  end
168
173
 
@@ -224,12 +229,12 @@ describe "class_table_inheritance plugin" do
224
229
  it "should handle many_to_one relationships correctly" do
225
230
  Manager.dataset._fetch = {:id=>3, :name=>'E', :kind=>'Executive', :num_managers=>3}
226
231
  Staff.load(:manager_id=>3).manager.should == Executive.load(:id=>3, :name=>'E', :kind=>'Executive', :num_managers=>3)
227
- @db.sqls.should == ['SELECT * FROM employees INNER JOIN managers USING (id) WHERE (id = 3) LIMIT 1']
232
+ @db.sqls.should == ['SELECT employees.id, employees.name, employees.kind, managers.num_staff FROM employees INNER JOIN managers ON (managers.id = employees.id) WHERE (managers.id = 3) LIMIT 1']
228
233
  end
229
234
 
230
235
  it "should handle one_to_many relationships correctly" do
231
236
  Staff.dataset._fetch = {:id=>1, :name=>'S', :kind=>'Staff', :manager_id=>3}
232
237
  Executive.load(:id=>3).staff_members.should == [Staff.load(:id=>1, :name=>'S', :kind=>'Staff', :manager_id=>3)]
233
- @db.sqls.should == ['SELECT * FROM employees INNER JOIN staff USING (id) WHERE (staff.manager_id = 3)']
238
+ @db.sqls.should == ['SELECT employees.id, employees.name, employees.kind, staff.manager_id FROM employees INNER JOIN staff ON (staff.id = employees.id) WHERE (staff.manager_id = 3)']
234
239
  end
235
240
  end
@@ -0,0 +1,108 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ describe "Sequel::Plugins::ColumnSelect" do
4
+ def set_cols(*cols)
5
+ @cols.replace(cols)
6
+ end
7
+
8
+ before do
9
+ cols = @cols = []
10
+ @db = Sequel.mock
11
+ @db.extend_datasets(Module.new{define_method(:columns){cols}})
12
+ set_cols :id, :a, :b, :c
13
+ @Album = Class.new(Sequel::Model(@db[:albums]))
14
+ end
15
+
16
+ it "should add a explicit column selections to existing dataset without explicit selection" do
17
+ @Album.plugin :column_select
18
+ @Album.dataset.sql.should == 'SELECT albums.id, albums.a, albums.b, albums.c FROM albums'
19
+
20
+ @Album.dataset = :albs
21
+ @Album.dataset.sql.should == 'SELECT albs.id, albs.a, albs.b, albs.c FROM albs'
22
+
23
+ @Album.dataset = Sequel.identifier(:albs)
24
+ @Album.dataset.sql.should == 'SELECT albs.id, albs.a, albs.b, albs.c FROM albs'
25
+ end
26
+
27
+ it "should handle qualified tables" do
28
+ @Album.dataset = :s__albums
29
+ @Album.plugin :column_select
30
+ @Album.dataset.sql.should == 'SELECT s.albums.id, s.albums.a, s.albums.b, s.albums.c FROM s.albums'
31
+
32
+ @Album.dataset = Sequel.qualify(:s2, :albums)
33
+ @Album.dataset.sql.should == 'SELECT s2.albums.id, s2.albums.a, s2.albums.b, s2.albums.c FROM s2.albums'
34
+ end
35
+
36
+ it "should handle aliases" do
37
+ @Album.dataset = :albums___a
38
+ @Album.plugin :column_select
39
+ @Album.dataset.sql.should == 'SELECT a.id, a.a, a.b, a.c FROM albums AS a'
40
+
41
+ @Album.dataset = Sequel.as(:albums, :b)
42
+ @Album.dataset.sql.should == 'SELECT b.id, b.a, b.b, b.c FROM albums AS b'
43
+
44
+ @Album.dataset = :s__albums___a
45
+ @Album.dataset.sql.should == 'SELECT a.id, a.a, a.b, a.c FROM s.albums AS a'
46
+
47
+ @Album.dataset = @Album.db[:albums].from_self
48
+ @Album.dataset.sql.should == 'SELECT t1.id, t1.a, t1.b, t1.c FROM (SELECT * FROM albums) AS t1'
49
+
50
+ @Album.dataset = Sequel.as(@Album.db[:albums], :b)
51
+ @Album.dataset.sql.should == 'SELECT b.id, b.a, b.b, b.c FROM (SELECT * FROM albums) AS b'
52
+ end
53
+
54
+ it "should not add a explicit column selection selection on existing dataset with explicit selection" do
55
+ @Album.dataset = @Album.dataset.select(:name)
56
+ @Album.plugin :column_select
57
+ @Album.dataset.sql.should == 'SELECT name FROM albums'
58
+
59
+ @Album.dataset = @Album.dataset.select(:name, :artist)
60
+ @Album.dataset.sql.should == 'SELECT name, artist FROM albums'
61
+ end
62
+
63
+ it "should not add a explicit column selection on existing dataset with multiple tables" do
64
+ @Album.dataset = @Album.db.from(:a1, :a2)
65
+ @Album.plugin :column_select
66
+ @Album.dataset.sql.should == 'SELECT * FROM a1, a2'
67
+
68
+ @Album.dataset = @Album.db.from(:a1).cross_join(:a2)
69
+ @Album.dataset.sql.should == 'SELECT * FROM a1 CROSS JOIN a2'
70
+ end
71
+
72
+ it "should use explicit column selection for many_to_many associations" do
73
+ @Album.plugin :column_select
74
+ @Album.many_to_many :albums, :class=>@Album, :left_key=>:l, :right_key=>:r, :join_table=>:j
75
+ @Album.load(:id=>1).albums_dataset.sql.should == 'SELECT albums.id, albums.a, albums.b, albums.c FROM albums INNER JOIN j ON (j.r = albums.id) WHERE (j.l = 1)'
76
+ end
77
+
78
+ it "should set not explicit column selection for many_to_many associations when overriding select" do
79
+ @Album.plugin :column_select
80
+ @Album.dataset = @Album.dataset.select(:a)
81
+ @Album.many_to_many :albums, :class=>@Album, :left_key=>:l, :right_key=>:r, :join_table=>:j
82
+ @Album.load(:id=>1).albums_dataset.sql.should == 'SELECT albums.* FROM albums INNER JOIN j ON (j.r = albums.id) WHERE (j.l = 1)'
83
+ end
84
+
85
+ it "should use the schema to get columns if available" do
86
+ def @db.supports_schema_parsing?() true end
87
+ def @db.schema(t, *)
88
+ [[:t, {}], [:d, {}]]
89
+ end
90
+ @Album.plugin :column_select
91
+ @Album.dataset.sql.should == 'SELECT albums.t, albums.d FROM albums'
92
+ end
93
+
94
+ it "should handle case where schema parsing does not produce results" do
95
+ def @db.supports_schema_parsing?() true end
96
+ def @db.schema_parse_table(t, *) [] end
97
+ @Album.plugin :column_select
98
+ @Album.dataset.sql.should == 'SELECT albums.id, albums.a, albums.b, albums.c FROM albums'
99
+ end
100
+
101
+ it "works correctly when loaded on model without a dataset" do
102
+ c = Class.new(Sequel::Model)
103
+ c.plugin :column_select
104
+ sc = Class.new(c)
105
+ sc.dataset = @db[:a]
106
+ sc.dataset.sql.should == "SELECT a.id, a.a, a.b, a.c FROM a"
107
+ end
108
+ end
@@ -27,6 +27,26 @@ describe "Composition plugin" do
27
27
  proc{@c.composition :date, :mapping=>[]}.should_not raise_error
28
28
  end
29
29
 
30
+ it "should handle validations of underlying columns" do
31
+ @c.composition :date, :mapping=>[:year, :month, :day]
32
+ o = @c.new
33
+ def o.validate
34
+ [:year, :month, :day].each{|c| errors.add(c, "not present") unless send(c)}
35
+ end
36
+ o.valid?.should == false
37
+ o.date = Date.new(1, 2, 3)
38
+ o.valid?.should == true
39
+ end
40
+
41
+ it "should set column values even when not validating" do
42
+ @c.composition :date, :mapping=>[:year, :month, :day]
43
+ @c.load({}).set(:date=>Date.new(4, 8, 12)).save(:validate=>false)
44
+ sql = DB.sqls.last
45
+ sql.should include("year = 4")
46
+ sql.should include("month = 8")
47
+ sql.should include("day = 12")
48
+ end
49
+
30
50
  it ".compositions should return the reflection hash of compositions" do
31
51
  @c.compositions.should == {}
32
52
  @c.composition :date, :mapping=>[:year, :month, :day]
@@ -0,0 +1,51 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ describe "dataset_source_alias extension" do
4
+ before do
5
+ @db = Sequel.mock
6
+ @db.extension(:dataset_source_alias)
7
+ end
8
+
9
+ it "should automatically alias datasets to their first source in #from" do
10
+ @db[@db[:a]].sql.should == 'SELECT * FROM (SELECT * FROM a) AS a'
11
+ @db[:a, @db[:b]].sql.should == 'SELECT * FROM a, (SELECT * FROM b) AS b'
12
+ end
13
+
14
+ it "should handle virtual row blocks in #from" do
15
+ @db.dataset.from{|_| @db[:a]}.sql.should == 'SELECT * FROM (SELECT * FROM a) AS a'
16
+ @db.dataset.from(:a){|_| @db[:b]}.sql.should == 'SELECT * FROM a, (SELECT * FROM b) AS b'
17
+ end
18
+
19
+ it "should automatically alias datasets to their first source in #join" do
20
+ @db[:a].cross_join(@db[:b]).sql.should == 'SELECT * FROM a CROSS JOIN (SELECT * FROM b) AS b'
21
+ end
22
+
23
+ it "should handle :table_alias option when joining" do
24
+ @db[:a].cross_join(@db[:b], :table_alias=>:c).sql.should == 'SELECT * FROM a CROSS JOIN (SELECT * FROM b) AS c'
25
+ end
26
+
27
+ it "should handle aliasing issues automatically" do
28
+ @db[:a, @db[:a]].sql.should == 'SELECT * FROM a, (SELECT * FROM a) AS a_0'
29
+ @db.dataset.from(:a, @db[:a]){|_| @db[:a]}.sql.should == 'SELECT * FROM a, (SELECT * FROM a) AS a_0, (SELECT * FROM a) AS a_1'
30
+ @db.dataset.from(:a, @db[:a]){|_| @db[:a]}.cross_join(@db[:a]).sql.should == 'SELECT * FROM a, (SELECT * FROM a) AS a_0, (SELECT * FROM a) AS a_1 CROSS JOIN (SELECT * FROM a) AS a_2'
31
+ end
32
+
33
+ it "should handle from_self" do
34
+ @db[:a].from_self.sql.should == 'SELECT * FROM (SELECT * FROM a) AS a'
35
+ @db[:a].from_self.from_self.sql.should == 'SELECT * FROM (SELECT * FROM (SELECT * FROM a) AS a) AS a'
36
+ end
37
+
38
+ it "should handle datasets without sources" do
39
+ @db[@db.select(1)].sql.should == 'SELECT * FROM (SELECT 1) AS t1'
40
+ @db[:t, @db.select(1)].sql.should == 'SELECT * FROM t, (SELECT 1) AS t1'
41
+ @db[:a].cross_join(@db.select(1)).sql.should == 'SELECT * FROM a CROSS JOIN (SELECT 1) AS t1'
42
+ end
43
+
44
+ it "should handle datasets selecting from functions" do
45
+ @db.dataset.from{|o| @db[o.f(:a)]}.sql.should == 'SELECT * FROM (SELECT * FROM f(a)) AS t1'
46
+ end
47
+
48
+ it "should handle datasets with literal SQL" do
49
+ @db.from(@db['SELECT c FROM d']).sql.should == 'SELECT * FROM (SELECT c FROM d) AS t1'
50
+ end
51
+ end
@@ -0,0 +1,46 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ describe "Sequel::Plugins::InsertReturningSelect" do
4
+ before do
5
+ @db = Sequel.mock(:fetch=>{:id=>1, :x=>2}, :autoid=>1)
6
+ @db.extend_datasets do
7
+ def supports_returning?(_) true end
8
+ def insert_select(*v) with_sql_first("#{insert_sql(*v)} RETURNING #{opts[:returning].map{|x| literal(x)}.join(', ')}") end
9
+ end
10
+ @Album = Class.new(Sequel::Model(@db[:albums].select(:id, :x)))
11
+ @Album.columns :id, :x
12
+ @db.sqls
13
+ end
14
+
15
+ it "should add a returning clause when inserting using selected columns" do
16
+ @Album.plugin :insert_returning_select
17
+ @Album.create(:x=>2).should == @Album.load(:id=>1, :x=>2)
18
+ @db.sqls.should == ['INSERT INTO albums (x) VALUES (2) RETURNING id, x']
19
+ end
20
+
21
+ it "should not add a returning clause if selection does not consist of just columns" do
22
+ @Album.dataset = @Album.dataset.select_append(Sequel.as(1, :b))
23
+ @Album.plugin :insert_returning_select
24
+ @db.sqls.clear
25
+ @Album.create(:x=>2).should == @Album.load(:id=>1, :x=>2)
26
+ @db.sqls.should == ['INSERT INTO albums (x) VALUES (2)', 'SELECT id, x, 1 AS b FROM albums WHERE (id = 1) LIMIT 1']
27
+ end
28
+
29
+ it "should not add a returning clause if database doesn't support it" do
30
+ @db.extend_datasets{def supports_returning?(_) false end}
31
+ @Album.plugin :insert_returning_select
32
+ @Album.create(:x=>2).should == @Album.load(:id=>1, :x=>2)
33
+ @db.sqls.should == ['INSERT INTO albums (x) VALUES (2)', 'SELECT id, x FROM albums WHERE (id = 1) LIMIT 1']
34
+ end
35
+
36
+ it "should work correctly with subclasses" do
37
+ c = Class.new(Sequel::Model)
38
+ c.plugin :insert_returning_select
39
+ b = Class.new(c)
40
+ b.columns :id, :x
41
+ b.dataset = @db[:albums].select(:id, :x)
42
+ @db.sqls.clear
43
+ b.create(:x=>2).should == b.load(:id=>1, :x=>2)
44
+ @db.sqls.should == ['INSERT INTO albums (x) VALUES (2) RETURNING id, x']
45
+ end
46
+ end
@@ -25,7 +25,7 @@ describe "Sequel::Plugins::LazyAttributes" do
25
25
  elsif sql =~ /id = (\d)/
26
26
  [$1]
27
27
  end.map do |x|
28
- if sql =~ /SELECT name FROM/
28
+ if sql =~ /SELECT (la.)?name FROM/
29
29
  {:name=>x.to_s}
30
30
  else
31
31
  {:id=>x.to_i, :name=>x.to_s}
@@ -46,7 +46,6 @@ describe "Sequel::Plugins::LazyAttributes" do
46
46
  @c.set_dataset(@ds.select(:id, :blah))
47
47
  @c.dataset.sql.should == 'SELECT id, blah FROM la'
48
48
  @c.plugin :lazy_attributes, :blah
49
- @c.dataset.opts[:select].should == [:id]
50
49
  @c.dataset.sql.should == 'SELECT id FROM la'
51
50
  end
52
51
 
@@ -54,13 +53,18 @@ describe "Sequel::Plugins::LazyAttributes" do
54
53
  @c.set_dataset(@ds.select(:id, :blah))
55
54
  @c.dataset.sql.should == 'SELECT id, blah FROM la'
56
55
  @c.lazy_attributes :blah
57
- @c.dataset.opts[:select].should == [:id]
58
56
  @c.dataset.sql.should == 'SELECT id FROM la'
59
57
  end
60
58
 
59
+ it "should handle lazy attributes that are qualified in the selection" do
60
+ @c.set_dataset(@ds.select(:la__id, :la__blah))
61
+ @c.dataset.sql.should == 'SELECT la.id, la.blah FROM la'
62
+ @c.plugin :lazy_attributes, :blah
63
+ @c.dataset.sql.should == 'SELECT la.id FROM la'
64
+ end
65
+
61
66
  it "should remove the attributes given from the SELECT columns of the model's dataset" do
62
- @ds.opts[:select].should == [:id]
63
- @ds.sql.should == 'SELECT id FROM la'
67
+ @ds.sql.should == 'SELECT la.id FROM la'
64
68
  end
65
69
 
66
70
  it "should still typecast correctly in lazy loaded column setters" do
@@ -80,16 +84,16 @@ describe "Sequel::Plugins::LazyAttributes" do
80
84
  m.values.should == {:id=>1}
81
85
  m.name.should == '1'
82
86
  m.values.should == {:id=>1, :name=>'1'}
83
- @db.sqls.should == ['SELECT id FROM la LIMIT 1', 'SELECT name FROM la WHERE (id = 1) LIMIT 1']
87
+ @db.sqls.should == ['SELECT la.id FROM la LIMIT 1', 'SELECT la.name FROM la WHERE (id = 1) LIMIT 1']
84
88
  end
85
89
 
86
90
  it "should lazily load the attribute for a frozen model object" do
87
91
  m = @c.first
88
92
  m.freeze
89
93
  m.name.should == '1'
90
- @db.sqls.should == ['SELECT id FROM la LIMIT 1', 'SELECT name FROM la WHERE (id = 1) LIMIT 1']
94
+ @db.sqls.should == ['SELECT la.id FROM la LIMIT 1', 'SELECT la.name FROM la WHERE (id = 1) LIMIT 1']
91
95
  m.name.should == '1'
92
- @db.sqls.should == ['SELECT name FROM la WHERE (id = 1) LIMIT 1']
96
+ @db.sqls.should == ['SELECT la.name FROM la WHERE (id = 1) LIMIT 1']
93
97
  end
94
98
 
95
99
  it "should not lazily load the attribute for a single model object if the value already exists" do
@@ -98,7 +102,7 @@ describe "Sequel::Plugins::LazyAttributes" do
98
102
  m[:name] = '1'
99
103
  m.name.should == '1'
100
104
  m.values.should == {:id=>1, :name=>'1'}
101
- @db.sqls.should == ['SELECT id FROM la LIMIT 1']
105
+ @db.sqls.should == ['SELECT la.id FROM la LIMIT 1']
102
106
  end
103
107
 
104
108
  it "should not lazily load the attribute for a single model object if it is a new record" do
@@ -114,16 +118,16 @@ describe "Sequel::Plugins::LazyAttributes" do
114
118
  ms.map{|m| m.name}.should == %w'1 2'
115
119
  ms.map{|m| m.values}.should == [{:id=>1, :name=>'1'}, {:id=>2, :name=>'2'}]
116
120
  sqls = @db.sqls
117
- ['SELECT id, name FROM la WHERE (id IN (1, 2))',
118
- 'SELECT id, name FROM la WHERE (id IN (2, 1))'].should include(sqls.pop)
119
- sqls.should == ['SELECT id FROM la']
121
+ ['SELECT la.id, la.name FROM la WHERE (la.id IN (1, 2))',
122
+ 'SELECT la.id, la.name FROM la WHERE (la.id IN (2, 1))'].should include(sqls.pop)
123
+ sqls.should == ['SELECT la.id FROM la']
120
124
  end
121
125
 
122
126
  it "should not eagerly load the attribute if model instance is frozen, and deal with other frozen instances if not frozen" do
123
127
  ms = @c.all
124
128
  ms.first.freeze
125
129
  ms.map{|m| m.name}.should == %w'1 2'
126
- @db.sqls.should == ['SELECT id FROM la', 'SELECT name FROM la WHERE (id = 1) LIMIT 1', 'SELECT id, name FROM la WHERE (id IN (2))']
130
+ @db.sqls.should == ['SELECT la.id FROM la', 'SELECT la.name FROM la WHERE (id = 1) LIMIT 1', 'SELECT la.id, la.name FROM la WHERE (la.id IN (2))']
127
131
  end
128
132
 
129
133
  it "should add the accessors to a module included in the class, so they can be easily overridden" do
@@ -137,9 +141,9 @@ describe "Sequel::Plugins::LazyAttributes" do
137
141
  ms.map{|m| m.name}.should == %w'1-blah 2-blah'
138
142
  ms.map{|m| m.values}.should == [{:id=>1, :name=>'1'}, {:id=>2, :name=>'2'}]
139
143
  sqls = @db.sqls
140
- ['SELECT id, name FROM la WHERE (id IN (1, 2))',
141
- 'SELECT id, name FROM la WHERE (id IN (2, 1))'].should include(sqls.pop)
142
- sqls.should == ['SELECT id FROM la']
144
+ ['SELECT la.id, la.name FROM la WHERE (la.id IN (1, 2))',
145
+ 'SELECT la.id, la.name FROM la WHERE (la.id IN (2, 1))'].should include(sqls.pop)
146
+ sqls.should == ['SELECT la.id FROM la']
143
147
  end
144
148
 
145
149
  it "should work with the serialization plugin" do
@@ -152,15 +156,15 @@ describe "Sequel::Plugins::LazyAttributes" do
152
156
  ms.map{|m| m.deserialized_values}.should == [{:name=>3}, {:name=>6}]
153
157
  ms.map{|m| m.name}.should == [3,6]
154
158
  sqls = @db.sqls
155
- ['SELECT id, name FROM la WHERE (id IN (1, 2))',
156
- 'SELECT id, name FROM la WHERE (id IN (2, 1))'].should include(sqls.pop)
157
- sqls.should == ['SELECT id FROM la']
159
+ ['SELECT la.id, la.name FROM la WHERE (la.id IN (1, 2))',
160
+ 'SELECT la.id, la.name FROM la WHERE (la.id IN (2, 1))'].should include(sqls.pop)
161
+ sqls.should == ['SELECT la.id FROM la']
158
162
  m = @ds.first
159
163
  m.values.should == {:id=>1}
160
164
  m.name.should == 3
161
165
  m.values.should == {:id=>1, :name=>"--- 3\n"}
162
166
  m.deserialized_values.should == {:name=>3}
163
167
  m.name.should == 3
164
- @db.sqls.should == ["SELECT id FROM la LIMIT 1", "SELECT name FROM la WHERE (id = 1) LIMIT 1"]
168
+ @db.sqls.should == ["SELECT la.id FROM la LIMIT 1", "SELECT la.name FROM la WHERE (id = 1) LIMIT 1"]
165
169
  end
166
170
  end