sequel 5.39.0 → 5.72.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.
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
@@ -508,7 +508,9 @@ module Sequel
508
508
 
509
509
  m.configure(self, *args, &block) if m.respond_to?(:configure)
510
510
  end
511
+ # :nocov:
511
512
  ruby2_keywords(:plugin) if respond_to?(:ruby2_keywords, true)
513
+ # :nocov:
512
514
 
513
515
  # Returns primary key attribute hash. If using a composite primary key
514
516
  # value such be an array with values for each primary key in the correct
@@ -604,6 +606,7 @@ module Sequel
604
606
  @db_schema = get_db_schema
605
607
  end
606
608
 
609
+ @fast_pk_lookup_sql = @fast_instance_delete_sql = nil unless @dataset.supports_placeholder_literalizer?
607
610
  reset_instance_dataset
608
611
  self
609
612
  end
@@ -678,24 +681,26 @@ module Sequel
678
681
 
679
682
  private
680
683
 
681
- # Yield to the passed block and if do_raise is false, swallow all errors other than DatabaseConnectionErrors.
684
+ # Yield to the passed block and if do_raise is false, swallow Sequel::Errors other than DatabaseConnectionError
685
+ # and DatabaseDisconnectError.
682
686
  def check_non_connection_error(do_raise=require_valid_table)
683
- begin
684
- db.transaction(:savepoint=>:only){yield}
685
- rescue Sequel::DatabaseConnectionError
686
- raise
687
- rescue Sequel::Error
688
- raise if do_raise
689
- end
687
+ db.transaction(:savepoint=>:only){yield}
688
+ rescue Sequel::DatabaseConnectionError, Sequel::DatabaseDisconnectError
689
+ raise
690
+ rescue Sequel::Error
691
+ raise if do_raise
690
692
  end
691
693
 
692
694
  # Convert the given object to a Dataset that should be used as
693
695
  # this model's dataset.
694
696
  def convert_input_dataset(ds)
695
697
  case ds
696
- when Symbol, SQL::Identifier, SQL::QualifiedIdentifier, SQL::AliasedExpression, LiteralString
698
+ when Symbol, SQL::Identifier, SQL::QualifiedIdentifier
697
699
  self.simple_table = db.literal(ds).freeze
698
700
  ds = db.from(ds)
701
+ when SQL::AliasedExpression, LiteralString
702
+ self.simple_table = nil
703
+ ds = db.from(ds)
699
704
  when Dataset
700
705
  ds = ds.from_self(:alias=>ds.first_source) if ds.joined_dataset?
701
706
 
@@ -727,8 +732,14 @@ module Sequel
727
732
  im = instance_methods
728
733
  overridable_methods_module.module_eval do
729
734
  meth = :"#{column}="
730
- define_method(column){self[column]} unless im.include?(column)
731
- define_method(meth){|v| self[column] = v} unless im.include?(meth)
735
+ unless im.include?(column)
736
+ define_method(column){self[column]}
737
+ alias_method(column, column)
738
+ end
739
+ unless im.include?(meth)
740
+ define_method(meth){|v| self[column] = v}
741
+ alias_method(meth, meth)
742
+ end
732
743
  end
733
744
  end
734
745
 
@@ -741,8 +752,14 @@ module Sequel
741
752
  im = instance_methods
742
753
  columns.each do |column|
743
754
  meth = :"#{column}="
744
- overridable_methods_module.module_eval("def #{column}; self[:#{column}] end", __FILE__, __LINE__) unless im.include?(column)
745
- overridable_methods_module.module_eval("def #{meth}(v); self[:#{column}] = v end", __FILE__, __LINE__) unless im.include?(meth)
755
+ unless im.include?(column)
756
+ overridable_methods_module.module_eval("def #{column}; self[:#{column}] end", __FILE__, __LINE__)
757
+ overridable_methods_module.send(:alias_method, column, column)
758
+ end
759
+ unless im.include?(meth)
760
+ overridable_methods_module.module_eval("def #{meth}(v); self[:#{column}] = v end", __FILE__, __LINE__)
761
+ overridable_methods_module.send(:alias_method, meth, meth)
762
+ end
746
763
  end
