sequel 5.39.0 → 5.72.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +408 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +59 -27
- data/bin/sequel +11 -3
- data/doc/advanced_associations.rdoc +16 -14
- data/doc/association_basics.rdoc +119 -24
- data/doc/cheat_sheet.rdoc +11 -3
- data/doc/mass_assignment.rdoc +1 -1
- data/doc/migration.rdoc +13 -6
- data/doc/model_hooks.rdoc +1 -1
- data/doc/object_model.rdoc +8 -8
- data/doc/opening_databases.rdoc +26 -12
- data/doc/postgresql.rdoc +16 -8
- data/doc/querying.rdoc +5 -3
- data/doc/release_notes/5.40.0.txt +40 -0
- data/doc/release_notes/5.41.0.txt +25 -0
- data/doc/release_notes/5.42.0.txt +136 -0
- data/doc/release_notes/5.43.0.txt +98 -0
- data/doc/release_notes/5.44.0.txt +32 -0
- data/doc/release_notes/5.45.0.txt +34 -0
- data/doc/release_notes/5.46.0.txt +87 -0
- data/doc/release_notes/5.47.0.txt +59 -0
- data/doc/release_notes/5.48.0.txt +14 -0
- data/doc/release_notes/5.49.0.txt +59 -0
- data/doc/release_notes/5.50.0.txt +78 -0
- data/doc/release_notes/5.51.0.txt +47 -0
- data/doc/release_notes/5.52.0.txt +87 -0
- data/doc/release_notes/5.53.0.txt +23 -0
- data/doc/release_notes/5.54.0.txt +27 -0
- data/doc/release_notes/5.55.0.txt +21 -0
- data/doc/release_notes/5.56.0.txt +51 -0
- data/doc/release_notes/5.57.0.txt +23 -0
- data/doc/release_notes/5.58.0.txt +31 -0
- data/doc/release_notes/5.59.0.txt +73 -0
- data/doc/release_notes/5.60.0.txt +22 -0
- data/doc/release_notes/5.61.0.txt +43 -0
- data/doc/release_notes/5.62.0.txt +132 -0
- data/doc/release_notes/5.63.0.txt +33 -0
- data/doc/release_notes/5.64.0.txt +50 -0
- data/doc/release_notes/5.65.0.txt +21 -0
- data/doc/release_notes/5.66.0.txt +24 -0
- data/doc/release_notes/5.67.0.txt +32 -0
- data/doc/release_notes/5.68.0.txt +61 -0
- data/doc/release_notes/5.69.0.txt +26 -0
- data/doc/release_notes/5.70.0.txt +35 -0
- data/doc/release_notes/5.71.0.txt +21 -0
- data/doc/release_notes/5.72.0.txt +33 -0
- data/doc/schema_modification.rdoc +1 -1
- data/doc/security.rdoc +9 -9
- data/doc/sharding.rdoc +3 -1
- data/doc/sql.rdoc +28 -16
- data/doc/testing.rdoc +22 -11
- data/doc/transactions.rdoc +6 -6
- data/doc/virtual_rows.rdoc +2 -2
- data/lib/sequel/adapters/ado/access.rb +1 -1
- data/lib/sequel/adapters/ado.rb +17 -17
- data/lib/sequel/adapters/amalgalite.rb +3 -5
- data/lib/sequel/adapters/ibmdb.rb +2 -2
- data/lib/sequel/adapters/jdbc/derby.rb +8 -0
- data/lib/sequel/adapters/jdbc/h2.rb +60 -10
- data/lib/sequel/adapters/jdbc/hsqldb.rb +6 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +7 -4
- data/lib/sequel/adapters/jdbc.rb +16 -18
- data/lib/sequel/adapters/mysql.rb +92 -67
- data/lib/sequel/adapters/mysql2.rb +54 -49
- data/lib/sequel/adapters/odbc.rb +6 -2
- data/lib/sequel/adapters/oracle.rb +4 -3
- data/lib/sequel/adapters/postgres.rb +83 -40
- data/lib/sequel/adapters/shared/access.rb +11 -1
- data/lib/sequel/adapters/shared/db2.rb +30 -0
- data/lib/sequel/adapters/shared/mssql.rb +90 -9
- data/lib/sequel/adapters/shared/mysql.rb +47 -2
- data/lib/sequel/adapters/shared/oracle.rb +82 -1
- data/lib/sequel/adapters/shared/postgres.rb +496 -178
- data/lib/sequel/adapters/shared/sqlanywhere.rb +11 -1
- data/lib/sequel/adapters/shared/sqlite.rb +116 -11
- data/lib/sequel/adapters/sqlanywhere.rb +1 -1
- data/lib/sequel/adapters/sqlite.rb +60 -18
- data/lib/sequel/adapters/tinytds.rb +1 -1
- data/lib/sequel/adapters/trilogy.rb +117 -0
- data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
- data/lib/sequel/ast_transformer.rb +6 -0
- data/lib/sequel/connection_pool/sharded_single.rb +5 -7
- data/lib/sequel/connection_pool/sharded_threaded.rb +16 -11
- data/lib/sequel/connection_pool/sharded_timed_queue.rb +374 -0
- data/lib/sequel/connection_pool/single.rb +6 -8
- data/lib/sequel/connection_pool/threaded.rb +14 -8
- data/lib/sequel/connection_pool/timed_queue.rb +270 -0
- data/lib/sequel/connection_pool.rb +55 -31
- data/lib/sequel/core.rb +28 -18
- data/lib/sequel/database/connecting.rb +27 -3
- data/lib/sequel/database/dataset.rb +16 -6
- data/lib/sequel/database/misc.rb +69 -14
- data/lib/sequel/database/query.rb +73 -2
- data/lib/sequel/database/schema_generator.rb +46 -53
- data/lib/sequel/database/schema_methods.rb +18 -2
- data/lib/sequel/dataset/actions.rb +108 -14
- data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
- data/lib/sequel/dataset/features.rb +20 -0
- data/lib/sequel/dataset/misc.rb +12 -2
- data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
- data/lib/sequel/dataset/prepared_statements.rb +2 -0
- data/lib/sequel/dataset/query.rb +171 -44
- data/lib/sequel/dataset/sql.rb +182 -47
- data/lib/sequel/dataset.rb +4 -0
- data/lib/sequel/extensions/_model_pg_row.rb +0 -12
- data/lib/sequel/extensions/_pretty_table.rb +1 -1
- data/lib/sequel/extensions/any_not_empty.rb +1 -1
- data/lib/sequel/extensions/async_thread_pool.rb +439 -0
- data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
- data/lib/sequel/extensions/blank.rb +8 -0
- data/lib/sequel/extensions/connection_expiration.rb +15 -9
- data/lib/sequel/extensions/connection_validator.rb +16 -11
- data/lib/sequel/extensions/constraint_validations.rb +1 -1
- data/lib/sequel/extensions/core_refinements.rb +36 -11
- data/lib/sequel/extensions/date_arithmetic.rb +71 -31
- data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
- data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
- data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -1
- data/lib/sequel/extensions/eval_inspect.rb +2 -0
- data/lib/sequel/extensions/index_caching.rb +5 -1
- data/lib/sequel/extensions/inflector.rb +9 -1
- data/lib/sequel/extensions/is_distinct_from.rb +141 -0
- data/lib/sequel/extensions/looser_typecasting.rb +3 -0
- data/lib/sequel/extensions/migration.rb +11 -2
- data/lib/sequel/extensions/named_timezones.rb +26 -6
- data/lib/sequel/extensions/pagination.rb +1 -1
- data/lib/sequel/extensions/pg_array.rb +32 -4
- data/lib/sequel/extensions/pg_array_ops.rb +2 -2
- data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
- data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +110 -0
- data/lib/sequel/extensions/pg_enum.rb +2 -3
- data/lib/sequel/extensions/pg_extended_date_support.rb +38 -27
- data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
- data/lib/sequel/extensions/pg_hstore.rb +6 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
- data/lib/sequel/extensions/pg_inet.rb +10 -11
- data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
- data/lib/sequel/extensions/pg_interval.rb +45 -19
- data/lib/sequel/extensions/pg_json.rb +13 -15
- data/lib/sequel/extensions/pg_json_ops.rb +73 -2
- data/lib/sequel/extensions/pg_loose_count.rb +3 -1
- data/lib/sequel/extensions/pg_multirange.rb +367 -0
- data/lib/sequel/extensions/pg_range.rb +11 -24
- data/lib/sequel/extensions/pg_range_ops.rb +37 -9
- data/lib/sequel/extensions/pg_row.rb +21 -19
- data/lib/sequel/extensions/pg_row_ops.rb +1 -1
- data/lib/sequel/extensions/query.rb +2 -0
- data/lib/sequel/extensions/s.rb +2 -1
- data/lib/sequel/extensions/schema_caching.rb +1 -1
- data/lib/sequel/extensions/schema_dumper.rb +45 -11
- data/lib/sequel/extensions/server_block.rb +10 -13
- data/lib/sequel/extensions/set_literalizer.rb +58 -0
- data/lib/sequel/extensions/sql_comments.rb +110 -3
- data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
- data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
- data/lib/sequel/extensions/string_agg.rb +1 -1
- data/lib/sequel/extensions/string_date_time.rb +19 -23
- data/lib/sequel/extensions/symbol_aref.rb +2 -0
- data/lib/sequel/model/associations.rb +345 -101
- data/lib/sequel/model/base.rb +51 -27
- data/lib/sequel/model/dataset_module.rb +3 -0
- data/lib/sequel/model/errors.rb +10 -1
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/model/plugins.rb +5 -0
- data/lib/sequel/plugins/association_proxies.rb +2 -0
- data/lib/sequel/plugins/async_thread_pool.rb +39 -0
- data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
- data/lib/sequel/plugins/auto_validations.rb +87 -15
- data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
- data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
- data/lib/sequel/plugins/column_encryption.rb +728 -0
- data/lib/sequel/plugins/composition.rb +10 -4
- data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
- data/lib/sequel/plugins/constraint_validations.rb +10 -6
- data/lib/sequel/plugins/dataset_associations.rb +4 -1
- data/lib/sequel/plugins/defaults_setter.rb +16 -0
- data/lib/sequel/plugins/dirty.rb +1 -1
- data/lib/sequel/plugins/enum.rb +124 -0
- data/lib/sequel/plugins/finder.rb +4 -2
- data/lib/sequel/plugins/insert_conflict.rb +4 -0
- data/lib/sequel/plugins/instance_specific_default.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +39 -24
- data/lib/sequel/plugins/lazy_attributes.rb +3 -0
- data/lib/sequel/plugins/list.rb +3 -1
- data/lib/sequel/plugins/many_through_many.rb +109 -10
- data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -38
- data/lib/sequel/plugins/nested_attributes.rb +12 -7
- data/lib/sequel/plugins/optimistic_locking.rb +9 -42
- data/lib/sequel/plugins/optimistic_locking_base.rb +55 -0
- data/lib/sequel/plugins/pg_array_associations.rb +56 -38
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +11 -3
- data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
- data/lib/sequel/plugins/prepared_statements.rb +12 -2
- data/lib/sequel/plugins/prepared_statements_safe.rb +2 -1
- data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
- data/lib/sequel/plugins/rcte_tree.rb +27 -19
- data/lib/sequel/plugins/require_valid_schema.rb +67 -0
- data/lib/sequel/plugins/serialization.rb +9 -3
- data/lib/sequel/plugins/serialization_modification_detection.rb +2 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
- data/lib/sequel/plugins/sql_comments.rb +189 -0
- data/lib/sequel/plugins/static_cache.rb +39 -1
- data/lib/sequel/plugins/static_cache_cache.rb +5 -1
- data/lib/sequel/plugins/subclasses.rb +28 -11
- data/lib/sequel/plugins/tactical_eager_loading.rb +23 -10
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/unused_associations.rb +521 -0
- data/lib/sequel/plugins/update_or_create.rb +1 -1
- data/lib/sequel/plugins/validate_associated.rb +22 -12
- data/lib/sequel/plugins/validation_helpers.rb +46 -12
- data/lib/sequel/plugins/validation_helpers_generic_type_messages.rb +73 -0
- data/lib/sequel/plugins/xml_serializer.rb +1 -1
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/timezones.rb +12 -14
- data/lib/sequel/version.rb +1 -1
- metadata +132 -38
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
module Plugins
|
5
|
+
# This plugin implements optimistic locking mechanism on PostgreSQL based
|
6
|
+
# on the xmin of the row. The xmin system column is automatically set to
|
7
|
+
# the current transaction id whenever the row is inserted or updated:
|
8
|
+
#
|
9
|
+
# class Person < Sequel::Model
|
10
|
+
# plugin :pg_xmin_optimistic_locking
|
11
|
+
# end
|
12
|
+
# p1 = Person[1]
|
13
|
+
# p2 = Person[1]
|
14
|
+
# p1.update(name: 'Jim') # works
|
15
|
+
# p2.update(name: 'Bob') # raises Sequel::NoExistingObject
|
16
|
+
#
|
17
|
+
# The advantage of pg_xmin_optimistic_locking plugin compared to the
|
18
|
+
# regular optimistic_locking plugin as that it does not require any
|
19
|
+
# additional columns setup on the model. This allows it to be loaded
|
20
|
+
# in the base model and have all subclasses automatically use
|
21
|
+
# optimistic locking. The disadvantage is that testing can be
|
22
|
+
# more difficult if you are modifying the underlying row between
|
23
|
+
# when a model is retrieved and when it is saved.
|
24
|
+
#
|
25
|
+
# This plugin may not work with the class_table_inheritance plugin.
|
26
|
+
#
|
27
|
+
# This plugin relies on the instance_filters plugin.
|
28
|
+
module PgXminOptimisticLocking
|
29
|
+
WILDCARD = LiteralString.new('*').freeze
|
30
|
+
|
31
|
+
# Define the xmin column accessor
|
32
|
+
def self.apply(model)
|
33
|
+
model.instance_exec do
|
34
|
+
plugin(:optimistic_locking_base)
|
35
|
+
@lock_column = :xmin
|
36
|
+
def_column_accessor(:xmin)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Update the dataset to append the xmin column if it is usable
|
41
|
+
# and there is a dataset for the model.
|
42
|
+
def self.configure(model)
|
43
|
+
model.instance_exec do
|
44
|
+
set_dataset(@dataset) if @dataset
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
module ClassMethods
|
49
|
+
private
|
50
|
+
|
51
|
+
# Ensure the dataset selects the xmin column if doing so
|
52
|
+
def convert_input_dataset(ds)
|
53
|
+
append_xmin_column_if_usable(super)
|
54
|
+
end
|
55
|
+
|
56
|
+
# If the xmin column is not already selected, and selecting it does not
|
57
|
+
# raise an error, append it to the selections.
|
58
|
+
def append_xmin_column_if_usable(ds)
|
59
|
+
select = ds.opts[:select]
|
60
|
+
|
61
|
+
unless select && select.include?(:xmin)
|
62
|
+
xmin_ds = ds.select_append(:xmin)
|
63
|
+
begin
|
64
|
+
columns = xmin_ds.columns!
|
65
|
+
rescue Sequel::DatabaseConnectionError, Sequel::DatabaseDisconnectError
|
66
|
+
raise
|
67
|
+
rescue Sequel::DatabaseError
|
68
|
+
# ignore, could be view, subquery, table returning function, etc.
|
69
|
+
else
|
70
|
+
ds = xmin_ds if columns.include?(:xmin)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
ds
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
module InstanceMethods
|
79
|
+
private
|
80
|
+
|
81
|
+
# Only set the lock column instance filter if there is an xmin value.
|
82
|
+
def lock_column_instance_filter
|
83
|
+
super if @values[:xmin]
|
84
|
+
end
|
85
|
+
|
86
|
+
# Include xmin value when inserting initial row
|
87
|
+
def _insert_dataset
|
88
|
+
super.returning(WILDCARD, :xmin)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Remove the xmin from the columns to update.
|
92
|
+
# PostgreSQL automatically updates the xmin value, and it cannot be assigned.
|
93
|
+
def _save_update_all_columns_hash
|
94
|
+
v = super
|
95
|
+
v.delete(:xmin)
|
96
|
+
v
|
97
|
+
end
|
98
|
+
|
99
|
+
# Add an RETURNING clause to fetch the updated xmin when updating the row.
|
100
|
+
def _update_without_checking(columns)
|
101
|
+
ds = _update_dataset
|
102
|
+
rows = ds.clone(ds.send(:default_server_opts, :sql=>ds.returning(:xmin).update_sql(columns))).all
|
103
|
+
values[:xmin] = rows.first[:xmin] unless rows.empty?
|
104
|
+
rows.length
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -3,7 +3,8 @@
|
|
3
3
|
module Sequel
|
4
4
|
module Plugins
|
5
5
|
# The prepared_statements plugin modifies the model to use prepared statements for
|
6
|
-
# instance level inserts and updates.
|
6
|
+
# instance level inserts and updates. This plugin exists for backwards compatibility
|
7
|
+
# and is not recommended for general use.
|
7
8
|
#
|
8
9
|
# Note that this plugin is unsafe in some circumstances, as it can allow up to
|
9
10
|
# 2^N prepared statements to be created for each type of insert and update query, where
|
@@ -169,8 +170,17 @@ module Sequel
|
|
169
170
|
end
|
170
171
|
|
171
172
|
case type
|
172
|
-
when :insert, :
|
173
|
+
when :insert, :update
|
173
174
|
true
|
175
|
+
when :insert_select
|
176
|
+
# SQLite RETURNING support has a bug that doesn't allow for committing transactions
|
177
|
+
# when a prepared statement with RETURNING has been used on the connection:
|
178
|
+
#
|
179
|
+
# SQLite3::BusyException: cannot commit transaction - SQL statements in progress: COMMIT
|
180
|
+
#
|
181
|
+
# Disabling usage of prepared statements for insert_select on SQLite seems to be the
|
182
|
+
# simplest way to workaround the problem.
|
183
|
+
db.database_type != :sqlite
|
174
184
|
# :nocov:
|
175
185
|
when :delete, :refresh
|
176
186
|
Sequel::Deprecation.deprecate("The :delete and :refresh prepared statement types", "There should be no need to check if these types are supported")
|
@@ -5,7 +5,8 @@ module Sequel
|
|
5
5
|
# The prepared_statements_safe plugin modifies the model to reduce the number of
|
6
6
|
# prepared statements that can be created, by setting as many columns as possible
|
7
7
|
# before creating, and by changing +save_changes+ to save all columns instead of
|
8
|
-
# just the changed ones.
|
8
|
+
# just the changed ones. This plugin exists for backwards compatibility
|
9
|
+
# and is not recommended for general use.
|
9
10
|
#
|
10
11
|
# This plugin depends on the +prepared_statements+ plugin.
|
11
12
|
#
|
@@ -0,0 +1,154 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
module Plugins
|
5
|
+
# The primary_key_lookup_check_values plugin typecasts given primary key
|
6
|
+
# values before performing a lookup by primary key. If the given primary
|
7
|
+
# key value cannot be typecasted correctly, the lookup returns nil
|
8
|
+
# without issuing a query. If the schema for the primary key column
|
9
|
+
# includes minimum and maximum values, this also checks the given value
|
10
|
+
# is not outside the range. If the given value is outside the allowed
|
11
|
+
# range, the lookup returns nil without issuing a query.
|
12
|
+
#
|
13
|
+
# This affects the following Model methods:
|
14
|
+
#
|
15
|
+
# * Model.[] (when called with non-Hash)
|
16
|
+
# * Model.with_pk
|
17
|
+
# * Model.with_pk!
|
18
|
+
#
|
19
|
+
# It also affects the following Model dataset methods:
|
20
|
+
#
|
21
|
+
# * Dataset#[] (when called with Integer)
|
22
|
+
# * Dataset#with_pk
|
23
|
+
# * dataset#with_pk!
|
24
|
+
#
|
25
|
+
# Note that this can break working code. The above methods accept
|
26
|
+
# any filter condition by default, not just primary key values. The
|
27
|
+
# plugin will handle Symbol, Sequel::SQL::Expression, and
|
28
|
+
# Sequel::LiteralString objects, but code such as the following will break:
|
29
|
+
#
|
30
|
+
# # Return first Album where primary key is one of the given values
|
31
|
+
# Album.dataset.with_pk([1, 2, 3])
|
32
|
+
#
|
33
|
+
# Usage:
|
34
|
+
#
|
35
|
+
# # Make all model subclasses support checking primary key values before
|
36
|
+
# # lookup # (called before loading subclasses)
|
37
|
+
# Sequel::Model.plugin :primary_key_lookup_check_values
|
38
|
+
#
|
39
|
+
# # Make the Album class support checking primary key values before lookup
|
40
|
+
# Album.plugin :primary_key_lookup_check_values
|
41
|
+
module PrimaryKeyLookupCheckValues
|
42
|
+
def self.configure(model)
|
43
|
+
model.instance_exec do
|
44
|
+
setup_primary_key_lookup_check_values if @dataset
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
module ClassMethods
|
49
|
+
Plugins.after_set_dataset(self, :setup_primary_key_lookup_check_values)
|
50
|
+
|
51
|
+
Plugins.inherited_instance_variables(self,
|
52
|
+
:@primary_key_type=>nil,
|
53
|
+
:@primary_key_value_range=>nil)
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
# Check the given primary key value. Typecast it to the appropriate
|
58
|
+
# database type if the database type is known. If it cannot be
|
59
|
+
# typecasted, or the typecasted value is outside the range of column
|
60
|
+
# values, return nil.
|
61
|
+
def _check_pk_lookup_value(pk)
|
62
|
+
return if nil == pk
|
63
|
+
case pk
|
64
|
+
when SQL::Expression, LiteralString, Symbol
|
65
|
+
return pk
|
66
|
+
end
|
67
|
+
return pk unless pk_type = @primary_key_type
|
68
|
+
|
69
|
+
if pk_type.is_a?(Array)
|
70
|
+
return unless pk.is_a?(Array)
|
71
|
+
return unless pk.size == pk_type.size
|
72
|
+
return if pk.any?(&:nil?)
|
73
|
+
|
74
|
+
pk_value_range = @primary_key_value_range
|
75
|
+
i = 0
|
76
|
+
pk.map do |v|
|
77
|
+
if type = pk_type[i]
|
78
|
+
v = _typecast_pk_lookup_value(v, type)
|
79
|
+
return if nil == v
|
80
|
+
if pk_value_range
|
81
|
+
min, max = pk_value_range[i]
|
82
|
+
return if min && v < min
|
83
|
+
return if max && v > max
|
84
|
+
end
|
85
|
+
end
|
86
|
+
i += 1
|
87
|
+
v
|
88
|
+
end
|
89
|
+
elsif pk.is_a?(Array)
|
90
|
+
return
|
91
|
+
elsif nil != (pk = _typecast_pk_lookup_value(pk, pk_type))
|
92
|
+
min, max = @primary_key_value_range
|
93
|
+
return if min && pk < min
|
94
|
+
return if max && pk > max
|
95
|
+
pk
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Typecast the value to the appropriate type,
|
100
|
+
# returning nil if it cannot be typecasted.
|
101
|
+
def _typecast_pk_lookup_value(value, type)
|
102
|
+
db.typecast_value(type, value)
|
103
|
+
rescue InvalidValue
|
104
|
+
nil
|
105
|
+
end
|
106
|
+
|
107
|
+
# Skip the primary key lookup if the typecasted and checked
|
108
|
+
# primary key value is nil.
|
109
|
+
def primary_key_lookup(pk)
|
110
|
+
unless nil == (pk = _check_pk_lookup_value(pk))
|
111
|
+
super
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Setup the primary key type and value range used for checking
|
116
|
+
# primary key values during lookup.
|
117
|
+
def setup_primary_key_lookup_check_values
|
118
|
+
if primary_key.is_a?(Array)
|
119
|
+
types = []
|
120
|
+
value_ranges = []
|
121
|
+
primary_key.each do |pk|
|
122
|
+
type, min, max = _type_min_max_values_for_column(pk)
|
123
|
+
types << type
|
124
|
+
value_ranges << ([min, max].freeze if min || max)
|
125
|
+
end
|
126
|
+
@primary_key_type = (types.freeze if types.any?)
|
127
|
+
@primary_key_value_range = (value_ranges.freeze if @primary_key_type && value_ranges.any?)
|
128
|
+
else
|
129
|
+
@primary_key_type, min, max = _type_min_max_values_for_column(primary_key)
|
130
|
+
@primary_key_value_range = ([min, max].freeze if @primary_key_type && (min || max))
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Return the type, min_value, and max_value schema entries
|
135
|
+
# for the column, if they exist.
|
136
|
+
def _type_min_max_values_for_column(column)
|
137
|
+
if schema = db_schema[column]
|
138
|
+
schema.values_at(:type, :min_value, :max_value)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
module DatasetMethods
|
144
|
+
# Skip the primary key lookup if the typecasted and checked
|
145
|
+
# primary key value is nil.
|
146
|
+
def with_pk(pk)
|
147
|
+
unless nil == (pk = model.send(:_check_pk_lookup_value, pk))
|
148
|
+
super
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
@@ -170,11 +170,13 @@ module Sequel
|
|
170
170
|
id_map = eo[:id_map]
|
171
171
|
parent_map = {}
|
172
172
|
children_map = {}
|
173
|
-
eo[:
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
173
|
+
Sequel.synchronize_with(eo[:mutex]) do
|
174
|
+
eo[:rows].each do |obj|
|
175
|
+
parent_map[prkey_conv[obj]] = obj
|
176
|
+
(children_map[key_conv[obj]] ||= []) << obj
|
177
|
+
obj.associations[ancestors] = []
|
178
|
+
obj.associations[parent] = nil
|
179
|
+
end
|
178
180
|
end
|
179
181
|
r = model.association_reflection(ancestors)
|
180
182
|
base_case = model.where(prkey=>id_map.keys).
|
@@ -207,10 +209,12 @@ module Sequel
|
|
207
209
|
root.associations[ancestors] << obj
|
208
210
|
end
|
209
211
|
end
|
210
|
-
|
211
|
-
|
212
|
-
children
|
213
|
-
|
212
|
+
Sequel.synchronize_with(eo[:mutex]) do
|
213
|
+
parent_map.each do |parent_id, obj|
|
214
|
+
if children = children_map[parent_id]
|
215
|
+
children.each do |child|
|
216
|
+
child.associations[parent] = obj
|
217
|
+
end
|
214
218
|
end
|
215
219
|
end
|
216
220
|
end
|
@@ -268,10 +272,12 @@ module Sequel
|
|
268
272
|
associations = eo[:associations]
|
269
273
|
parent_map = {}
|
270
274
|
children_map = {}
|
271
|
-
eo[:
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
+
Sequel.synchronize_with(eo[:mutex]) do
|
276
|
+
eo[:rows].each do |obj|
|
277
|
+
parent_map[prkey_conv[obj]] = obj
|
278
|
+
obj.associations[descendants] = []
|
279
|
+
obj.associations[childrena] = []
|
280
|
+
end
|
275
281
|
end
|
276
282
|
r = model.association_reflection(descendants)
|
277
283
|
base_case = model.where(key=>id_map.keys).
|
@@ -316,12 +322,14 @@ module Sequel
|
|
316
322
|
|
317
323
|
(children_map[key_conv[obj]] ||= []) << obj
|
318
324
|
end
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
+
Sequel.synchronize_with(eo[:mutex]) do
|
326
|
+
children_map.each do |parent_id, objs|
|
327
|
+
objs = objs.uniq
|
328
|
+
parent_obj = parent_map[parent_id]
|
329
|
+
parent_obj.associations[childrena] = objs
|
330
|
+
objs.each do |obj|
|
331
|
+
obj.associations[parent] = parent_obj
|
332
|
+
end
|
325
333
|
end
|
326
334
|
end
|
327
335
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
module Plugins
|
5
|
+
# The require_valid_schema plugin makes Sequel raise or warn if attempting
|
6
|
+
# to set the dataset of a model class to a simple table, where the database
|
7
|
+
# supports schema parsing, but schema parsing does not work for the model's
|
8
|
+
# table.
|
9
|
+
#
|
10
|
+
# The plugin's default behavior requires that all models that select from a
|
11
|
+
# single identifier have a valid table schema, if the database supports
|
12
|
+
# schema parsing. If the schema cannot be determined for such
|
13
|
+
# a model, an error is raised:
|
14
|
+
#
|
15
|
+
# Sequel::Model.plugin :require_valid_schema
|
16
|
+
#
|
17
|
+
# If you load the plugin with an argument of :warn, Sequel will warn instead
|
18
|
+
# of raising for such tables:
|
19
|
+
#
|
20
|
+
# Sequel::Model.plugin :require_valid_schema, :warn
|
21
|
+
#
|
22
|
+
# This can catch bugs where you expect models to have valid schema, but
|
23
|
+
# they do not. This setting only affects future attempts to set datasets
|
24
|
+
# in the current class and subclasses created in the future.
|
25
|
+
#
|
26
|
+
# If you load the plugin with an argument of false, it will not require valid schema.
|
27
|
+
# This can be used in subclasses where you do not want to require valid schema,
|
28
|
+
# but the plugin must be loaded before a dataset with invalid schema is set:
|
29
|
+
#
|
30
|
+
# Sequel::Model.plugin :require_valid_schema
|
31
|
+
# InvalidSchemaAllowed = Class.new(Sequel::Model)
|
32
|
+
# InvalidSchemaAllowed.plugin :require_valid_schema, false
|
33
|
+
# class MyModel < InvalidSchemaAllowed
|
34
|
+
# end
|
35
|
+
module RequireValidSchema
|
36
|
+
# Modify the current model's dataset selection, if the model
|
37
|
+
# has a dataset.
|
38
|
+
def self.configure(model, setting=true)
|
39
|
+
model.instance_variable_set(:@require_valid_schema, setting)
|
40
|
+
end
|
41
|
+
|
42
|
+
module ClassMethods
|
43
|
+
Plugins.inherited_instance_variables(self, :@require_valid_schema=>nil)
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# If the schema cannot be determined, the model uses a simple table,
|
48
|
+
# require_valid_schema is set, and the database supports schema parsing, raise or
|
49
|
+
# warn based on the require_valid_schema setting.
|
50
|
+
def get_db_schema_array(reload)
|
51
|
+
schema_array = super
|
52
|
+
|
53
|
+
if !schema_array && simple_table && @require_valid_schema
|
54
|
+
message = "Not able to parse schema for model: #{inspect}, table: #{simple_table}"
|
55
|
+
if @require_valid_schema == :warn
|
56
|
+
warn message
|
57
|
+
else
|
58
|
+
raise Error, message
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
schema_array
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -127,7 +127,7 @@ module Sequel
|
|
127
127
|
def serialize_attributes(format, *columns)
|
128
128
|
if format.is_a?(Symbol)
|
129
129
|
unless format = Sequel.synchronize{REGISTERED_FORMATS[format]}
|
130
|
-
raise(Error, "Unsupported serialization format: #{format} (valid formats: #{Sequel.synchronize{REGISTERED_FORMATS.keys}.
|
130
|
+
raise(Error, "Unsupported serialization format: #{format} (valid formats: #{Sequel.synchronize{REGISTERED_FORMATS.keys}.inspect})")
|
131
131
|
end
|
132
132
|
end
|
133
133
|
serializer, deserializer = format
|
@@ -154,7 +154,10 @@ module Sequel
|
|
154
154
|
deserialized_values[column] = deserialize_value(column, super())
|
155
155
|
end
|
156
156
|
end
|
157
|
-
|
157
|
+
alias_method(column, column)
|
158
|
+
|
159
|
+
setter = :"#{column}="
|
160
|
+
define_method(setter) do |v|
|
158
161
|
cc = changed_columns
|
159
162
|
if !cc.include?(column) && (new? || get_column_value(column) != v)
|
160
163
|
cc << column
|
@@ -164,6 +167,7 @@ module Sequel
|
|
164
167
|
|
165
168
|
deserialized_values[column] = v
|
166
169
|
end
|
170
|
+
alias_method(setter, setter)
|
167
171
|
end
|
168
172
|
end
|
169
173
|
end
|
@@ -177,8 +181,10 @@ module Sequel
|
|
177
181
|
|
178
182
|
# Freeze the deserialized values
|
179
183
|
def freeze
|
180
|
-
deserialized_values
|
184
|
+
deserialized_values
|
181
185
|
super
|
186
|
+
deserialized_values.freeze
|
187
|
+
self
|
182
188
|
end
|
183
189
|
|
184
190
|
# Serialize deserialized values before saving
|
@@ -50,8 +50,9 @@ module Sequel
|
|
50
50
|
# Freeze the original deserialized values when freezing the instance.
|
51
51
|
def freeze
|
52
52
|
@original_deserialized_values ||= {}
|
53
|
-
@original_deserialized_values.freeze
|
54
53
|
super
|
54
|
+
@original_deserialized_values.freeze
|
55
|
+
self
|
55
56
|
end
|
56
57
|
|
57
58
|
private
|
@@ -253,6 +253,14 @@ module Sequel
|
|
253
253
|
|
254
254
|
private
|
255
255
|
|
256
|
+
# Limit tactical eager loading objects to objects that support the same association.
|
257
|
+
def _filter_tactical_eager_load_objects(opts)
|
258
|
+
objects = defined?(super) ? super : retrieved_with.dup
|
259
|
+
name = opts[:name]
|
260
|
+
objects.select!{|x| x.model.association_reflections.include?(name)}
|
261
|
+
objects
|
262
|
+
end
|
263
|
+
|
256
264
|
# Don't allow use of prepared statements.
|
257
265
|
def use_prepared_statements_for?(type)
|
258
266
|
false
|