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
@@ -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