747
764
  end
748
765
 
@@ -757,7 +774,10 @@ module Sequel
757
774
  else
758
775
  define_singleton_method(meth){|*args, &block| dataset.public_send(meth, *args, &block)}
759
776
  end
777
+ singleton_class.send(:alias_method, meth, meth)
778
+ # :nocov:
760
779
  singleton_class.send(:ruby2_keywords, meth) if respond_to?(:ruby2_keywords, true)
780
+ # :nocov:
761
781
  end
762
782
 
763
783
  # Get the schema from the database, fall back on checking the columns
@@ -769,7 +789,7 @@ module Sequel
769
789
  schema_hash = {}
770
790
  ds_opts = dataset.opts
771
791
  get_columns = proc{check_non_connection_error{columns} || []}
772
- schema_array = check_non_connection_error(false){db.schema(dataset, :reload=>reload)} if db.supports_schema_parsing?
792
+ schema_array = get_db_schema_array(reload) if db.supports_schema_parsing?
773
793
  if schema_array
774
794
  schema_array.each{|k,v| schema_hash[k] = v}
775
795
 
@@ -806,6 +826,12 @@ module Sequel
806
826
  schema_hash
807
827
  end
808
828
 
829
+ # Get the array of schema information for the dataset. Returns nil if
830
+ # the schema information cannot be determined.
831
+ def get_db_schema_array(reload)
832
+ check_non_connection_error(false){db.schema(dataset, :reload=>reload)}
833
+ end
834
+
809
835
  # Uncached version of setter_methods, to be overridden by plugins
810
836
  # that want to modify the methods used.
811
837
  def get_setter_methods
@@ -1076,7 +1102,7 @@ module Sequel
1076
1102
  @modified = true
1077
1103
  initialize_set(values)
1078
1104
  _clear_changed_columns(:initialize)
1079
- yield self if block_given?
1105
+ yield self if defined?(yield)
1080
1106
  end
1081
1107
 
1082
1108
  # Returns value of the column's attribute.
@@ -1116,7 +1142,7 @@ module Sequel
1116
1142
  #
1117
1143
  # Artist[1] === Artist[1] # => true
1118
1144
  # Artist.new === Artist.new # => false
1119
- # Artist[1].set(:name=>'Bob') === Artist[1] # => true
1145
+ # Artist[1].set(name: 'Bob') === Artist[1] # => true
1120
1146
  def ===(obj)
1121
1147
  case pkv = pk
1122
1148
  when nil
@@ -1135,7 +1161,7 @@ module Sequel
1135
1161
  #
1136
1162
  # Artist[1].pk_equal?(Artist[1]) # => true
1137
1163
  # Artist.new.pk_equal?(Artist.new) # => false
1138
- # Artist[1].set(:name=>'Bob').pk_equal?(Artist[1]) # => true
1164
+ # Artist[1].set(name: 'Bob').pk_equal?(Artist[1]) # => true
1139
1165
  alias pk_equal? ===
1140
1166
 
1141
1167
  # class is defined in Object, but it is also a keyword,
@@ -1207,7 +1233,7 @@ module Sequel
1207
1233
  #
1208
1234
  # Artist[1] == Artist[1] # => true
1209
1235
  # Artist.new == Artist.new # => true
1210
- # Artist[1].set(:name=>'Bob') == Artist[1] # => false
1236
+ # Artist[1].set(name: 'Bob') == Artist[1] # => false
1211
1237
  def eql?(obj)
1212
1238
  (obj.class == model) && (obj.values == @values)
1213
1239
  end
@@ -1243,12 +1269,12 @@ module Sequel
1243
1269
  # Once an object is frozen, you cannot modify it's values, changed_columns,
1244
1270
  # errors, or dataset.
1245
1271
  def freeze
1246
- values.freeze
1247
- _changed_columns.freeze
1248
1272
  unless errors.frozen?
1249
1273
  validate
1250
1274
  errors.freeze
