sequel 5.39.0 → 5.65.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (190) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +326 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +57 -25
  5. data/bin/sequel +11 -3
  6. data/doc/advanced_associations.rdoc +13 -13
  7. data/doc/association_basics.rdoc +119 -24
  8. data/doc/cheat_sheet.rdoc +11 -3
  9. data/doc/migration.rdoc +12 -6
  10. data/doc/model_hooks.rdoc +1 -1
  11. data/doc/object_model.rdoc +8 -8
  12. data/doc/opening_databases.rdoc +18 -11
  13. data/doc/postgresql.rdoc +16 -8
  14. data/doc/querying.rdoc +5 -3
  15. data/doc/release_notes/5.40.0.txt +40 -0
  16. data/doc/release_notes/5.41.0.txt +25 -0
  17. data/doc/release_notes/5.42.0.txt +136 -0
  18. data/doc/release_notes/5.43.0.txt +98 -0
  19. data/doc/release_notes/5.44.0.txt +32 -0
  20. data/doc/release_notes/5.45.0.txt +34 -0
  21. data/doc/release_notes/5.46.0.txt +87 -0
  22. data/doc/release_notes/5.47.0.txt +59 -0
  23. data/doc/release_notes/5.48.0.txt +14 -0
  24. data/doc/release_notes/5.49.0.txt +59 -0
  25. data/doc/release_notes/5.50.0.txt +78 -0
  26. data/doc/release_notes/5.51.0.txt +47 -0
  27. data/doc/release_notes/5.52.0.txt +87 -0
  28. data/doc/release_notes/5.53.0.txt +23 -0
  29. data/doc/release_notes/5.54.0.txt +27 -0
  30. data/doc/release_notes/5.55.0.txt +21 -0
  31. data/doc/release_notes/5.56.0.txt +51 -0
  32. data/doc/release_notes/5.57.0.txt +23 -0
  33. data/doc/release_notes/5.58.0.txt +31 -0
  34. data/doc/release_notes/5.59.0.txt +73 -0
  35. data/doc/release_notes/5.60.0.txt +22 -0
  36. data/doc/release_notes/5.61.0.txt +43 -0
  37. data/doc/release_notes/5.62.0.txt +132 -0
  38. data/doc/release_notes/5.63.0.txt +33 -0
  39. data/doc/release_notes/5.64.0.txt +50 -0
  40. data/doc/release_notes/5.65.0.txt +21 -0
  41. data/doc/schema_modification.rdoc +1 -1
  42. data/doc/security.rdoc +9 -9
  43. data/doc/sql.rdoc +28 -16
  44. data/doc/testing.rdoc +22 -11
  45. data/doc/transactions.rdoc +6 -6
  46. data/doc/virtual_rows.rdoc +2 -2
  47. data/lib/sequel/adapters/ado/access.rb +1 -1
  48. data/lib/sequel/adapters/ado.rb +17 -17
  49. data/lib/sequel/adapters/amalgalite.rb +3 -5
  50. data/lib/sequel/adapters/ibmdb.rb +2 -2
  51. data/lib/sequel/adapters/jdbc/derby.rb +8 -0
  52. data/lib/sequel/adapters/jdbc/h2.rb +60 -10
  53. data/lib/sequel/adapters/jdbc/hsqldb.rb +6 -0
  54. data/lib/sequel/adapters/jdbc/postgresql.rb +4 -4
  55. data/lib/sequel/adapters/jdbc.rb +16 -18
  56. data/lib/sequel/adapters/mysql.rb +80 -67
  57. data/lib/sequel/adapters/mysql2.rb +54 -49
  58. data/lib/sequel/adapters/odbc.rb +6 -2
  59. data/lib/sequel/adapters/oracle.rb +4 -3
  60. data/lib/sequel/adapters/postgres.rb +83 -40
  61. data/lib/sequel/adapters/shared/access.rb +11 -1
  62. data/lib/sequel/adapters/shared/db2.rb +30 -0
  63. data/lib/sequel/adapters/shared/mssql.rb +58 -7
  64. data/lib/sequel/adapters/shared/mysql.rb +47 -2
  65. data/lib/sequel/adapters/shared/oracle.rb +76 -0
  66. data/lib/sequel/adapters/shared/postgres.rb +418 -174
  67. data/lib/sequel/adapters/shared/sqlanywhere.rb +11 -1
  68. data/lib/sequel/adapters/shared/sqlite.rb +103 -11
  69. data/lib/sequel/adapters/sqlanywhere.rb +1 -1
  70. data/lib/sequel/adapters/sqlite.rb +60 -18
  71. data/lib/sequel/adapters/tinytds.rb +1 -1
  72. data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
  73. data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
  74. data/lib/sequel/ast_transformer.rb +6 -0
  75. data/lib/sequel/connection_pool/sharded_single.rb +5 -7
  76. data/lib/sequel/connection_pool/sharded_threaded.rb +5 -1
  77. data/lib/sequel/connection_pool/single.rb +6 -8
  78. data/lib/sequel/connection_pool/threaded.rb +8 -8
  79. data/lib/sequel/connection_pool/timed_queue.rb +257 -0
  80. data/lib/sequel/connection_pool.rb +47 -30
  81. data/lib/sequel/core.rb +28 -18
  82. data/lib/sequel/database/connecting.rb +26 -2
  83. data/lib/sequel/database/misc.rb +69 -14
  84. data/lib/sequel/database/query.rb +73 -2
  85. data/lib/sequel/database/schema_generator.rb +45 -52
  86. data/lib/sequel/database/schema_methods.rb +17 -1
  87. data/lib/sequel/dataset/actions.rb +107 -13
  88. data/lib/sequel/dataset/features.rb +20 -0
  89. data/lib/sequel/dataset/misc.rb +12 -2
  90. data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
  91. data/lib/sequel/dataset/prepared_statements.rb +2 -0
  92. data/lib/sequel/dataset/query.rb +118 -16
  93. data/lib/sequel/dataset/sql.rb +177 -47
  94. data/lib/sequel/extensions/_model_pg_row.rb +0 -12
  95. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  96. data/lib/sequel/extensions/any_not_empty.rb +1 -1
  97. data/lib/sequel/extensions/async_thread_pool.rb +438 -0
  98. data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
  99. data/lib/sequel/extensions/blank.rb +8 -0
  100. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  101. data/lib/sequel/extensions/core_refinements.rb +36 -11
  102. data/lib/sequel/extensions/date_arithmetic.rb +71 -31
  103. data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
  104. data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
  105. data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -1
  106. data/lib/sequel/extensions/eval_inspect.rb +2 -0
  107. data/lib/sequel/extensions/inflector.rb +9 -1
  108. data/lib/sequel/extensions/is_distinct_from.rb +141 -0
  109. data/lib/sequel/extensions/looser_typecasting.rb +3 -0
  110. data/lib/sequel/extensions/migration.rb +7 -2
  111. data/lib/sequel/extensions/named_timezones.rb +26 -6
  112. data/lib/sequel/extensions/pagination.rb +1 -1
  113. data/lib/sequel/extensions/pg_array.rb +23 -3
  114. data/lib/sequel/extensions/pg_array_ops.rb +2 -2
  115. data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
  116. data/lib/sequel/extensions/pg_enum.rb +1 -1
  117. data/lib/sequel/extensions/pg_extended_date_support.rb +28 -25
  118. data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
  119. data/lib/sequel/extensions/pg_hstore.rb +6 -1
  120. data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
  121. data/lib/sequel/extensions/pg_inet.rb +10 -11
  122. data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
  123. data/lib/sequel/extensions/pg_interval.rb +45 -19
  124. data/lib/sequel/extensions/pg_json.rb +13 -15
  125. data/lib/sequel/extensions/pg_json_ops.rb +73 -2
  126. data/lib/sequel/extensions/pg_loose_count.rb +3 -1
  127. data/lib/sequel/extensions/pg_multirange.rb +367 -0
  128. data/lib/sequel/extensions/pg_range.rb +10 -23
  129. data/lib/sequel/extensions/pg_range_ops.rb +37 -9
  130. data/lib/sequel/extensions/pg_row.rb +19 -13
  131. data/lib/sequel/extensions/pg_row_ops.rb +1 -1
  132. data/lib/sequel/extensions/query.rb +2 -0
  133. data/lib/sequel/extensions/s.rb +2 -1
  134. data/lib/sequel/extensions/schema_dumper.rb +13 -2
  135. data/lib/sequel/extensions/server_block.rb +8 -12
  136. data/lib/sequel/extensions/sql_comments.rb +110 -3
  137. data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
  138. data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
  139. data/lib/sequel/extensions/string_agg.rb +1 -1
  140. data/lib/sequel/extensions/string_date_time.rb +19 -23
  141. data/lib/sequel/extensions/symbol_aref.rb +2 -0
  142. data/lib/sequel/model/associations.rb +345 -101
  143. data/lib/sequel/model/base.rb +51 -27
  144. data/lib/sequel/model/errors.rb +10 -1
  145. data/lib/sequel/model/inflections.rb +1 -1
  146. data/lib/sequel/model/plugins.rb +5 -0
  147. data/lib/sequel/plugins/association_proxies.rb +2 -0
  148. data/lib/sequel/plugins/async_thread_pool.rb +39 -0
  149. data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
  150. data/lib/sequel/plugins/auto_validations.rb +87 -15
  151. data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
  152. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  153. data/lib/sequel/plugins/column_encryption.rb +728 -0
  154. data/lib/sequel/plugins/composition.rb +10 -4
  155. data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
  156. data/lib/sequel/plugins/constraint_validations.rb +2 -1
  157. data/lib/sequel/plugins/dataset_associations.rb +4 -1
  158. data/lib/sequel/plugins/dirty.rb +1 -1
  159. data/lib/sequel/plugins/enum.rb +124 -0
  160. data/lib/sequel/plugins/finder.rb +4 -2
  161. data/lib/sequel/plugins/insert_conflict.rb +4 -0
  162. data/lib/sequel/plugins/instance_specific_default.rb +1 -1
  163. data/lib/sequel/plugins/json_serializer.rb +39 -24
  164. data/lib/sequel/plugins/lazy_attributes.rb +3 -0
  165. data/lib/sequel/plugins/list.rb +3 -1
  166. data/lib/sequel/plugins/many_through_many.rb +109 -10
  167. data/lib/sequel/plugins/nested_attributes.rb +12 -7
  168. data/lib/sequel/plugins/pg_array_associations.rb +56 -38
  169. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +3 -1
  170. data/lib/sequel/plugins/prepared_statements.rb +10 -1
  171. data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
  172. data/lib/sequel/plugins/rcte_tree.rb +27 -19
  173. data/lib/sequel/plugins/require_valid_schema.rb +67 -0
  174. data/lib/sequel/plugins/serialization.rb +9 -3
  175. data/lib/sequel/plugins/serialization_modification_detection.rb +2 -1
  176. data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
  177. data/lib/sequel/plugins/sql_comments.rb +189 -0
  178. data/lib/sequel/plugins/static_cache.rb +1 -1
  179. data/lib/sequel/plugins/subclasses.rb +28 -11
  180. data/lib/sequel/plugins/tactical_eager_loading.rb +23 -10
  181. data/lib/sequel/plugins/timestamps.rb +1 -1
  182. data/lib/sequel/plugins/unused_associations.rb +521 -0
  183. data/lib/sequel/plugins/update_or_create.rb +1 -1
  184. data/lib/sequel/plugins/validate_associated.rb +22 -12
  185. data/lib/sequel/plugins/validation_helpers.rb +38 -11
  186. data/lib/sequel/plugins/xml_serializer.rb +1 -1
  187. data/lib/sequel/sql.rb +1 -1
  188. data/lib/sequel/timezones.rb +12 -14
  189. data/lib/sequel/version.rb +1 -1
  190. metadata +101 -43
@@ -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
@@ -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