sequel 4.12.0 → 4.13.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 (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
@@ -900,6 +900,14 @@ module Sequel
900
900
  schema_array = check_non_connection_error{db.schema(dataset, :reload=>reload)} if db.supports_schema_parsing?
901
901
  if schema_array
902
902
  schema_array.each{|k,v| schema_hash[k] = v}
903
+
904
+ # Set the primary key(s) based on the schema information,
905
+ # if the schema information includes primary key information
906
+ if schema_array.all?{|k,v| v.has_key?(:primary_key)}
907
+ pks = schema_array.collect{|k,v| k if v[:primary_key]}.compact
908
+ pks.length > 0 ? set_primary_key(pks) : no_primary_key
909
+ end
910
+
903
911
  if (select = ds_opts[:select]) && !(select.length == 1 && select.first.is_a?(SQL::ColumnAll))
904
912
  # We don't remove the columns from the schema_hash,
905
913
  # as it's possible they will be used for typecasting
@@ -913,12 +921,6 @@ module Sequel
913
921
  # returned by the schema.
914
922
  cols = schema_array.collect{|k,v| k}
915
923
  set_columns(cols)
916
- # Set the primary key(s) based on the schema information,
917
- # if the schema information includes primary key information
918
- if schema_array.all?{|k,v| v.has_key?(:primary_key)}
919
- pks = schema_array.collect{|k,v| k if v[:primary_key]}.compact
920
- pks.length > 0 ? set_primary_key(pks) : no_primary_key
921
- end
922
924
  # Also set the columns for the dataset, so the dataset
923
925
  # doesn't have to do a query to get them.
924
926
  dataset.instance_variable_set(:@columns, cols)
@@ -1036,6 +1038,8 @@ module Sequel
1036
1038
  ds.literal_append(sql, pk)
1037
1039
  ds.fetch_rows(sql){|r| return ds.row_proc.call(r)}
1038
1040
  nil
1041
+ elsif dataset.joined_dataset?
1042
+ first_where(qualified_primary_key_hash(pk))
1039
1043
  else
1040
1044
  first_where(primary_key_hash(pk))
1041
1045
  end
@@ -1147,7 +1151,7 @@ module Sequel
1147
1151
  changed_columns.clear
1148
1152
  yield self if block_given?
1149
1153
  end
1150
-
1154
+
1151
1155
  # Returns value of the column's attribute.
1152
1156
  #
1153
1157
  # Artist[1][:id] #=> 1
@@ -1216,14 +1220,6 @@ module Sequel
1216
1220
  @changed_columns ||= []
1217
1221
  end
1218
1222
 
1219
- # Similar to Model#dup, but copies frozen status to returned object
1220
- # if current object is frozen.
1221
- def clone
1222
- o = dup
1223
- o.freeze if frozen?
1224
- o
1225
- end
1226
-
1227
1223
  # Deletes and returns +self+. Does not run destroy hooks.
1228
1224
  # Look into using +destroy+ instead.
1229
1225
  #
@@ -1249,18 +1245,6 @@ module Sequel
1249
1245
  checked_save_failure(opts){checked_transaction(opts){_destroy(opts)}}
1250
1246
  end
1251
1247
 
1252
- # Produce a shallow copy of the object, similar to Object#dup.
1253
- def dup
1254
- s = self
1255
- super.instance_eval do
1256
- @values = s.values.dup
1257
- @changed_columns = s.changed_columns.dup
1258
- @errors = s.errors.dup
1259
- @this = s.this.dup if !new? && model.primary_key
1260
- self
1261
- end
1262
- end
1263
-
1264
1248
  # Iterates through all of the current values using each.
1265
1249
  #
1266
1250
  # Album[1].each{|k, v| puts "#{k} => #{v}"}
@@ -1511,6 +1495,7 @@ module Sequel
1511
1495
  def save(opts=OPTS)
1512
1496
  raise Sequel::Error, "can't save frozen object" if frozen?
1513
1497
  set_server(opts[:server]) if opts[:server]
1498
+ _before_validation
1514
1499
  if opts[:validate] != false
1515
1500
  unless checked_save_failure(opts){_valid?(true, opts)}
1516
1501
  raise(ValidationFailed.new(self)) if raise_on_failure?(opts)
@@ -1642,7 +1627,16 @@ module Sequel
1642
1627
  # Artist[1].this
1643
1628
  # # SELECT * FROM artists WHERE (id = 1) LIMIT 1
1644
1629
  def this
1645
- @this ||= use_server(model.instance_dataset.filter(pk_hash))
1630
+ return @this if @this
1631
+ raise Error, "No dataset for model #{model}" unless ds = model.instance_dataset
1632
+
1633
+ cond = if ds.joined_dataset?
1634
+ model.qualified_primary_key_hash(pk)
1635
+ else
1636
+ pk_hash
1637
+ end
1638
+
1639
+ @this = use_server(ds.where(cond))
1646
1640
  end
1647
1641
 
1648
1642
  # Runs #set with the passed hash and then runs save_changes.
@@ -1701,11 +1695,21 @@ module Sequel
1701
1695
  # artist(:name=>'Invalid').valid? # => false
1702
1696
  # artist.errors.full_messages # => ['name cannot be Invalid']
1703
1697
  def valid?(opts = OPTS)
1698
+ _before_validation
1704
1699
  _valid?(false, opts)
1705
1700
  end
1706
1701
 
1707
1702
  private
1708
1703
 
1704
+ # Run code before any validation is done, but also run it before saving
1705
+ # even if validation is skipped. This is a private hook. It exists so that
1706
+ # plugins can set values automatically before validation (as the values
1707
+ # need to be validated), but should be set even if validation is skipped.
1708
+ # Unlike the regular before_validation hook, we do not skip the save/validation
1709
+ # if this returns false.
1710
+ def _before_validation
1711
+ end
1712
+
1709
1713
  # Do the deletion of the object's dataset, and check that the row
1710
1714
  # was actually deleted.
1711
1715
  def _delete
@@ -1761,7 +1765,7 @@ module Sequel
1761
1765
  # the record should be refreshed from the database.
1762
1766
  def _insert
1763
1767
  ds = _insert_dataset
1764
- if !ds.opts[:select] and ds.supports_insert_select? and h = _insert_select_raw(ds)
1768
+ if _use_insert_select?(ds) && (h = _insert_select_raw(ds))
1765
1769
  _save_set_values(h)
1766
1770
  nil
1767
1771
  else
@@ -1924,6 +1928,11 @@ module Sequel
1924
1928
  _update_dataset.update(columns)
1925
1929
  end
1926
1930
 
1931
+ # Whether to use insert_select when inserting a new row.
1932
+ def _use_insert_select?(ds)
1933
+ (!ds.opts[:select] || ds.opts[:returning]) && ds.supports_insert_select?
1934
+ end
1935
+
1927
1936
  # Internal validation method. If +raise_errors+ is +true+, hook
1928
1937
  # failures will be raised as HookFailure exceptions. If it is
1929
1938
  # +false+, +false+ will be returned instead.
@@ -1990,6 +1999,36 @@ module Sequel
1990
1999
  Errors
1991
2000
  end
1992
2001
 
2002
+ if RUBY_VERSION >= '1.9'
2003
+ # Clone constructor -- freeze internal data structures if the original's
2004
+ # are frozen.
2005
+ def initialize_clone(other)
2006
+ super
2007
+ freeze if other.frozen?
2008
+ self
2009
+ end
2010
+ else
2011
+ # :nocov:
2012
+ # Ruby 1.8 doesn't support initialize_clone, so override clone to dup and freeze.
2013
+ def clone
2014
+ o = dup
2015
+ o.freeze if frozen?
2016
+ o
2017
+ end
2018
+ public :clone
2019
+ # :nocov:
2020
+ end
2021
+
2022
+ # Copy constructor -- Duplicate internal data structures.
2023
+ def initialize_copy(other)
2024
+ super
2025
+ @values = @values.dup
2026
+ @changed_columns = @changed_columns.dup if @changed_columns
2027
+ @errors = @errors.dup if @errors
2028
+ @this = @this.dup if @this
2029
+ self
2030
+ end
2031
+
1993
2032
  # Set the columns with the given hash. By default, the same as +set+, but
1994
2033
  # exists so it can be overridden. This is called only for new records, before
1995
2034
  # changed_columns is cleared.
@@ -31,12 +31,20 @@ module Sequel
31
31
  # When using the class_table_inheritance plugin, subclasses use joined
32
32
  # datasets:
33
33
  #
34
- # Employee.dataset.sql # SELECT * FROM employees
35
- # Manager.dataset.sql # SELECT * FROM employees
36
- # # INNER JOIN managers USING (id)
37
- # Executive.dataset.sql # SELECT * FROM employees
38
- # # INNER JOIN managers USING (id)
39
- # # INNER JOIN executives USING (id)
34
+ # Employee.dataset.sql
35
+ # # SELECT employees.id, employees.name, employees.kind
36
+ # # FROM employees
37
+ #
38
+ # Manager.dataset.sql
39
+ # # SELECT employees.id, employees.name, employees.kind, managers.num_staff
40
+ # # FROM employees
41
+ # # JOIN managers ON (managers.id = employees.id)
42
+ #
43
+ # Executive.dataset.sql
44
+ # # SELECT employees.id, employees.name, employees.kind, managers.num_staff, executives.num_managers
45
+ # # FROM employees
46
+ # # JOIN managers ON (managers.id = employees.id)
47
+ # # JOIN executives ON (executives.id = managers.id)
40
48
  #
41
49
  # This allows Executive.all to return instances with all attributes
42
50
  # loaded. The plugin overrides the deleting, inserting, and updating
@@ -99,12 +107,13 @@ module Sequel
99
107
  def self.configure(model, opts=OPTS)
100
108
  model.instance_eval do
101
109
  @cti_base_model = self
102
- @cti_key = key = opts[:key]
110
+ @cti_key = opts[:key]
103
111
  @cti_tables = [table_name]
104
112
  @cti_columns = {table_name=>columns}
105
113
  @cti_table_map = opts[:table_map] || {}
106
114
  @cti_model_map = opts[:model_map]
107
115
  set_dataset_cti_row_proc
116
+ set_dataset(dataset.select(*columns.map{|c| Sequel.qualify(table_name, Sequel.identifier(c))}))
108
117
  end
109
118
  end
110
119
 
@@ -163,7 +172,7 @@ module Sequel
163
172
  # Need to set dataset and columns before calling super so that
164
173
  # the main column accessor module is included in the class before any
165
174
  # plugin accessor modules (such as the lazy attributes accessor module).
166
- set_dataset(ds.join(table, [pk]))
175
+ set_dataset(ds.join(table, pk=>pk).select_append(*(columns - [primary_key]).map{|c| Sequel.qualify(table, Sequel.identifier(c))}))
167
176
  set_columns(self.columns)
168
177
  end
169
178
  super
@@ -221,14 +230,6 @@ module Sequel
221
230
  end
222
231
 
223
232
  module InstanceMethods
224
- # Set the cti_key column to the name of the model.
225
- def before_validation
226
- if new? && model.cti_key && !model.cti_model_map
227
- send("#{model.cti_key}=", model.name.to_s)
228
- end
229
- super
230
- end
231
-
232
233
  # Delete the row from all backing tables, starting from the
233
234
  # most recent table and going through all superclasses.
234
235
  def delete
@@ -242,6 +243,14 @@ module Sequel
242
243
 
243
244
  private
244
245
 
246
+ # Set the cti_key column to the name of the model.
247
+ def _before_validation
248
+ if new? && model.cti_key && !model.cti_model_map
249
+ send("#{model.cti_key}=", model.name.to_s)
250
+ end
251
+ super
252
+ end
253
+
245
254
  # Insert rows into all backing tables, using the columns
246
255
  # in each table.
247
256
  def _insert
@@ -0,0 +1,57 @@
1
+ module Sequel
2
+ module Plugins
3
+ # The column_select plugin changes the default selection for a
4
+ # model dataset to explicit select all columns from the table:
5
+ # <tt>table.column1, table.column2, table.column3, ...</tt>.
6
+ # This makes it simpler to add columns to the model's table
7
+ # in a migration concurrently while running the application,
8
+ # without it affecting the operation of the application.
9
+ #
10
+ # Note that by default on databases that supporting RETURNING,
11
+ # using explicit column selections will cause instance creations
12
+ # to use two queries (insert and refresh) instead of a single
13
+ # query using RETURNING. You can use the insert_returning_select
14
+ # plugin to automatically use RETURNING for instance creations
15
+ # for models where the column_select plugin automatically sets up
16
+ # an explicit column selection.
17
+ #
18
+ # Usage:
19
+ #
20
+ # # Make all model subclasses explicitly select qualified columns
21
+ # Sequel::Model.plugin :column_select
22
+ #
23
+ # # Make the Album class select qualified columns
24
+ # Album.plugin :column_select
25
+ module ColumnSelect
26
+ # Modify the current model's dataset selection, if the model
27
+ # has a dataset.
28
+ def self.configure(model)
29
+ model.instance_eval do
30
+ self.dataset = dataset if @dataset
31
+ end
32
+ end
33
+
34
+ module ClassMethods
35
+ private
36
+
37
+ # If the underlying dataset selects from a single table and
38
+ # has no explicit selection, explicitly select all columns from that table,
39
+ # qualifying them with table's name.
40
+ def convert_input_dataset(ds)
41
+ ds = super
42
+ if !ds.opts[:select] && (from = ds.opts[:from]) && from.length == 1 && !ds.opts[:join]
43
+ if db.supports_schema_parsing?
44
+ cols = check_non_connection_error{db.schema(ds)}
45
+ if cols
46
+ cols = cols.map{|c, _| c}
47
+ end
48
+ end
49
+ cols ||= check_non_connection_error{ds.columns}
50
+ ds = ds.select(*cols.map{|c| Sequel.qualify(ds.first_source, Sequel.identifier(c))})
51
+ end
52
+ ds
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -149,27 +149,11 @@ module Sequel
149
149
  end
150
150
 
151
151
  module InstanceMethods
152
- # For each composition, set the columns in the model class based
153
- # on the composition object.
154
- def before_save
155
- @compositions.keys.each{|n| instance_eval(&model.compositions[n][:decomposer])} if @compositions
156
- super
157
- end
158
-
159
152
  # Cache of composition objects for this class.
160
153
  def compositions
161
154
  @compositions ||= {}
162
155
  end
163
156
 
164
- # Duplicate compositions hash when duplicating model instance.
165
- def dup
166
- s = self
167
- super.instance_eval do
168
- @compositions = s.compositions.dup
169
- self
170
- end
171
- end
172
-
173
157
  # Freeze compositions hash when freezing model instance.
174
158
  def freeze
175
159
  compositions.freeze
@@ -178,11 +162,25 @@ module Sequel
178
162
 
179
163
  private
180
164
 
165
+ # For each composition, set the columns in the model class based
166
+ # on the composition object.
167
+ def _before_validation
168
+ @compositions.keys.each{|n| instance_eval(&model.compositions[n][:decomposer])} if @compositions
169
+ super
170
+ end
171
+
181
172
  # Clear the cached compositions when manually refreshing.
182
173
  def _refresh_set_values(hash)
183
174
  @compositions.clear if @compositions
184
175
  super
185
176
  end
177
+
178
+ # Duplicate compositions hash when duplicating model instance.
179
+ def initialize_copy(other)
180
+ super
181
+ @compositions = other.compositions.dup
182
+ self
183
+ end
186
184
  end
187
185
  end
188
186
  end
@@ -85,17 +85,6 @@ module Sequel
85
85
  initial_values.has_key?(column)
86
86
  end
87
87
 
88
- # Duplicate internal data structures
89
- def dup
90
- s = self
91
- super.instance_eval do
92
- @initial_values = s.initial_values.dup
93
- @missing_initial_values = s.send(:missing_initial_values).dup
94
- @previous_changes = s.previous_changes.dup if s.previous_changes
95
- self
96
- end
97
- end
98
-
99
88
  # Freeze internal data structures
100
89
  def freeze
101
90
  initial_values.freeze
@@ -209,6 +198,15 @@ module Sequel
209
198
  end
210
199
  end
211
200
 
201
+ # Duplicate internal data structures
202
+ def initialize_copy(other)
203
+ super
204
+ @initial_values = other.initial_values.dup
205
+ @missing_initial_values = other.send(:missing_initial_values).dup
206
+ @previous_changes = other.previous_changes.dup if other.previous_changes
207
+ self
208
+ end
209
+
212
210
  # Reset the initial values when initializing.
213
211
  def initialize_set(h)
214
212
  super
@@ -0,0 +1,70 @@
1
+ module Sequel
2
+ module Plugins
3
+ # If the model's dataset selects explicit columns and the
4
+ # database supports it, the insert_returning_select plugin will
5
+ # automatically set the RETURNING clause on the dataset used to
6
+ # insert rows to the columns selected, which allows the default model
7
+ # support to run the insert and refresh of the data in a single
8
+ # query, instead of two separate queries. This is Sequel's default
9
+ # behavior when the model does not select explicit columns.
10
+ #
11
+ # Usage:
12
+ #
13
+ # # Make all model subclasses automatically setup insert returning clauses
14
+ # Sequel::Model.plugin :insert_returning_select
15
+ #
16
+ # # Make the Album class automatically setup insert returning clauses
17
+ # Album.plugin :insert_returning_select
18
+ module InsertReturningSelect
19
+ # Modify the current model's dataset selection, if the model
20
+ # has a dataset.
21
+ def self.configure(model)
22
+ model.instance_eval do
23
+ self.dataset = dataset if @dataset && @dataset.opts[:select]
24
+ end
25
+ end
26
+
27
+ module ClassMethods
28
+ # The dataset to use to insert new rows. For internal use only.
29
+ attr_reader :instance_insert_dataset
30
+
31
+ private
32
+
33
+ # When reseting the instance dataset, also reset the instance_insert_dataset.
34
+ def reset_instance_dataset
35
+ ret = super
36
+ ds = @instance_dataset
37
+
38
+ if columns = insert_returning_columns(ds)
39
+ ds = ds.returning(*columns)
40
+ end
41
+ @instance_insert_dataset = ds
42
+
43
+ ret
44
+ end
45
+
46
+ # Determine the columns to use for the returning clause, or return nil
47
+ # if they can't be determined and a returning clause should not be
48
+ # added automatically.
49
+ def insert_returning_columns(ds)
50
+ return unless ds.supports_returning?(:insert)
51
+ return unless values = ds.opts[:select]
52
+
53
+ values = values.map{|v| ds.unqualified_column_for(v)}
54
+ if values.all?
55
+ values
56
+ end
57
+ end
58
+ end
59
+
60
+ module InstanceMethods
61
+ private
62
+
63
+ # Use the instance_insert_dataset as the base dataset for the insert.
64
+ def _insert_dataset
65
+ use_server(model.instance_insert_dataset)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end