1251
1275
  end
1276
+ values.freeze
1277
+ _changed_columns.freeze
1252
1278
  this if !new? && model.primary_key
1253
1279
  super
1254
1280
  end
@@ -1309,13 +1335,13 @@ module Sequel
1309
1335
  # a = Artist[1]
1310
1336
  # Artist.db.transaction do
1311
1337
  # a.lock!
1312
- # a.update(:name=>'A')
1338
+ # a.update(name: 'A')
1313
1339
  # end
1314
1340
  #
1315
1341
  # a = Artist[2]
1316
1342
  # Artist.db.transaction do
1317
1343
  # a.lock!('FOR NO KEY UPDATE')
1318
- # a.update(:name=>'B')
1344
+ # a.update(name: 'B')
1319
1345
  # end
1320
1346
  def lock!(style=:update)
1321
1347
  _refresh(this.lock_style(style)) unless new?
@@ -1613,11 +1639,9 @@ module Sequel
1613
1639
  # artist.set(name: 'Invalid').valid? # => false
1614
1640
  # artist.errors.full_messages # => ['name cannot be Invalid']
1615
1641
  def valid?(opts = OPTS)
1616
- begin
1617
- _valid?(opts)
1618
- rescue HookFailed
1619
- false
1620
- end
1642
+ _valid?(opts)
1643
+ rescue HookFailed
1644
+ false
1621
1645
  end
1622
1646
 
1623
1647
  private
@@ -8,6 +8,9 @@ module Sequel
8
8
  # automatically creates class methods for public dataset
9
9
  # methods.
10
10
  class DatasetModule < Dataset::DatasetModule
11
+ # The model class related to this dataset module.
12
+ attr_reader :model
13
+
11
14
  # Store the model related to this dataset module.
12
15
  def initialize(model)
13
16
  @model = model
@@ -38,7 +38,7 @@ module Sequel
38
38
  def full_messages
39
39
  inject([]) do |m, kv|
40
40
  att, errors = *kv
41
- errors.each {|e| m << (e.is_a?(LiteralString) ? e : "#{Array(att).join(' and ')} #{e}")}
41
+ errors.each {|e| m << (e.is_a?(LiteralString) ? e : full_message(att, e))}
42
42
  m
43
43
  end
44
44
  end
@@ -53,6 +53,15 @@ module Sequel
53
53
  v
54
54
  end
55
55
  end
56
+
57
+ private
58
+
59
+ # Create full error message to use for the given attribute (or array of attributes)
60
+ # and error message. This can be overridden for easier internalization.
61
+ def full_message(att, error_msg)
62
+ att = att.join(' and ') if att.is_a?(Array)
63
+ "#{att} #{error_msg}"
64
+ end
56
65
  end
57
66
  end
58
67
  end
@@ -4,7 +4,7 @@ module Sequel
4
4
  # Yield the Inflections module if a block is given, and return
5
5
  # the Inflections module.
6
6
  def self.inflections
7
- yield Inflections if block_given?
7
+ yield Inflections if defined?(yield)
8
8
  Inflections
9
9
  end
10
10
 
@@ -31,7 +31,9 @@ module Sequel
31
31
  def self.def_dataset_methods(mod, meths)
32
32
  Array(meths).each do |meth|
33
33
  mod.class_eval("def #{meth}(*args, &block); dataset.#{meth}(*args, &block) end", __FILE__, __LINE__)
34
+ # :nocov:
34
35
  mod.send(:ruby2_keywords, meth) if respond_to?(:ruby2_keywords, true)
36
+ # :nocov:
35
37
  end
36
38
  end
37
39
 
@@ -120,6 +122,7 @@ module Sequel
120
122
 
121
123
  model.send(:define_method, meth, &block)
122
124
  model.send(:private, meth)
125
+ model.send(:alias_method, meth, meth)
123
126
  call_meth
124
127
  end
125
128
 
@@ -141,6 +144,8 @@ module Sequel
141
144
  keyword = :required
142
145
  when :key, :keyrest
143
146
  keyword ||= true
