sequel 5.34.0 → 5.35.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +24 -0
  3. data/doc/association_basics.rdoc +7 -2
  4. data/doc/dataset_filtering.rdoc +2 -2
  5. data/doc/release_notes/5.35.0.txt +56 -0
  6. data/lib/sequel/adapters/oracle.rb +2 -1
  7. data/lib/sequel/adapters/shared/sqlite.rb +8 -2
  8. data/lib/sequel/adapters/tinytds.rb +1 -0
  9. data/lib/sequel/dataset/query.rb +1 -1
  10. data/lib/sequel/deprecated.rb +1 -1
  11. data/lib/sequel/extensions/_pretty_table.rb +1 -2
  12. data/lib/sequel/extensions/columns_introspection.rb +1 -2
  13. data/lib/sequel/extensions/core_refinements.rb +2 -0
  14. data/lib/sequel/extensions/duplicate_columns_handler.rb +2 -0
  15. data/lib/sequel/extensions/migration.rb +0 -1
  16. data/lib/sequel/extensions/pg_array_ops.rb +4 -0
  17. data/lib/sequel/extensions/pg_enum.rb +2 -0
  18. data/lib/sequel/extensions/pg_extended_date_support.rb +1 -1
  19. data/lib/sequel/extensions/pg_hstore_ops.rb +2 -0
  20. data/lib/sequel/extensions/pg_inet.rb +2 -0
  21. data/lib/sequel/extensions/pg_json_ops.rb +2 -0
  22. data/lib/sequel/extensions/pg_range.rb +3 -7
  23. data/lib/sequel/extensions/pg_range_ops.rb +2 -0
  24. data/lib/sequel/extensions/pg_row.rb +0 -1
  25. data/lib/sequel/extensions/run_transaction_hooks.rb +1 -1
  26. data/lib/sequel/extensions/s.rb +2 -0
  27. data/lib/sequel/extensions/symbol_aref_refinement.rb +2 -0
  28. data/lib/sequel/extensions/symbol_as_refinement.rb +2 -0
  29. data/lib/sequel/extensions/to_dot.rb +9 -3
  30. data/lib/sequel/model/associations.rb +24 -7
  31. data/lib/sequel/model/base.rb +7 -3
  32. data/lib/sequel/plugins/association_pks.rb +3 -2
  33. data/lib/sequel/plugins/blacklist_security.rb +1 -2
  34. data/lib/sequel/plugins/class_table_inheritance.rb +3 -3
  35. data/lib/sequel/plugins/csv_serializer.rb +2 -0
  36. data/lib/sequel/plugins/forbid_lazy_load.rb +2 -0
  37. data/lib/sequel/plugins/instance_specific_default.rb +113 -0
  38. data/lib/sequel/plugins/lazy_attributes.rb +1 -1
  39. data/lib/sequel/plugins/pg_array_associations.rb +2 -3
  40. data/lib/sequel/plugins/prepared_statements.rb +5 -11
  41. data/lib/sequel/plugins/prepared_statements_safe.rb +1 -3
  42. data/lib/sequel/plugins/rcte_tree.rb +8 -14
  43. data/lib/sequel/plugins/string_stripper.rb +1 -1
  44. data/lib/sequel/plugins/validation_class_methods.rb +5 -1
  45. data/lib/sequel/version.rb +1 -1
  46. metadata +5 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6c87a14904ac28bee61ed778628cc68f7c7318d6cb2f7c1f8aeeaab0ac2e7f22
4
- data.tar.gz: 3fdbda6fce44c6a0d168cc7fe0fe3a2e269ef668397ec1f4289120231006e51f
3
+ metadata.gz: 7ac0d24707f9029714f4e5258c484148d7dc10a733c0c33865d252141cd4cc2e
4
+ data.tar.gz: cdabb1bb51fdfe7b7a22df0a6a9102dd15366bbdd98100907b2e3d74db4e5878
5
5
  SHA512:
