sequel 4.12.0 → 4.13.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +64 -0
- data/Rakefile +3 -1
- data/bin/sequel +13 -5
- data/doc/release_notes/4.13.0.txt +169 -0
- data/doc/sql.rdoc +3 -3
- data/lib/sequel/adapters/do.rb +11 -23
- data/lib/sequel/adapters/do/mysql.rb +8 -0
- data/lib/sequel/adapters/do/postgres.rb +8 -0
- data/lib/sequel/adapters/do/{sqlite.rb → sqlite3.rb} +9 -0
- data/lib/sequel/adapters/jdbc.rb +16 -139
- data/lib/sequel/adapters/jdbc/as400.rb +9 -0
- data/lib/sequel/adapters/jdbc/cubrid.rb +9 -0
- data/lib/sequel/adapters/jdbc/db2.rb +9 -0
- data/lib/sequel/adapters/jdbc/derby.rb +9 -0
- data/lib/sequel/adapters/jdbc/{firebird.rb → firebirdsql.rb} +9 -0
- data/lib/sequel/adapters/jdbc/h2.rb +10 -0
- data/lib/sequel/adapters/jdbc/hsqldb.rb +9 -0
- data/lib/sequel/adapters/jdbc/{informix.rb → informix-sqli.rb} +9 -0
- data/lib/sequel/adapters/jdbc/{progress.rb → jdbcprogress.rb} +9 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +10 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +14 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +9 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +9 -0
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +23 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +10 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +10 -0
- data/lib/sequel/adapters/odbc.rb +6 -14
- data/lib/sequel/adapters/odbc/db2.rb +9 -0
- data/lib/sequel/adapters/odbc/mssql.rb +8 -0
- data/lib/sequel/adapters/odbc/progress.rb +8 -0
- data/lib/sequel/adapters/oracle.rb +1 -1
- data/lib/sequel/adapters/postgres.rb +1 -1
- data/lib/sequel/adapters/shared/firebird.rb +8 -1
- data/lib/sequel/adapters/shared/mssql.rb +68 -27
- data/lib/sequel/adapters/shared/mysql.rb +3 -5
- data/lib/sequel/adapters/shared/oracle.rb +17 -3
- data/lib/sequel/adapters/shared/postgres.rb +9 -4
- data/lib/sequel/adapters/shared/sqlanywhere.rb +6 -6
- data/lib/sequel/database/connecting.rb +38 -17
- data/lib/sequel/dataset/actions.rb +6 -2
- data/lib/sequel/dataset/graph.rb +18 -20
- data/lib/sequel/dataset/misc.rb +37 -0
- data/lib/sequel/dataset/prepared_statements.rb +1 -2
- data/lib/sequel/dataset/query.rb +1 -0
- data/lib/sequel/dataset/sql.rb +17 -10
- data/lib/sequel/extensions/dataset_source_alias.rb +90 -0
- data/lib/sequel/extensions/pg_array.rb +14 -10
- data/lib/sequel/extensions/pg_enum.rb +135 -0
- data/lib/sequel/extensions/pg_hstore.rb +4 -6
- data/lib/sequel/extensions/pg_inet.rb +4 -5
- data/lib/sequel/extensions/pg_interval.rb +3 -3
- data/lib/sequel/extensions/pg_json.rb +16 -12
- data/lib/sequel/extensions/pg_range.rb +5 -3
- data/lib/sequel/extensions/pg_row.rb +2 -2
- data/lib/sequel/extensions/round_timestamps.rb +52 -0
- data/lib/sequel/model.rb +5 -2
- data/lib/sequel/model/associations.rb +29 -3
- data/lib/sequel/model/base.rb +68 -29
- data/lib/sequel/plugins/class_table_inheritance.rb +25 -16
- data/lib/sequel/plugins/column_select.rb +57 -0
- data/lib/sequel/plugins/composition.rb +14 -16
- data/lib/sequel/plugins/dirty.rb +9 -11
- data/lib/sequel/plugins/insert_returning_select.rb +70 -0
- data/lib/sequel/plugins/instance_filters.rb +7 -9
- data/lib/sequel/plugins/lazy_attributes.rb +16 -4
- data/lib/sequel/plugins/list.rb +9 -0
- data/lib/sequel/plugins/modification_detection.rb +90 -0
- data/lib/sequel/plugins/serialization.rb +13 -15
- data/lib/sequel/plugins/serialization_modification_detection.rb +9 -9
- data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
- data/lib/sequel/plugins/timestamps.rb +6 -6
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mysql_spec.rb +7 -0
- data/spec/adapters/postgres_spec.rb +41 -0
- data/spec/bin_spec.rb +4 -1
- data/spec/core/database_spec.rb +6 -0
- data/spec/core/dataset_spec.rb +100 -90
- data/spec/core/object_graph_spec.rb +5 -0
- data/spec/extensions/class_table_inheritance_spec.rb +18 -13
- data/spec/extensions/column_select_spec.rb +108 -0
- data/spec/extensions/composition_spec.rb +20 -0
- data/spec/extensions/dataset_source_alias_spec.rb +51 -0
- data/spec/extensions/insert_returning_select_spec.rb +46 -0
- data/spec/extensions/lazy_attributes_spec.rb +24 -20
- data/spec/extensions/list_spec.rb +5 -0
- data/spec/extensions/modification_detection_spec.rb +80 -0
- data/spec/extensions/pg_enum_spec.rb +64 -0
- data/spec/extensions/pg_json_spec.rb +7 -13
- data/spec/extensions/prepared_statements_spec.rb +6 -4
- data/spec/extensions/round_timestamps_spec.rb +43 -0
- data/spec/extensions/serialization_modification_detection_spec.rb +10 -1
- data/spec/extensions/serialization_spec.rb +18 -0
- data/spec/extensions/single_table_inheritance_spec.rb +5 -0
- data/spec/extensions/timestamps_spec.rb +6 -0
- data/spec/integration/plugin_test.rb +14 -8
- data/spec/integration/prepared_statement_test.rb +12 -0
- data/spec/model/associations_spec.rb +24 -0
- data/spec/model/model_spec.rb +13 -3
- data/spec/model/record_spec.rb +24 -1
- metadata +22 -6
data/lib/sequel/model/base.rb
CHANGED
@@ -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
|
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
|
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
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
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 =
|
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, [
|
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
|
data/lib/sequel/plugins/dirty.rb
CHANGED
@@ -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
|