147
+ else
148
+ raise Error, "invalid arg_type passed to _define_sequel_method_arg_numbers: #{arg_type}"
144
149
  end
145
150
  end
146
151
  arity = callable.arity
@@ -99,7 +99,9 @@ module Sequel
99
99
  end
100
100
  v.public_send(meth, *args, &block)
101
101
  end
102
+ # :nocov:
102
103
  ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
104
+ # :nocov:
103
105
  end
104
106
 
105
107
  module ClassMethods
@@ -0,0 +1,39 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Sequel
4
+ extension 'async_thread_pool'
5
+
6
+ module Plugins
7
+ # The async_thread_pool plugin makes it slightly easier to use the async_thread_pool
8
+ # Database extension with models. It makes Model.async return an async dataset for the
9
+ # model, and support async behavior for #destroy, #with_pk, and #with_pk! for model
10
+ # datasets:
11
+ #
12
+ # # Will load the artist with primary key 1 asynchronously
13
+ # artist = Artist.async.with_pk(1)
14
+ #
15
+ # You must load the async_thread_pool Database extension into the Database object the
16
+ # model class uses in order for async behavior to work.
17
+ #
18
+ # Usage:
19
+ #
20
+ # # Make all model subclass datasets support support async class methods and additional
21
+ # # async dataset methods
22
+ # Sequel::Model.plugin :async_thread_pool
23
+ #
24
+ # # Make the Album class support async class method and additional async dataset methods
25
+ # Album.plugin :async_thread_pool
26
+ module AsyncThreadPool
27
+ module ClassMethods
28
+ Plugins.def_dataset_methods(self, :async)
29
+ end
30
+
31
+ module DatasetMethods
32
+ [:destroy, :with_pk, :with_pk!].each do |meth|
33
+ ::Sequel::Database::AsyncThreadPool::DatasetMethods.define_async_method(self, meth)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+
@@ -0,0 +1,62 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Sequel
4
+ module Plugins
5
+ # The auto_restrict_eager_graph plugin will automatically disallow the use
6
+ # of eager_graph for associations that have associated blocks but no :graph_*
7
+ # association options. The reason for this is the block will have an effect
8
+ # during regular and eager loading, but not loading via eager_graph, and it
9
+ # is likely that whatever the block is doing should have an equivalent done
10
+ # when eager_graphing. Most likely, not including a :graph_* option was either
11
+ # an oversight (and one should be added), or use with eager_graph was never
12
+ # intended (and usage should be forbidden). Disallowing eager_graph in this
13
+ # case prevents likely unexpected behavior during eager_graph.
14
+ #
15
+ # As an example of this, consider the following code:
16
+ #
17
+ # Album.one_to_many :popular_tracks, class: :Track do |ds|
18
+ # ds = ds.where(popular: true)
19
+ # end
20
+ #
21
+ # Album.eager(:popular_tracks).all
22
+ # # SELECT * FROM albums
23
+ # # SELECT * FROM tracks WHERE ((popular IS TRUE) AND (album_id IN (...)))
24
+ #
25
+ # # Notice that no condition for tracks.popular is added.
26
+ # Album.eager_graph(:popular_tracks).all
27
+ # # SELECT ... FROM albums LEFT JOIN tracks ON (tracks.album_id = albums.id)
28
+ #
29
+ # With the auto_restrict_eager_graph plugin, the eager_graph call above will
30
+ # raise an error, alerting you to the fact that you either should not be
31
+ # using eager_graph with the association, or that you should be adding an
32
+ # appropriate :graph_* option, such as:
33
+ #
34
+ # Album.one_to_many :popular_tracks, class: :Track, graph_conditions: {popular: true} do |ds|
35
+ # ds = ds.where(popular: true)
36
+ # end
37
+ #
38
+ # Usage:
39
+ #
40
+ # # Automatically restrict eager_graph for associations if appropriate for all
41
+ # # model subclasses (called before loading subclasses)
42
+ # Sequel::Model.plugin :auto_restrict_eager_graph
43
+ #
44
+ # # Automatically restrict eager_graph for associations in Album class
45
+ # Album.plugin :auto_restrict_eager_graph
46
+ module AutoRestrictEagerGraph
47
+ module ClassMethods
48
+ # When defining an association, if a block is given for the association, but
49
+ # a :graph_* option is not used, disallow the use of eager_graph.
50
+ def associate(type, name, opts = OPTS, &block)
51
+ opts = super
52
+
53
+ if opts[:block] && !opts.has_key?(:allow_eager_graph) && !opts[:orig_opts].any?{|k,| /\Agraph_/ =~ k}
54
+ opts[:allow_eager_graph] = false
55
+ end
56
+
57
+ opts
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -9,12 +9,16 @@ module Sequel
9
9
  # 2. not_null validations on NOT NULL columns (optionally, presence validations)