6
- metadata.gz: 012717171d1b9aded574e16ba2670c95ccb87ef365b68bcee18b1e6730e5ce04425eaeca3be563c5ba416d2691ed83526bcb5254275b6484526856d0cadf14d1
7
- data.tar.gz: 02f430fa24d8b61905192a8f387421b28d877978fcd5ce05fdb5f0768b93a6f7771c02ed445c937863b30f24121ad9fae73c8303293bc7475621f077792b3f78
6
+ metadata.gz: d87a7d026c51d8897c964cca01ad30384b853bdbbda42811ee7245ef669ac23c1093e572a0e414db5daeb84b8f417e63c14c3ac13aa0b12ac9eb8f801ea6e6c2
7
+ data.tar.gz: 3e788cabe74d01035b930ca699b5ebc763a6a34410c1b8a3a5075d5a276c5155355d9476303b6b606d1b0a74d6a535ca4fdca696cdc0b83e32658be45c8f946e
data/CHANGELOG CHANGED
@@ -1,3 +1,27 @@
1
+ === 5.35.0 (2020-08-01)
2
+
3
+ * Recognize another disconnect error in the oracle adapter (sterlzbd) (#1705)
4
+
5
+ * Consider all associations with :dataset options as instance-specific associations (jeremyevans)
6
+
7
+ * Make Model.finalize_associations not break with instance-specific associations (jeremyevans)
8
+
9
+ * Make association placeholder loader consider block if instance_specific: false association option is used (jeremyevans)
10
+
11
+ * Copy composite unique constraints when emulating alter table operations on SQLite (jeremyevans) (#1704)
12
+
13
+ * Add instance_specific_default plugin for setting default association :instance_specific value, or warning/raising for cases where it is not specified (jeremyevans)
14
+
15
+ * Make Model.plugin issue deprecation warning if loading plugin with arguments and block if plugin does not accept arguments/block (jeremyevans)
16
+
17
+ * Make validation_class_methods consider all :if, :allow_missing, :allow_nil, and :allow_blank settings, instead of just the first (jeremyevans)
18
+
19
+ * Include hash entries with nil keys in Dataset#to_dot output in to_dot extension (jeremyevans)
20
+
21
+ * Remove unneeded conditionals from plugins and extensions (jeremyevans)
22
+
23
+ * Fix exception class in run_transaction_hooks extension if calling run_after_{commit,rollback}_hooks outside of a transaction (jeremyevans)
24
+
1
25
  === 5.34.0 (2020-07-01)
2
26
 
3
27
  * Make eager_graph work correctly if called with no associations (jeremyevans)
@@ -1676,11 +1676,16 @@ instances.
1676
1676
  ==== :instance_specific
1677
1677
 
1678
1678
  This allows you to override the setting of whether the dataset contains instance
1679
- specific code. For example, if you are passing a block to the association,
1679
+ specific code. If you are passing a block to the association,
1680
1680
  Sequel sets this to true by default, which disables some optimizations that
1681
1681
  would be invalid if the association is instance specific. If you know that the
1682
1682
  block does not contain instance specific code, you can set this to false to
1683
- reenable the optimizations.
1683
+ reenable the optimizations. Instance specific code is mostly commonly calling
1684
+ model instance methods inside an association block, but also
1685
+ includes cases where the association block can return different values based
1686
+ on the runtime environment, such as calls to <tt>Time.now</tt> in the block.
1687
+ Associations that use the :dataset option are always considered instance specific,
1688
+ even if explicitly specified otherwise.
1684
1689
 
1685
1690
  ==== :cartesian_product_number
1686
1691
 
@@ -36,8 +36,8 @@ Ranges (both inclusive and exclusive) can also be used:
36
36
 
37
37
  If you need to select multiple items from a dataset, you can supply an array:
38
38
 
39
- items.where(id: [1, 38, 47, 99]).sql
40
- # "SELECT * FROM items WHERE (id IN (1, 38, 47, 99))"
39
+ items.where(id: [1, 38, 47, 99]).sql
40
+ # "SELECT * FROM items WHERE (id IN (1, 38, 47, 99))"
41
41
 
42
42
  == Filtering using expressions
43
43
 
@@ -0,0 +1,56 @@
1
+ = New Features
2
+
3
+ * An instance_specific_default plugin has been added for setting the
4
+ default for the :instance_specific association option, or
5
+ warning/raises in cases where it is not specified. This allows
6
+ you to easily find associations that would be considering instance
7
+ specific by default, and mark them as not instance specific for
8
+ better performance.
9
+
10
+ = Other Improvements
11
+
12
+ * Setting the :instance_specific association option to false now
13
+ works correctly if the association uses a block. Associations
14
+ that set the :dataset option are now always considered instance
15
+ specific, even if the :instance_specific option is explicitly
16
+ passed.
17
+
18
+ * The validation_class_methods plugin now considers all :if,
19
+ :allow_missing, :allow_nil, and :allow_blank options. Previously,
20
+ it only considered the first of those options that was set.
21
+
22
+ * Model.finalize_associations no longer breaks if you have
23
+ instance-specific associations.
24
+
25
+ * Model.plugin now warns if you load the plugin with arguments or a
26
+ block if the plugin does not accept arguments or block. This is
27
+ because a future change to Sequel could break the call.
28
+
29
+ * When emulating unsupported alter table operations on SQLite, Sequel
30
+ now copies composite unique constraints unless the alter table
31
+ operation is the dropping of a unique constraint.
32
+
33
+ * Sequel now recognizes an additional disconnect error in the oracle
34
+ adapter.
35
+
36
+ * In the run_transaction_hooks extension, calling
37
+ run_after_{commit,rollback}_hooks now raises the correct exception
38
+ class.
39
+
40
+ * In the pg_range extension, conversion procs for the tsrange[] and
41
+ tstzrange[] types are not added unless the Database uses the
42
+ pg_array extension.
43
+
44
+ * Multiple unnecessary conditionals in plugins and extensions have
45
+ been removed.
46
+
47
+ * Sequel plugin and extension code now have 100% branch coverage.
48
+
49
+ * Sequel now avoids a statement not reached verbose warning in
50
+ Dataset#clone.
51
+
52
+ = Backwards Compatibility
53
+
54
+ * The output of Dataset#to_dot in the to_dot extension has changed
55
+ slightly, including hash entries with nil keys. These entries
56
+ were previously ignored.
@@ -14,7 +14,8 @@ module Sequel
14
14
  # ORA-02396: exceeded maximum idle time, please connect again
15
15
  # ORA-03113: end-of-file on communication channel
16
16
  # ORA-03114: not connected to ORACLE
17
- CONNECTION_ERROR_CODES = [ 28, 1012, 2396, 3113, 3114 ].freeze
17
+ # ORA-03135: connection lost contact
18
+ CONNECTION_ERROR_CODES = [ 28, 1012, 2396, 3113, 3114, 3135 ].freeze
18
19
 
19
20
  ORACLE_TYPES = {
20
21
  :blob=>lambda{|b| Sequel::SQL::Blob.new(b.read)},
@@ -269,6 +269,8 @@ module Sequel
269
269
  else
270
270
  duplicate_table(table, :no_foreign_keys=>true)
271
271
  end
272
+ when :unique
273
+ duplicate_table(table, :no_unique=>true)
272
274
  else
273
275
  duplicate_table(table)
274
276
  end
@@ -422,8 +424,12 @@ module Sequel
422
424
  skip_indexes = []
423
425
  indexes(table, :only_autocreated=>true).each do |name, h|
424
426
  skip_indexes << name
425
- if h[:columns].length == 1 && h[:unique]
426
- unique_columns.concat(h[:columns])
427
+ if h[:unique]
428
+ if h[:columns].length == 1
429
+ unique_columns.concat(h[:columns])
430
+ elsif h[:columns].map(&:to_s) != pks && !opts[:no_unique]
431
+ constraints << {:type=>:unique, :columns=>h[:columns]}
432
+ end
427
433
  end
428
434
  end
429
435
  unique_columns -= pks
@@ -16,6 +16,7 @@ module Sequel
16
16
  c = TinyTds::Client.new(opts)
17
17
  c.query_options.merge!(:cache_rows=>false)
18
18
 
19
+ # SEQUEL6: Default to ansi: true
19
20
  if opts[:ansi]
20
21
  sql = %w(
21
22
  ANSI_NULLS
@@ -81,7 +81,7 @@ module Sequel
81
81
  # If the options changed include options in COLUMN_CHANGE_OPTS, the cached
82
82
  # columns are deleted. This method should generally not be called
83
83
  # directly by user code.
84
- def clone(opts = (return self; nil))
84
+ def clone(opts = nil || (return self))
85
85
  # return self used above because clone is called by almost all
86
86
  # other query methods, and it is the fastest approach
87
87
  c = super(:freeze=>false)
@@ -39,7 +39,7 @@ module Sequel
39
39
  # Print the message and possibly backtrace to the output.
40
40
  def self.deprecate(method, instead=nil)
41
41
  return unless output
42
- message = instead ? "#{method} is deprecated and will be removed in Sequel 5.1. #{instead}." : method
42
+ message = instead ? "#{method} is deprecated and will be removed in Sequel 6. #{instead}." : method
43
43
  message = "#{prefix}#{message}" if prefix
44
44
  output.puts(message)
45
45
  case b = backtrace_filter
@@ -41,8 +41,7 @@ module Sequel
41
41
  def self.column_sizes(records, columns) # :nodoc:
42
42
  sizes = Hash.new {0}
43
43
  columns.each do |c|
44
- s = c.to_s.size
45
- sizes[c] = s if s > sizes[c]
44
+ sizes[c] = c.to_s.size
46
45
  end
47
46
  records.each do |r|
48
47
  columns.each do |c|
@@ -75,8 +75,7 @@ module Sequel
75
75
  when SQL::Identifier
76
76
  c.value.to_sym
77
77
  when SQL::QualifiedIdentifier
78
- col = c.column
79
- col.is_a?(SQL::Identifier) ? col.value.to_sym : col.to_sym
78
+ c.column.to_sym
80
79
  when SQL::AliasedExpression
81
80
  a = c.alias
82
81
  a.is_a?(SQL::Identifier) ? a.value.to_sym : a.to_sym
@@ -10,7 +10,9 @@
10
10
  #
11
11
  # using Sequel::CoreRefinements
12
12
 
13
+ # :nocov:
13
14
  raise(Sequel::Error, "Refinements require ruby 2.0.0 or greater") unless RUBY_VERSION >= '2.0.0'
15
+ # :nocov:
14
16
 
15
17
  module Sequel::CoreRefinements
16
18
  refine Array do
@@ -39,7 +39,9 @@
39
39
 
40
40
  module Sequel
41
41
  module DuplicateColumnsHandler
42
+ # :nocov:
42
43
  CALLER_ARGS = (RUBY_VERSION >= '2.0' ? [0,1] : [0]).freeze
44
+ # :nocov:
43
45
 
44
46
  # Customize handling of duplicate columns for this dataset.
45
47
  def on_duplicate_columns(handler = (raise Error, "Must provide either an argument or a block to on_duplicate_columns" unless block_given?; nil), &block)
@@ -518,7 +518,6 @@ module Sequel
518
518
  def initialize(db, directory, opts=OPTS)
519
519
  super
520
520
  @current = opts[:current] || current_migration_version
521
- raise(Error, "No current version available") unless current
522
521
 
523
522
  latest_version = latest_migration_version
524
523
  @target = if opts[:target]
@@ -157,7 +157,9 @@ module Sequel
157
157
  else
158
158
  Sequel.function(:hstore, self, wrap_array(arg))
159
159
  end
160
+ # :nocov:
160
161
  if Sequel.respond_to?(:hstore_op)
162
+ # :nocov:
161
163
  v = Sequel.hstore_op(v)
162
164
  end
163
165
  v
@@ -283,7 +285,9 @@ module Sequel
283
285
  end
284
286
  end
285
287
 
288
+ # :nocov:
286
289
  if defined?(PGArray)
290
+ # :nocov:
287
291
  class PGArray
288
292
  # Wrap the PGArray instance in an ArrayOp, allowing you to easily use
289
293
  # the PostgreSQL array functions and operators with literal arrays.
@@ -181,7 +181,9 @@ module Sequel
181
181
  end
182
182
 
183
183
  # support reversible create_enum statements if the migration extension is loaded
184
+ # :nocov:
184
185
  if defined?(MigrationReverser)
186
+ # :nocov:
185
187
  class MigrationReverser
186
188
  private
187
189
  def create_enum(name, _)
@@ -189,7 +189,7 @@ module Sequel
189
189
  if date < DATETIME_YEAR_1
190
190
  date <<= ((date.year) * 24 - 12)
191
191
  date = db.from_application_timestamp(date)
192
- minutes = (date.is_a?(DateTime) ? date.offset * 1440 : date.utc_offset/60).to_i
192
+ minutes = (date.offset * 1440).to_i
193
193
  date.strftime("'%Y-%m-%d %H:%M:%S.%N#{format_timestamp_offset(*minutes.divmod(60))} BC'")
194
194
  else
195
195
  super
@@ -312,7 +312,9 @@ module Sequel
312
312
  end
313
313
  end
314
314
 
315
+ # :nocov:
315
316
  if defined?(HStore)
317
+ # :nocov:
316
318
  class HStore
317
319
  # Wrap the receiver in an HStoreOp so you can easily use the PostgreSQL
318
320
  # hstore functions and operators with it.
@@ -41,7 +41,9 @@ module Sequel
41
41
  db.instance_exec do
42
42
  extend_datasets(InetDatasetMethods)
43
43
 
44
+ # :nocov:
44
45
  if !defined?(SEQUEL_PG_VERSION_INTEGER) || SEQUEL_PG_VERSION_INTEGER >= 11300
46
+ # :nocov:
45
47
  # sequel_pg 1.13.0+ will use inet/cidr conversion procs, but doing so is
46
48
  # slower, so don't add the conversion procs if using sequel_pg 1.13.0+.
47
49
  meth = IPAddr.method(:new)
@@ -554,7 +554,9 @@ module Sequel
554
554
  end
555
555
  end
556
556
 
557
+ # :nocov:
557
558
  if defined?(JSONArray)
559
+ # :nocov:
558
560
  class JSONArray
559
561
  # Wrap the JSONArray instance in an JSONOp, allowing you to easily use
560
562
  # the PostgreSQL json functions and operators with literal jsons.
@@ -158,7 +158,7 @@ module Sequel
158
158
  procs = conversion_procs
159
159
  add_conversion_proc(3908, Parser.new("tsrange", procs[1114]))
160
160
  add_conversion_proc(3910, Parser.new("tstzrange", procs[1184]))
161
- if defined?(PGArray::Creator)
161
+ if respond_to?(:register_array_type) && defined?(PGArray::Creator)
162
162
  add_conversion_proc(3909, PGArray::Creator.new("tsrange", procs[3908]))
163
163
  add_conversion_proc(3911, PGArray::Creator.new("tstzrange", procs[3910]))
164
164
  end
@@ -215,12 +215,6 @@ module Sequel
215
215
 
216
216
  db_type = db_type.to_s.dup.freeze
217
217
 
218
- if converter = opts[:converter]
219
- raise Error, "can't provide both a block and :converter option to register" if block
220
- else
221
- converter = block
222
- end
223
-
224
218
  if soid
225
219
  raise Error, "can't provide both a converter and :subtype_oid option to register" if has_converter
226
220
  raise Error, "no conversion proc for :subtype_oid=>#{soid.inspect} in conversion_procs" unless converter = conversion_procs[soid]
@@ -471,8 +465,10 @@ module Sequel
471
465
  return @range if @range
472
466
  raise(Error, "cannot create ruby range for an empty PostgreSQL range") if empty?
473
467
  raise(Error, "cannot create ruby range when PostgreSQL range excludes beginning element") if exclude_begin?
468
+ # :nocov:
474
469
  raise(Error, "cannot create ruby range when PostgreSQL range has unbounded beginning") if STARTLESS_RANGE_NOT_SUPPORTED && !self.begin
475
470
  raise(Error, "cannot create ruby range when PostgreSQL range has unbounded ending") if ENDLESS_RANGE_NOT_SUPPORTED && !self.end
471
+ # :nocov:
476
472
  @range = Range.new(self.begin, self.end, exclude_end?)
477
473
  end
478
474
 
@@ -116,7 +116,9 @@ module Sequel
116
116
  end
117
117
  end
118
118
 
119
+ # :nocov:
119
120
  if defined?(PGRange)
121
+ # :nocov:
120
122
  class PGRange
121
123
  # Wrap the PGRange instance in an RangeOp, allowing you to easily use
122
124
  # the PostgreSQL range functions and operators with literal ranges.
@@ -222,7 +222,6 @@ module Sequel
222
222
  # Split the stored string into an array of strings, handling
223
223
  # the different types of quoting.
224
224
  def parse
225
- return @result if @result
226
225
  values = []
227
226
  skip(/\(/)
228
227
  if skip(/\)/)
@@ -48,7 +48,7 @@ class Sequel::Database
48
48
  def _run_transaction_hooks(type, opts)
49
49
  synchronize(opts[:server]) do |conn|
50
50
  unless h = _trans(conn)
51
- raise Error, "Cannot call run_#{type}_hooks outside of a transaction"
51
+ raise Sequel::Error, "Cannot call run_#{type}_hooks outside of a transaction"
52
52
  end
53
53
 
54
54
  if hooks = h[type]
@@ -49,7 +49,9 @@ module Sequel::S
49
49
  Sequel.expr(*a, &block)
50
50
  end
51
51
 
52
+ # :nocov:
52
53
  if RUBY_VERSION >= '2.0.0'
54
+ # :nocov:
53
55
  refine Object do
54
56
  include Sequel::S
55
57
  end
@@ -25,7 +25,9 @@
25
25
  #
26
26
  # Related module: Sequel::SymbolAref
27
27
 
28
+ # :nocov:
28
29
  raise(Sequel::Error, "Refinements require ruby 2.0.0 or greater") unless RUBY_VERSION >= '2.0.0'
30
+ # :nocov:
29
31
 
30
32
  module Sequel::SymbolAref
31
33
  refine Symbol do
@@ -23,7 +23,9 @@
23
23
  #
24
24
  # Related module: Sequel::SymbolAs
25
25
 
26
+ # :nocov:
26
27
  raise(Sequel::Error, "Refinements require ruby 2.0.0 or greater") unless RUBY_VERSION >= '2.0.0'
28
+ # :nocov:
27
29
 
28
30
  module Sequel::SymbolAs
29
31
  refine Symbol do
@@ -53,7 +53,13 @@ module Sequel
53
53
  # is given, it is used directly as the node or transition. Otherwise
54
54
  # a node is created for the current object.
55
55
  def dot(label, j=nil)
56
- @dot << "#{j||@i} [label=#{label.to_s.inspect}];"
56
+ label = case label
57
+ when nil
58
+ "<nil>"
59
+ else
60
+ label.to_s
61
+ end
62
+ @dot << "#{j||@i} [label=#{label.inspect}];"
57
63
  end
58
64
 
59
65
  # Recursive method that parses all of Sequel's internal datastructures,
@@ -61,7 +67,7 @@ module Sequel
61
67
  # structure.
62
68
  def v(e, l)
63
69
  @i += 1
64
- dot(l, "#{@stack.last} -> #{@i}") if l
70
+ dot(l, "#{@stack.last} -> #{@i}")
65
71
  @stack.push(@i)
66
72
  case e
67
73
  when LiteralString
@@ -144,7 +150,7 @@ module Sequel
144
150
  dot "Dataset"
145
151
  TO_DOT_OPTIONS.each do |k|
146
152
  if val = e.opts[k]
147
- v(val, k.to_s)
153
+ v(val, k)
148
154
  end
149
155
  end
150
156
  else
@@ -356,7 +356,7 @@ module Sequel
356
356
  def finalize
357
357
  return unless cache = self[:cache]
358
358
 
359
- finalize_settings.each do |meth, key|
359
+ finalizer = proc do |meth, key|
360
360
  next if has_key?(key)
361
361
 
362
362
  # Allow calling private methods to make sure caching is done appropriately
@@ -364,6 +364,13 @@ module Sequel
364
364
  self[key] = cache.delete(key) if cache.has_key?(key)
365
365
  end
366
366
 
367
+ finalize_settings.each(&finalizer)
368
+
369
+ unless self[:instance_specific]
370
+ finalizer.call(:associated_eager_dataset, :associated_eager_dataset)
371
+ finalizer.call(:filter_by_associations_conditions_dataset, :filter_by_associations_conditions_dataset)
372
+ end
373
+
367
374
  nil
368
375
  end
369
376
 
@@ -371,9 +378,7 @@ module Sequel
371
378
  FINALIZE_SETTINGS = {
372
379
  :associated_class=>:class,
373
380
  :associated_dataset=>:_dataset,
374
- :associated_eager_dataset=>:associated_eager_dataset,
375
381
  :eager_limit_strategy=>:_eager_limit_strategy,
376
- :filter_by_associations_conditions_dataset=>:filter_by_associations_conditions_dataset,
377
382
  :placeholder_loader=>:placeholder_loader,
378
383
  :predicate_key=>:predicate_key,
379
384
  :predicate_keys=>:predicate_keys,
@@ -432,7 +437,11 @@ module Sequel
432
437
  if use_placeholder_loader?
433
438
  cached_fetch(:placeholder_loader) do
434
439
  Sequel::Dataset::PlaceholderLiteralizer.loader(associated_dataset) do |pl, ds|
435
- ds.where(Sequel.&(*predicate_keys.map{|k| SQL::BooleanExpression.new(:'=', k, pl.arg)}))
440
+ ds = ds.where(Sequel.&(*predicate_keys.map{|k| SQL::BooleanExpression.new(:'=', k, pl.arg)}))
441
+ if self[:block]
442
+ ds = self[:block].call(ds)
443
+ end
444
+ ds
436
445
  end
437
446
  end
438
447
  end
@@ -796,7 +805,7 @@ module Sequel
796
805
 
797
806
  # Whether the placeholder loader can be used to load the association.
798
807
  def use_placeholder_loader?
799
- !self[:instance_specific] && !self[:eager_graph]
808
+ self[:use_placeholder_loader]
800
809
  end
801
810
  end
802
811
 
@@ -1793,11 +1802,12 @@ module Sequel
1793
1802
  opts.merge!(:type => type, :name => name, :cache=>({} if cache_associations), :model => self)
1794
1803
 
1795
1804
  opts[:block] = block if block
1796
- if !opts.has_key?(:instance_specific) && (block || orig_opts[:block] || orig_opts[:dataset])
1805
+ opts[:instance_specific] = true if orig_opts[:dataset]
1806
+ if !opts.has_key?(:instance_specific) && (block || orig_opts[:block])
1797
1807
  # It's possible the association is instance specific, in that it depends on
1798
1808
  # values other than the foreign key value. This needs to be checked for
1799
1809
  # in certain places to disable optimizations.
1800
- opts[:instance_specific] = true
1810
+ opts[:instance_specific] = _association_instance_specific_default(name)
1801
1811
  end
1802
1812
  opts = assoc_class.new.merge!(opts)
1803
1813
 
@@ -1805,6 +1815,7 @@ module Sequel
1805
1815
  raise(Error, "cannot clone an association to an association of different type (association #{name} with type #{type} cloning #{opts[:clone]} with type #{cloned_assoc[:type]})")
1806
1816
  end
1807
1817
 
1818
+ opts[:use_placeholder_loader] = !opts[:instance_specific] && !opts[:eager_graph]
1808
1819
  opts[:eager_block] = opts[:block] unless opts.include?(:eager_block)
1809
1820
  opts[:graph_join_type] ||= :left_outer
1810
1821
  opts[:order_eager_graph] = true unless opts.include?(:order_eager_graph)
@@ -1901,6 +1912,12 @@ module Sequel
1901
1912
  Plugins.def_dataset_methods(self, [:eager, :eager_graph, :eager_graph_with_options, :association_join, :association_full_join, :association_inner_join, :association_left_join, :association_right_join])
1902
1913
 
1903
1914
  private
1915
+
1916
+ # The default value for the instance_specific option, if the association
1917
+ # could be instance specific and the :instance_specific option is not specified.
1918
+ def _association_instance_specific_default(_)
1919
+ true
1920
+ end
1904
1921
 
1905
1922
  # The module to use for the association's methods. Defaults to
1906
1923
  # the overridable_methods_module.
@@ -491,6 +491,11 @@ module Sequel
491
491
  # the module using a the camelized plugin name under Sequel::Plugins.
492
492
  def plugin(plugin, *args, &block)
493
493
  m = plugin.is_a?(Module) ? plugin : plugin_module(plugin)
494
+
495
+ if !m.respond_to?(:apply) && !m.respond_to?(:configure) && (!args.empty? || block)
496
+ Deprecation.deprecate("Plugin #{plugin} accepts no arguments or block, and passing arguments/block to it", "Remove arguments and block when loading the plugin")
497
+ end
498
+
494
499
  unless @plugins.include?(m)
495
500
  @plugins << m
496
501
  m.apply(self, *args, &block) if m.respond_to?(:apply)
@@ -500,6 +505,7 @@ module Sequel
500
505
  dataset_extend(m::DatasetMethods, :create_class_methods=>false)
501
506
  end
502
507
  end
508
+
503
509
  m.configure(self, *args, &block) if m.respond_to?(:configure)
504
510
  end
505
511
 
@@ -632,8 +638,7 @@ module Sequel
632
638
 
633
639
  # Cache of setter methods to allow by default, in order to speed up mass assignment.
634
640
  def setter_methods
635
- return @setter_methods if @setter_methods
636
- @setter_methods = get_setter_methods
641
+ @setter_methods || (@setter_methods = get_setter_methods)
637
642
  end
638
643
 
639
644
  # Returns name of primary table for the dataset. If the table for the dataset
@@ -1988,7 +1993,6 @@ module Sequel
1988
1993
 
1989
1994
  # Get the ruby class or classes related to the given column's type.
1990
1995
  def schema_type_class(column)
1991
- # SEQUEL6: Remove
1992
1996
  if (sch = db_schema[column]) && (type = sch[:type])
1993
1997
  db.schema_type_class(type)
1994
1998
  end
@@ -295,9 +295,10 @@ module Sequel
295
295
 
296
296
  if primary_key.is_a?(Array)
297
297
  if (cols = sch.values_at(*klass.primary_key)).all? && (convs = cols.map{|c| c[:type] == :integer}).all?
298
+ db = model.db
298
299
  pks.map do |cpk|
299
- cpk.zip(convs).map do |pk, conv|
300
- conv ? model.db.typecast_value(:integer, pk) : pk
300
+ cpk.map do |pk|
301
+ db.typecast_value(:integer, pk)
301
302
  end
302
303
  end
303
304
  else
@@ -58,8 +58,7 @@ module Sequel
58
58
  # restricted_columns.
59
59
  def get_setter_methods
60
60
  meths = super
61
- #if !(respond_to?(:allowed_columns) && allowed_columns) && restricted_columns
62
- if (!defined?(::Sequel::Plugins::WhitelistSecurity) || !plugins.include?(::Sequel::Plugins::WhitelistSecurity) || !allowed_columns) && restricted_columns
61
+ if (!defined?(::Sequel::Plugins::WhitelistSecurity::ClassMethods) || !is_a?(::Sequel::Plugins::WhitelistSecurity::ClassMethods) || !allowed_columns) && restricted_columns
63
62
  meths -= restricted_columns.map{|x| "#{x}="}
64
63
  end
65
64
  meths
@@ -289,7 +289,7 @@ module Sequel
289
289
 
290
290
  # The name of the most recently joined table.
291
291
  def cti_table_name
292
- cti_tables ? cti_tables.last : dataset.first_source_alias
292
+ cti_tables.last
293
293
  end
294
294
 
295
295
  # The model class for the given key value.
@@ -310,7 +310,7 @@ module Sequel
310
310
  # Set table if this is a class table inheritance
311
311
  table = nil
312
312
  columns = nil
313
- if (n = subclass.name) && !n.empty?
313
+ if n = subclass.name
314
314
  if table = cti_table_map[n.to_sym]
315
315
  columns = db.schema(table).map(&:first)
316
316
  else
@@ -417,7 +417,7 @@ module Sequel
417
417
  @values[primary_key] ||= nid
418
418
  end
419
419
  end
420
- db.dataset.supports_insert_select? ? nil : @values[primary_key]
420
+ @values[primary_key]
421
421
  end
422
422
 
423
423
  # Update rows in all backing tables, using the columns in each table.
@@ -82,11 +82,13 @@ module Sequel
82
82
  end
83
83
  END
84
84
  else
85
+ # :nocov:
85
86
  # :nodoc:
86
87
  def self.csv_call(*args, opts, &block)
87
88
  CSV.send(*args, opts, &block)
88
89
  end
89
90
  # :nodoc:
91
+ # :nocov:
90
92
  end
91
93
 
92
94
  module ClassMethods
@@ -111,7 +111,9 @@ module Sequel
111
111
  # an association, allow lazy loading that association, since the
112
112
  # lazy association load will use a hash table lookup and not a query.
113
113
  def allow_lazy_load_for_static_cache_associations
114
+ # :nocov:
114
115
  if defined?(::Sequel::Plugins::StaticCache::ClassMethods)
116
+ # :nocov:
115
117
  @association_reflections.each_value do |ref|
116
118
  if ref.associated_class.is_a?(::Sequel::Plugins::StaticCache::ClassMethods)
117
119
  ref[:forbid_lazy_load] = false
@@ -0,0 +1,113 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Sequel
4
+ module Plugins
5
+ # The instance_specific_default plugin exists to make it easier to use a
6
+ # global :instance_specific association option, or to warn or raise when Sequel
7
+ # has to guess which value to use :instance_specific option (Sequel defaults to
8
+ # guessing true as that is the conservative setting). It is helpful to
9
+ # use this plugin, particularly with the :warn or :raise settings, to determine
10
+ # which associations should have :instance_specific set. Setting the
11
+ # :instance_specific to false for associations that are not instance specific
12
+ # can improve performance.
13
+ #
14
+ # Associations are instance-specific if their block calls
15
+ # a model instance method, or where the value of the block varies
16
+ # based on runtime state, and the variance is outside of a delayed evaluation.
17
+ # For example, with the following three associations:
18
+ #
19
+ # Album.one_to_one :first_track, class: :Track do |ds|
20
+ # ds.where(number: 1)
21
+ # end
22
+ #
23
+ # Album.one_to_one :last_track, class: :Track do |ds|
24
+ # ds.where(number: num_tracks)
25
+ # end
26
+ #
27
+ # Album.one_to_many :recent_tracks, class: :Track do |ds|
28
+ # ds.where{date_updated > Date.today - 10}
29
+ # end
30
+ #
31
+ # +first_track+ is not instance specific, but +last_track+ and +recent_tracks+ are.
32
+ # +last_trac+ is because the +num_tracks+ call in the block is calling
33
+ # <tt>Album#num_tracks</tt>. +recent_tracks+ is because the value will change over
34
+ # time. This plugin allows you to find these cases, and set the :instance_specific
35
+ # option appropriately for them:
36
+ #
37
+ # Album.one_to_one :first_track, class: :Track, instance_specific: false do |ds|
38
+ # ds.where(number: 1)
39
+ # end
40
+ #
41
+ # Album.one_to_one :last_track, class: :Track, instance_specific: true do |ds|
42
+ # ds.where(number: num_tracks)
43
+ # end
44
+ #
45
+ # Album.one_to_many :recent_tracks, class: :Track, instance_specific: true do |ds|
46
+ # ds.where{date_updated > Date.today - 10}
47
+ # end
48
+ #
49
+ # For the +recent_tracks+ association, instead of marking it instance_specific, you
50
+ # could also use a delayed evaluation, since it doesn't actually contain
51
+ # instance-specific code:
52
+ #
53
+ # Album.one_to_many :recent_tracks, class: :Track, instance_specific: false do |ds|
54
+ # ds.where{date_updated > Sequel.delay{Date.today - 10}}
55
+ # end
56
+ #
57
+ # Possible arguments to provide when loading the plugin:
58
+ #
59
+ # true :: Set the :instance_specific option to true
60
+ # false :: Set the :instance_specific option to false
61
+ # :default :: Call super to set the :instance_specific option
62
+ # :warn :: Emit a warning before calling super to set the :instance_specific option
63
+ # :raise :: Raise a Sequel::Error if an :instance_specific option is not provided and
64
+ # an association could be instance-specific.
65
+ #
66
+ # Note that this plugin only affects associations which could be instance
67
+ # specific (those with blocks), where the :instance_specific option was not
68
+ # specified when the association was created.
69
+ #
70
+ # Usage:
71
+ #
72
+ # # Set how to handle associations that could be instance specific
73
+ # # but did not specify an :instance_specific option, for all subclasses
74
+ # # (set before creating subclasses).
75
+ # Sequel::Model.plugin :instance_specific_default, :warn
76
+ #
77
+ # # Set how to handle associations that could be instance specific
78
+ # # but did not specify an :instance_specific option, for the Album class
79
+ # Album.plugin :instance_specific_default, :warn
80
+ module InstanceSpecificDefault
81
+ # Set how to handle associations that could be instance specific but did
82
+ # not specify an :instance_specific value.
83
+ def self.configure(model, default)
84
+ model.instance_variable_set(:@instance_specific_default, default)
85
+ end
86
+
87
+ module ClassMethods
88
+ Plugins.inherited_instance_variables(self, :@instance_specific_default=>nil)
89
+
90
+ private
91
+
92
+ # Return the appropriate :instance_specific value, or warn or raise if
93
+ # configured.
94
+ def _association_instance_specific_default(name)
95
+ case @instance_specific_default
96
+ when true, false
97
+ return @instance_specific_default
98
+ when :default
99
+ # nothing
100
+ when :warn
101
+ warn("possibly instance-specific association without :instance_specific option (class: #{self}, association: #{name})", :uplevel => 3)
102
+ when :raise
103
+ raise Sequel::Error, "possibly instance-specific association without :instance_specific option (class: #{self}, association: #{name})"
104
+ else
105
+ raise Sequel::Error, "invalid value passed to instance_specific_default plugin: #{@instance_specific_default.inspect}"
106
+ end
107
+
108
+ super
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
@@ -98,7 +98,7 @@ module Sequel
98
98
  end
99
99
 
100
100
  if retrieved_with
101
- raise(Error, "Invalid primary key column for #{model}: #{pkc.inspect}") unless primary_key = model.primary_key
101
+ primary_key = model.primary_key
102
102
  composite_pk = true if primary_key.is_a?(Array)
103
103
  id_map = {}
104
104
  retrieved_with.each{|o| id_map[o.pk] = o unless o.values.has_key?(a) || o.frozen?}
@@ -341,10 +341,9 @@ module Sequel
341
341
  eo[:loader] = false
342
342
 
343
343
  eager_load_results(opts, eo) do |assoc_record|
344
- if pks ||= assoc_record.get_column_value(key)
344
+ if pks = assoc_record.get_column_value(key)
345
345
  pks.each do |pkv|
346
- next unless objects = id_map[pkv]
347
- objects.each do |object|
346
+ id_map[pkv].each do |object|
348
347
  object.associations[name].push(assoc_record)
349
348
  end
350
349
  end
@@ -41,11 +41,9 @@ module Sequel
41
41
  # Create a prepared statement, but modify the SQL used so that the model's columns are explicitly
42
42
  # selected instead of using *, assuming that the dataset selects from a single table.
43
43
  def prepare_explicit_statement(ds, type, vals=OPTS)
44
- f = ds.opts[:from]
45
- meth = type == :insert_select ? :returning : :select
46
- s = ds.opts[meth]
47
- if f && f.length == 1 && !ds.opts[:join] && (!s || s.empty?)
48
- ds = ds.public_send(meth, *columns.map{|c| Sequel.identifier(c)})
44
+ s = ds.opts[:returning]
45
+ if !s || s.empty?
46
+ ds = ds.returning(*columns.map{|c| Sequel.identifier(c)})
49
47
  end
50
48
 
51
49
  prepare_statement(ds, type, vals)
@@ -70,9 +68,7 @@ module Sequel
70
68
  # Return a prepared statement that can be used to insert a row using the given columns
71
69
  # and return that column values for the row created.
72
70
  def prepared_insert_select(cols)
73
- if dataset.supports_insert_select?
74
- cached_prepared_statement(:insert_select, prepared_columns(cols)){prepare_explicit_statement(naked.clone(:server=>dataset.opts.fetch(:server, :default)), :insert_select, prepared_statement_key_hash(cols))}
75
- end
71
+ cached_prepared_statement(:insert_select, prepared_columns(cols)){prepare_explicit_statement(naked.clone(:server=>dataset.opts.fetch(:server, :default)), :insert_select, prepared_statement_key_hash(cols))}
76
72
  end
77
73
 
78
74
  # Return an array of two element arrays with the column symbol as the first entry and the
@@ -138,9 +134,7 @@ module Sequel
138
134
  # and return the new column values.
139
135
  def _insert_select_raw(ds)
140
136
  if use_prepared_statements_for?(:insert_select)
141
- if ps = model.send(:prepared_insert_select, @values.keys)
142
- _set_prepared_statement_server(ps).call(@values)
143
- end
137
+ _set_prepared_statement_server(model.send(:prepared_insert_select, @values.keys)).call(@values)
144
138
  else
145
139
  super
146
140
  end
@@ -66,9 +66,7 @@ module Sequel
66
66
  # Merge the current values into the default values to reduce the number
67
67
  # of free columns.
68
68
  def before_create
69
- if v = model.prepared_statements_column_defaults
70
- @values = v.merge(values)
71
- end
69
+ @values = model.prepared_statements_column_defaults.merge(@values)
72
70
  super
73
71
  end
74
72
 
@@ -194,21 +194,17 @@ module Sequel
194
194
  ds = ds.select_append(ka) unless ds.opts[:select] == nil
195
195
  model.eager_load_results(r, eo.merge(:loader=>false, :initialize_rows=>false, :dataset=>ds, :id_map=>nil)) do |obj|
196
196
  opk = prkey_conv[obj]
197
- if parent_map.has_key?(opk)
198
- if idm_obj = parent_map[opk]
199
- key_aliases.each{|ka_| idm_obj.values[ka_] = obj.values[ka_]}
200
- obj = idm_obj
201
- end
197
+ if idm_obj = parent_map[opk]
198
+ key_aliases.each{|ka_| idm_obj.values[ka_] = obj.values[ka_]}
199
+ obj = idm_obj
202
200
  else
203
201
  obj.associations[parent] = nil
204
202
  parent_map[opk] = obj
205
203
  (children_map[key_conv[obj]] ||= []) << obj
206
204
  end
207
205
 
208
- if roots = id_map[extract_key_alias[obj]]
209
- roots.each do |root|
210
- root.associations[ancestors] << obj
211
- end
206
+ id_map[extract_key_alias[obj]].each do |root|
207
+ root.associations[ancestors] << obj
212
208
  end
213
209
  end
214
210
  parent_map.each do |parent_id, obj|
@@ -306,11 +302,9 @@ module Sequel
306
302
  end
307
303
 
308
304
  opk = prkey_conv[obj]
309
- if parent_map.has_key?(opk)
310
- if idm_obj = parent_map[opk]
311
- key_aliases.each{|ka_| idm_obj.values[ka_] = obj.values[ka_]}
312
- obj = idm_obj
313
- end
305
+ if idm_obj = parent_map[opk]
306
+ key_aliases.each{|ka_| idm_obj.values[ka_] = obj.values[ka_]}
307
+ obj = idm_obj
314
308
  else
315
309
  obj.associations[childrena] = [] unless no_cache
316
310
  parent_map[opk] = obj
@@ -28,7 +28,7 @@ module Sequel
28
28
  model.plugin(:input_transformer, :string_stripper){|v| (v.is_a?(String) && !v.is_a?(SQL::Blob)) ? v.strip : v}
29
29
  end
30
30
  def self.configure(model)
31
- model.instance_exec{set_skipped_string_stripping_columns if @dataset}
31
+ model.send(:set_skipped_string_stripping_columns)
32
32
  end
33
33
 
34
34
  module ClassMethods
@@ -194,7 +194,11 @@ module Sequel
194
194
  def validates_each(*atts, &block)
195
195
  opts = extract_options!(atts)
196
196
  blank_meth = db.method(:blank_object?).to_proc
197
- blk = if (i = opts[:if]) || (am = opts[:allow_missing]) || (an = opts[:allow_nil]) || (ab = opts[:allow_blank])
197
+ i = opts[:if]
198
+ am = opts[:allow_missing]
199
+ an = opts[:allow_nil]
200
+ ab = opts[:allow_blank]
201
+ blk = if i || am || an || ab
198
202
  if i.is_a?(Proc)
199
203
  i = Plugins.def_sequel_method(self, "validation_class_methods_if", 0, &i)
200
204
  end
@@ -6,7 +6,7 @@ module Sequel
6
6
 
7
7
  # The minor version of Sequel. Bumped for every non-patch level
8
8
  # release, generally around once a month.
9
- MINOR = 34
9
+ MINOR = 35
10
10
 
11
11
  # The tiny version of Sequel. Usually 0, only bumped for bugfix
12
12
  # releases that fix regressions from previous versions.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequel
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.34.0
4
+ version: 5.35.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-01 00:00:00.000000000 Z
11
+ date: 2020-08-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -182,6 +182,7 @@ extra_rdoc_files:
182
182
  - doc/release_notes/5.32.0.txt
183
183
  - doc/release_notes/5.33.0.txt
184
184
  - doc/release_notes/5.34.0.txt
185
+ - doc/release_notes/5.35.0.txt
185
186
  files:
186
187
  - CHANGELOG
187
188
  - MIT-LICENSE
@@ -237,6 +238,7 @@ files:
237
238
  - doc/release_notes/5.32.0.txt
238
239
  - doc/release_notes/5.33.0.txt
239
240
  - doc/release_notes/5.34.0.txt
241
+ - doc/release_notes/5.35.0.txt
240
242
  - doc/release_notes/5.4.0.txt
241
243
  - doc/release_notes/5.5.0.txt
242
244
  - doc/release_notes/5.6.0.txt
@@ -462,6 +464,7 @@ files:
462
464
  - lib/sequel/plugins/insert_returning_select.rb
463
465
  - lib/sequel/plugins/instance_filters.rb
464
466
  - lib/sequel/plugins/instance_hooks.rb
467
+ - lib/sequel/plugins/instance_specific_default.rb
465
468
  - lib/sequel/plugins/inverted_subsets.rb
466
469
  - lib/sequel/plugins/json_serializer.rb
467
470
  - lib/sequel/plugins/lazy_attributes.rb