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