10
10
  # 3. unique validations on columns or sets of columns with unique indexes
11
11
  # 4. max length validations on string columns
12
+ # 5. no null byte validations on string columns
13
+ # 6. minimum and maximum values on columns
12
14
  #
13
- # To determine the columns to use for the type/not_null/max_length validations,
15
+ # To determine the columns to use for the type/not_null/max_length/no_null_byte/max_value/min_value validations,
14
16
  # the plugin looks at the database schema for the model's table. To determine
15
17
  # the unique validations, Sequel looks at the indexes on the table. In order
16
18
  # for this plugin to be fully functional, the underlying database adapter needs
17
- # to support both schema and index parsing.
19
+ # to support both schema and index parsing. Additionally, unique validations are
20
+ # only added for models that select from a simple table, they are not added for models
21
+ # that select from a subquery.
18
22
  #
19
23
  # This plugin uses the validation_helpers plugin underneath to implement the
20
24
  # validations. It does not allow for any per-column validation message
@@ -48,9 +52,14 @@ module Sequel
48
52
  #
49
53
  # Model.plugin :auto_validations, unique_opts: {only_if_modified: true}
50
54
  #
51
- # This works for unique_opts, max_length_opts, schema_types_opts,
55
+ # This works for unique_opts, max_length_opts, schema_types_opts, max_value_opts, min_value_opts, no_null_byte_opts,
52
56
  # explicit_not_null_opts, and not_null_opts.
53
57
  #
58
+ # If you only want auto_validations to add validations to columns that do not already
59
+ # have an error associated with them, you can use the skip_invalid option:
60
+ #
61
+ # Model.plugin :auto_validations, skip_invalid: true
62
+ #
54
63
  # Usage:
55
64
  #
56
65
  # # Make all model subclass use auto validations (called before loading subclasses)
@@ -64,25 +73,35 @@ module Sequel
64
73
  MAX_LENGTH_OPTIONS = {:from=>:values, :allow_nil=>true}.freeze
65
74
  SCHEMA_TYPES_OPTIONS = NOT_NULL_OPTIONS
66
75
  UNIQUE_OPTIONS = NOT_NULL_OPTIONS
76
+ NO_NULL_BYTE_OPTIONS = MAX_LENGTH_OPTIONS
77
+ MAX_VALUE_OPTIONS = {:from=>:values, :allow_nil=>true, :skip_invalid=>true}.freeze
78
+ MIN_VALUE_OPTIONS = MAX_VALUE_OPTIONS
79
+ AUTO_VALIDATE_OPTIONS = {
80
+ :no_null_byte=>NO_NULL_BYTE_OPTIONS,
81
+ :not_null=>NOT_NULL_OPTIONS,
82
+ :explicit_not_null=>EXPLICIT_NOT_NULL_OPTIONS,
83
+ :max_length=>MAX_LENGTH_OPTIONS,
84
+ :max_value=>MAX_VALUE_OPTIONS,
85
+ :min_value=>MIN_VALUE_OPTIONS,
86
+ :schema_types=>SCHEMA_TYPES_OPTIONS,
87
+ :unique=>UNIQUE_OPTIONS
88
+ }.freeze
89
+
67
90
  EMPTY_ARRAY = [].freeze
68
91
 
