sequel 5.39.0 → 5.72.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (219) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +408 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +59 -27
  5. data/bin/sequel +11 -3
  6. data/doc/advanced_associations.rdoc +16 -14
  7. data/doc/association_basics.rdoc +119 -24
  8. data/doc/cheat_sheet.rdoc +11 -3
  9. data/doc/mass_assignment.rdoc +1 -1
  10. data/doc/migration.rdoc +13 -6
  11. data/doc/model_hooks.rdoc +1 -1
  12. data/doc/object_model.rdoc +8 -8
  13. data/doc/opening_databases.rdoc +26 -12
  14. data/doc/postgresql.rdoc +16 -8
  15. data/doc/querying.rdoc +5 -3
  16. data/doc/release_notes/5.40.0.txt +40 -0
  17. data/doc/release_notes/5.41.0.txt +25 -0
  18. data/doc/release_notes/5.42.0.txt +136 -0
  19. data/doc/release_notes/5.43.0.txt +98 -0
  20. data/doc/release_notes/5.44.0.txt +32 -0
  21. data/doc/release_notes/5.45.0.txt +34 -0
  22. data/doc/release_notes/5.46.0.txt +87 -0
  23. data/doc/release_notes/5.47.0.txt +59 -0
  24. data/doc/release_notes/5.48.0.txt +14 -0
  25. data/doc/release_notes/5.49.0.txt +59 -0
  26. data/doc/release_notes/5.50.0.txt +78 -0
  27. data/doc/release_notes/5.51.0.txt +47 -0
  28. data/doc/release_notes/5.52.0.txt +87 -0
  29. data/doc/release_notes/5.53.0.txt +23 -0
  30. data/doc/release_notes/5.54.0.txt +27 -0
  31. data/doc/release_notes/5.55.0.txt +21 -0
  32. data/doc/release_notes/5.56.0.txt +51 -0
  33. data/doc/release_notes/5.57.0.txt +23 -0
  34. data/doc/release_notes/5.58.0.txt +31 -0
  35. data/doc/release_notes/5.59.0.txt +73 -0
  36. data/doc/release_notes/5.60.0.txt +22 -0
  37. data/doc/release_notes/5.61.0.txt +43 -0
  38. data/doc/release_notes/5.62.0.txt +132 -0
  39. data/doc/release_notes/5.63.0.txt +33 -0
  40. data/doc/release_notes/5.64.0.txt +50 -0
  41. data/doc/release_notes/5.65.0.txt +21 -0
  42. data/doc/release_notes/5.66.0.txt +24 -0
  43. data/doc/release_notes/5.67.0.txt +32 -0
  44. data/doc/release_notes/5.68.0.txt +61 -0
  45. data/doc/release_notes/5.69.0.txt +26 -0
  46. data/doc/release_notes/5.70.0.txt +35 -0
  47. data/doc/release_notes/5.71.0.txt +21 -0
  48. data/doc/release_notes/5.72.0.txt +33 -0
  49. data/doc/schema_modification.rdoc +1 -1
  50. data/doc/security.rdoc +9 -9
  51. data/doc/sharding.rdoc +3 -1
  52. data/doc/sql.rdoc +28 -16
  53. data/doc/testing.rdoc +22 -11
  54. data/doc/transactions.rdoc +6 -6
  55. data/doc/virtual_rows.rdoc +2 -2
  56. data/lib/sequel/adapters/ado/access.rb +1 -1
  57. data/lib/sequel/adapters/ado.rb +17 -17
  58. data/lib/sequel/adapters/amalgalite.rb +3 -5
  59. data/lib/sequel/adapters/ibmdb.rb +2 -2
  60. data/lib/sequel/adapters/jdbc/derby.rb +8 -0
  61. data/lib/sequel/adapters/jdbc/h2.rb +60 -10
  62. data/lib/sequel/adapters/jdbc/hsqldb.rb +6 -0
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +7 -4
  64. data/lib/sequel/adapters/jdbc.rb +16 -18
  65. data/lib/sequel/adapters/mysql.rb +92 -67
  66. data/lib/sequel/adapters/mysql2.rb +54 -49
  67. data/lib/sequel/adapters/odbc.rb +6 -2
  68. data/lib/sequel/adapters/oracle.rb +4 -3
  69. data/lib/sequel/adapters/postgres.rb +83 -40
  70. data/lib/sequel/adapters/shared/access.rb +11 -1
  71. data/lib/sequel/adapters/shared/db2.rb +30 -0
  72. data/lib/sequel/adapters/shared/mssql.rb +90 -9
  73. data/lib/sequel/adapters/shared/mysql.rb +47 -2
  74. data/lib/sequel/adapters/shared/oracle.rb +82 -1
  75. data/lib/sequel/adapters/shared/postgres.rb +496 -178
  76. data/lib/sequel/adapters/shared/sqlanywhere.rb +11 -1
  77. data/lib/sequel/adapters/shared/sqlite.rb +116 -11
  78. data/lib/sequel/adapters/sqlanywhere.rb +1 -1
  79. data/lib/sequel/adapters/sqlite.rb +60 -18
  80. data/lib/sequel/adapters/tinytds.rb +1 -1
  81. data/lib/sequel/adapters/trilogy.rb +117 -0
  82. data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
  83. data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
  84. data/lib/sequel/ast_transformer.rb +6 -0
  85. data/lib/sequel/connection_pool/sharded_single.rb +5 -7
  86. data/lib/sequel/connection_pool/sharded_threaded.rb +16 -11
  87. data/lib/sequel/connection_pool/sharded_timed_queue.rb +374 -0
  88. data/lib/sequel/connection_pool/single.rb +6 -8
  89. data/lib/sequel/connection_pool/threaded.rb +14 -8
  90. data/lib/sequel/connection_pool/timed_queue.rb +270 -0
  91. data/lib/sequel/connection_pool.rb +55 -31
  92. data/lib/sequel/core.rb +28 -18
  93. data/lib/sequel/database/connecting.rb +27 -3
  94. data/lib/sequel/database/dataset.rb +16 -6
  95. data/lib/sequel/database/misc.rb +69 -14
  96. data/lib/sequel/database/query.rb +73 -2
  97. data/lib/sequel/database/schema_generator.rb +46 -53
  98. data/lib/sequel/database/schema_methods.rb +18 -2
  99. data/lib/sequel/dataset/actions.rb +108 -14
  100. data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
  101. data/lib/sequel/dataset/features.rb +20 -0
  102. data/lib/sequel/dataset/misc.rb +12 -2
  103. data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
  104. data/lib/sequel/dataset/prepared_statements.rb +2 -0
  105. data/lib/sequel/dataset/query.rb +171 -44
  106. data/lib/sequel/dataset/sql.rb +182 -47
  107. data/lib/sequel/dataset.rb +4 -0
  108. data/lib/sequel/extensions/_model_pg_row.rb +0 -12
  109. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  110. data/lib/sequel/extensions/any_not_empty.rb +1 -1
  111. data/lib/sequel/extensions/async_thread_pool.rb +439 -0
  112. data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
  113. data/lib/sequel/extensions/blank.rb +8 -0
  114. data/lib/sequel/extensions/connection_expiration.rb +15 -9
  115. data/lib/sequel/extensions/connection_validator.rb +16 -11
  116. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  117. data/lib/sequel/extensions/core_refinements.rb +36 -11
  118. data/lib/sequel/extensions/date_arithmetic.rb +71 -31
  119. data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
  120. data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
  121. data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -1
  122. data/lib/sequel/extensions/eval_inspect.rb +2 -0
  123. data/lib/sequel/extensions/index_caching.rb +5 -1
  124. data/lib/sequel/extensions/inflector.rb +9 -1
  125. data/lib/sequel/extensions/is_distinct_from.rb +141 -0
  126. data/lib/sequel/extensions/looser_typecasting.rb +3 -0
  127. data/lib/sequel/extensions/migration.rb +11 -2
  128. data/lib/sequel/extensions/named_timezones.rb +26 -6
  129. data/lib/sequel/extensions/pagination.rb +1 -1
  130. data/lib/sequel/extensions/pg_array.rb +32 -4
  131. data/lib/sequel/extensions/pg_array_ops.rb +2 -2
  132. data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
  133. data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +110 -0
  134. data/lib/sequel/extensions/pg_enum.rb +2 -3
  135. data/lib/sequel/extensions/pg_extended_date_support.rb +38 -27
  136. data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
  137. data/lib/sequel/extensions/pg_hstore.rb +6 -1
  138. data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
  139. data/lib/sequel/extensions/pg_inet.rb +10 -11
  140. data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
  141. data/lib/sequel/extensions/pg_interval.rb +45 -19
  142. data/lib/sequel/extensions/pg_json.rb +13 -15
  143. data/lib/sequel/extensions/pg_json_ops.rb +73 -2
  144. data/lib/sequel/extensions/pg_loose_count.rb +3 -1
  145. data/lib/sequel/extensions/pg_multirange.rb +367 -0
  146. data/lib/sequel/extensions/pg_range.rb +11 -24
  147. data/lib/sequel/extensions/pg_range_ops.rb +37 -9
  148. data/lib/sequel/extensions/pg_row.rb +21 -19
  149. data/lib/sequel/extensions/pg_row_ops.rb +1 -1
  150. data/lib/sequel/extensions/query.rb +2 -0
  151. data/lib/sequel/extensions/s.rb +2 -1
  152. data/lib/sequel/extensions/schema_caching.rb +1 -1
  153. data/lib/sequel/extensions/schema_dumper.rb +45 -11
  154. data/lib/sequel/extensions/server_block.rb +10 -13
  155. data/lib/sequel/extensions/set_literalizer.rb +58 -0
  156. data/lib/sequel/extensions/sql_comments.rb +110 -3
  157. data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
  158. data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
  159. data/lib/sequel/extensions/string_agg.rb +1 -1
  160. data/lib/sequel/extensions/string_date_time.rb +19 -23
  161. data/lib/sequel/extensions/symbol_aref.rb +2 -0
  162. data/lib/sequel/model/associations.rb +345 -101
  163. data/lib/sequel/model/base.rb +51 -27
  164. data/lib/sequel/model/dataset_module.rb +3 -0
  165. data/lib/sequel/model/errors.rb +10 -1
  166. data/lib/sequel/model/inflections.rb +1 -1
  167. data/lib/sequel/model/plugins.rb +5 -0
  168. data/lib/sequel/plugins/association_proxies.rb +2 -0
  169. data/lib/sequel/plugins/async_thread_pool.rb +39 -0
  170. data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
  171. data/lib/sequel/plugins/auto_validations.rb +87 -15
  172. data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
  173. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  174. data/lib/sequel/plugins/column_encryption.rb +728 -0
  175. data/lib/sequel/plugins/composition.rb +10 -4
  176. data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
  177. data/lib/sequel/plugins/constraint_validations.rb +10 -6
  178. data/lib/sequel/plugins/dataset_associations.rb +4 -1
  179. data/lib/sequel/plugins/defaults_setter.rb +16 -0
  180. data/lib/sequel/plugins/dirty.rb +1 -1
  181. data/lib/sequel/plugins/enum.rb +124 -0
  182. data/lib/sequel/plugins/finder.rb +4 -2
  183. data/lib/sequel/plugins/insert_conflict.rb +4 -0
  184. data/lib/sequel/plugins/instance_specific_default.rb +1 -1
  185. data/lib/sequel/plugins/json_serializer.rb +39 -24
  186. data/lib/sequel/plugins/lazy_attributes.rb +3 -0
  187. data/lib/sequel/plugins/list.rb +3 -1
  188. data/lib/sequel/plugins/many_through_many.rb +109 -10
  189. data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -38
  190. data/lib/sequel/plugins/nested_attributes.rb +12 -7
  191. data/lib/sequel/plugins/optimistic_locking.rb +9 -42
  192. data/lib/sequel/plugins/optimistic_locking_base.rb +55 -0
  193. data/lib/sequel/plugins/pg_array_associations.rb +56 -38
  194. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +11 -3
  195. data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
  196. data/lib/sequel/plugins/prepared_statements.rb +12 -2
  197. data/lib/sequel/plugins/prepared_statements_safe.rb +2 -1
  198. data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
  199. data/lib/sequel/plugins/rcte_tree.rb +27 -19
  200. data/lib/sequel/plugins/require_valid_schema.rb +67 -0
  201. data/lib/sequel/plugins/serialization.rb +9 -3
  202. data/lib/sequel/plugins/serialization_modification_detection.rb +2 -1
  203. data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
  204. data/lib/sequel/plugins/sql_comments.rb +189 -0
  205. data/lib/sequel/plugins/static_cache.rb +39 -1
  206. data/lib/sequel/plugins/static_cache_cache.rb +5 -1
  207. data/lib/sequel/plugins/subclasses.rb +28 -11
  208. data/lib/sequel/plugins/tactical_eager_loading.rb +23 -10
  209. data/lib/sequel/plugins/timestamps.rb +1 -1
  210. data/lib/sequel/plugins/unused_associations.rb +521 -0
  211. data/lib/sequel/plugins/update_or_create.rb +1 -1
  212. data/lib/sequel/plugins/validate_associated.rb +22 -12
  213. data/lib/sequel/plugins/validation_helpers.rb +46 -12
  214. data/lib/sequel/plugins/validation_helpers_generic_type_messages.rb +73 -0
  215. data/lib/sequel/plugins/xml_serializer.rb +1 -1
  216. data/lib/sequel/sql.rb +1 -1
  217. data/lib/sequel/timezones.rb +12 -14
  218. data/lib/sequel/version.rb +1 -1
  219. 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, :insert_select, :update
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[:rows].each do |obj|
174
- parent_map[prkey_conv[obj]] = obj
175
- (children_map[key_conv[obj]] ||= []) << obj
176
- obj.associations[ancestors] = []
177
- obj.associations[parent] = nil
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
- parent_map.each do |parent_id, obj|
211
- if children = children_map[parent_id]
212
- children.each do |child|
213
- child.associations[parent] = obj
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[:rows].each do |obj|
272
- parent_map[prkey_conv[obj]] = obj
273
- obj.associations[descendants] = []
274
- obj.associations[childrena] = []
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
- children_map.each do |parent_id, objs|
320
- objs = objs.uniq
321
- parent_obj = parent_map[parent_id]
322
- parent_obj.associations[childrena] = objs
323
- objs.each do |obj|
324
- obj.associations[parent] = parent_obj
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}.map(&:inspect).join})")
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
- define_method("#{column}=") do |v|
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.freeze
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