69
92
  def self.apply(model, opts=OPTS)
70
93
  model.instance_exec do
71
94
  plugin :validation_helpers
72
95
  @auto_validate_presence = false
96
+ @auto_validate_no_null_byte_columns = []
73
97
  @auto_validate_not_null_columns = []
74
98
  @auto_validate_explicit_not_null_columns = []
75
99
  @auto_validate_max_length_columns = []
100
+ @auto_validate_max_value_columns = []
101
+ @auto_validate_min_value_columns = []
76
102
  @auto_validate_unique_columns = []
77
103
  @auto_validate_types = true
78
-
79
- @auto_validate_options = {
80
- :not_null=>NOT_NULL_OPTIONS,
81
- :explicit_not_null=>EXPLICIT_NOT_NULL_OPTIONS,
82
- :max_length=>MAX_LENGTH_OPTIONS,
83
- :schema_types=>SCHEMA_TYPES_OPTIONS,
84
- :unique=>UNIQUE_OPTIONS
85
- }.freeze
104
+ @auto_validate_options = AUTO_VALIDATE_OPTIONS
86
105
  end
87
106
  end
88
107
 
@@ -95,16 +114,26 @@ module Sequel
95
114
  end
96
115
 
97
116
  h = @auto_validate_options.dup
98
- [:not_null, :explicit_not_null, :max_length, :schema_types, :unique].each do |type|
117
+ [:not_null, :explicit_not_null, :max_length, :max_value, :min_value, :no_null_byte, :schema_types, :unique].each do |type|
99
118
  if type_opts = opts[:"#{type}_opts"]
100
119
  h[type] = h[type].merge(type_opts).freeze
101
120
  end
102
121
  end
122
+
123
+ if opts[:skip_invalid]
124
+ [:not_null, :explicit_not_null, :no_null_byte, :max_length, :schema_types].each do |type|
125
+ h[type] = h[type].merge(:skip_invalid=>true).freeze
126
+ end
127
+ end
128
+
103
129
  @auto_validate_options = h.freeze
104
130
  end
105
131
  end
106
132
 
107
133
  module ClassMethods
134
+ # The columns with automatic no_null_byte validations
135
+ attr_reader :auto_validate_no_null_byte_columns
136
+
108
137
  # The columns with automatic not_null validations
109
138
  attr_reader :auto_validate_not_null_columns
110
139
 
@@ -115,13 +144,31 @@ module Sequel
115
144
  # pairs, with the first entry being the column name and second entry being the maximum length.
116
145
  attr_reader :auto_validate_max_length_columns
117
146
 
147
+ # The columns with automatch max value validations, as an array of
148
+ # pairs, with the first entry being the column name and second entry being the maximum value.
149
+ attr_reader :auto_validate_max_value_columns
150
+
151
+ # The columns with automatch min value validations, as an array of
152
+ # pairs, with the first entry being the column name and second entry being the minimum value.
153
+ attr_reader :auto_validate_min_value_columns
154
+
118
155
  # The columns or sets of columns with automatic unique validations
119
156
  attr_reader :auto_validate_unique_columns
120
157
 
121
158
  # Inherited options
122
159
  attr_reader :auto_validate_options
123
160
 
124
- Plugins.inherited_instance_variables(self, :@auto_validate_presence=>nil, :@auto_validate_types=>nil, :@auto_validate_not_null_columns=>:dup, :@auto_validate_explicit_not_null_columns=>:dup, :@auto_validate_max_length_columns=>:dup, :@auto_validate_unique_columns=>:dup, :@auto_validate_options => :dup)
161
+ Plugins.inherited_instance_variables(self,
162
+ :@auto_validate_presence=>nil,
163
+ :@auto_validate_types=>nil,
164
+ :@auto_validate_no_null_byte_columns=>:dup,
165
+ :@auto_validate_not_null_columns=>:dup,
166
+ :@auto_validate_explicit_not_null_columns=>:dup,
167
+ :@auto_validate_max_length_columns=>:dup,
168
+ :@auto_validate_max_value_columns=>:dup,
169
+ :@auto_validate_min_value_columns=>:dup,
170
+ :@auto_validate_unique_columns=>:dup,
171
+ :@auto_validate_options => :dup)
125
172
  Plugins.after_set_dataset(self, :setup_auto_validations)
126
173
 
127
174
  # Whether to use a presence validation for not null columns
@@ -136,20 +183,27 @@ module Sequel
136
183
 
137
184
  # Freeze auto_validation settings when freezing model class.
138
185
  def freeze
186
+ @auto_validate_no_null_byte_columns.freeze
139
187
  @auto_validate_not_null_columns.freeze
140
188
  @auto_validate_explicit_not_null_columns.freeze
141
189
  @auto_validate_max_length_columns.freeze
190
+ @auto_validate_max_value_columns.freeze
191
+ @auto_validate_min_value_columns.freeze
142
192
  @auto_validate_unique_columns.freeze
143
193
 
144
194
  super
145
195
  end
146
196
 
147
- # Skip automatic validations for the given validation type (:not_null, :types, :unique).
197
+ # Skip automatic validations for the given validation type
198
+ # (:not_null, :no_null_byte, :types, :unique, :max_length, :max_value, :min_value).
148
199
  # If :all is given as the type, skip all auto validations.
200
+ #
201
+ # Skipping types validation automatically skips max_value and min_value validations,
202
+ # since those validations require valid types.
149
203
  def skip_auto_validations(type)
150
204
  case type
151
205
  when :all
152
- [:not_null, :types, :unique, :max_length].each{|v| skip_auto_validations(v)}
206
+ [:not_null, :no_null_byte, :types, :unique, :max_length, :max_value, :min_value].each{|v| skip_auto_validations(v)}
153
207
  when :not_null
154
208
  auto_validate_not_null_columns.clear
155
209
  auto_validate_explicit_not_null_columns.clear
@@ -169,6 +223,9 @@ module Sequel
169
223
  explicit_not_null_cols += Array(primary_key)
170
224
  @auto_validate_explicit_not_null_columns = explicit_not_null_cols.uniq
171
225
  @auto_validate_max_length_columns = db_schema.select{|col, sch| sch[:type] == :string && sch[:max_length].is_a?(Integer)}.map{|col, sch| [col, sch[:max_length]]}
226
+ @auto_validate_max_value_columns = db_schema.select{|col, sch| sch[:max_value]}.map{|col, sch| [col, sch[:max_value]]}
227
+ @auto_validate_min_value_columns = db_schema.select{|col, sch| sch[:min_value]}.map{|col, sch| [col, sch[:min_value]]}
228
+ @auto_validate_no_null_byte_columns = db_schema.select{|_, sch| sch[:type] == :string}.map{|col, _| col}
172
229
  table = dataset.first_source_table
173
230
  @auto_validate_unique_columns = if db.supports_index_parsing? && [Symbol, SQL::QualifiedIdentifier, SQL::Identifier, String].any?{|c| table.is_a?(c)}
174
231
  db.indexes(table).select{|name, idx| idx[:unique] == true}.map{|name, idx| idx[:columns].length == 1 ? idx[:columns].first : idx[:columns]}
@@ -195,6 +252,9 @@ module Sequel
195
252
  return if skip.include?(:all)
196
253
  opts = model.auto_validate_options
197
254
 
255
+ unless skip.include?(:no_null_byte) || (no_null_byte_columns = model.auto_validate_no_null_byte_columns).empty?
256
+ validates_no_null_byte(no_null_byte_columns, opts[:no_null_byte])
257
+ end
198
258
 
199
259
  unless skip.include?(:not_null)
200
260
  not_null_method = model.auto_validate_presence? ? :validates_presence : :validates_not_null
@@ -214,6 +274,18 @@ module Sequel
214
274
 
215
275
  unless skip.include?(:types) || !model.auto_validate_types?
216
276
  validates_schema_types(keys, opts[:schema_types])
277
+
278
+ unless skip.include?(:max_value) || ((max_value_columns = model.auto_validate_max_value_columns).empty?)
279
+ max_value_columns.each do |col, max|
280
+ validates_max_value(max, col, opts[:max_value])
281
+ end
282
+ end
283
+
284
+ unless skip.include?(:min_value) || ((min_value_columns = model.auto_validate_min_value_columns).empty?)
285
+ min_value_columns.each do |col, min|
286
+ validates_min_value(min, col, opts[:min_value])
287
+ end
288
+ end
217
289
  end
218
290
 
219
291
  unless skip.include?(:unique)
@@ -0,0 +1,68 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Sequel
4
+ module Plugins
5
+ # The auto_validations_constraint_validations_presence_message plugin provides
6
+ # integration for the auto_validations and constraint_validations plugins in
7
+ # the following situation:
8
+ #
9
+ # * A column has a NOT NULL constraint in the database
10
+ # * A constraint validation for presence exists on the column, with a :message
11
+ # option to set a column-specific message, and with the :allow_nil option set
12
+ # to true because the CHECK constraint doesn't need to check for NULL values
13
+ # as the column itself is NOT NULL
14
+ #
15
+ # In this case, by default the validation error message on the column will
16
+ # use the more specific constraint validation error message if the column
17
+ # has a non-NULL empty value, but will use the default auto_validations
18
+ # message if the column has a NULL value. With this plugin, the column-specific
19
+ # constraint validation error message will be used in both cases.
20
+ #
21
+ # Usage:
22
+ #
23
+ # # Make all model subclasses use this auto_validations/constraint_validations
24
+ # # integration (called before loading subclasses)
25
+ # Sequel::Model.plugin :auto_validations_constraint_validations_presence_message
26
+ #
27
+ # # Make the Album class use this auto_validations/constraint_validations integration
28
+ # Album.plugin :auto_validations_constraint_validations_presence_message
29
+ module AutoValidationsConstraintValidationsPresenceMessage
30
+ def self.apply(model)
31
+ model.plugin :auto_validations
32
+ model.plugin :constraint_validations
33
+ end
34
+
35
+ def self.configure(model, opts=OPTS)
36
+ model.send(:_adjust_auto_validations_constraint_validations_presence_message)
37
+ end
38
+
39
+ module ClassMethods
40
+ Plugins.after_set_dataset(self, :_adjust_auto_validations_constraint_validations_presence_message)
41
+
42
+ private
43
+
44
+ def _adjust_auto_validations_constraint_validations_presence_message
45
+ if @dataset &&
46
+ !@auto_validate_options[:not_null][:message] &&
47
+ !@auto_validate_options[:explicit_not_null][:message]
48
+
49
+ @constraint_validations.each do |array|
50
+ meth, column, opts = array
51
+
52
+ if meth == :validates_presence &&
53
+ opts &&
54
+ opts[:message] &&
55
+ opts[:allow_nil] &&
56
+ (@auto_validate_not_null_columns.include?(column) || @auto_validate_explicit_not_null_columns.include?(column))
57
+
58
+ @auto_validate_not_null_columns.delete(column)
59
+ @auto_validate_explicit_not_null_columns.delete(column)
60
+ array[2] = array[2].merge(:allow_nil=>false)
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -104,7 +104,7 @@ module Sequel
104
104
  # should reference the subquery alias (and qualified identifiers should not be needed
105
105
  # unless joining to another table):
106
106
  #
107
- # a = Executive.where(:id=>1).first # works
107
+ # a = Executive.where(id: 1).first # works
108
108
  # a = Executive.where{{employees[:id]=>1}}.first # works
109
109
  # a = Executive.where{{executives[:id]=>1}}.first # doesn't work
110
110
  #
@@ -115,7 +115,7 @@ module Sequel
115
115
  #
116
116
  # pks = Executive.where{num_staff < 10}.select_map(:id)
117
117
  # Executive.cti_tables.reverse_each do |table|
118
- # DB.from(table).where(:id=>pks).delete
118
+ # DB.from(table).where(id: pks).delete
119
119
  # end
120
120
  #
121
121
  # = Usage