sequel 5.96.0 → 5.98.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 52123540e6fde4ed7ade970b36fc60898bae7463c0e22f63c4205748365cd82b
4
- data.tar.gz: e3374571d1e4fc490d3e2dc847679a2d8893b196c152d26f760b723605951ddd
3
+ metadata.gz: 28641a0240d39f16a5b6f2d30986d44a8744c26e24de5e78b8e3823a409c48d2
4
+ data.tar.gz: 25435173509b5058e781f4b06b1ebf8005244e86864c5a72ac35bc739656a6b1
5
5
  SHA512:
6
- metadata.gz: a4a48515dcb90df9ca6dde4b657f6d1ccce0724b0bd6d09c2a36aed3f37d338fe468135f177fb7cd5f985c79a5e06a91d1459731dc2fdc21601083863d9efd1a
7
- data.tar.gz: 3c18417f6991a190781cf0098aa81dae3077f52e5fed75162352ffe10fc2f065b5b9ffad1db3bdded7ecc8afed7ea698fc1047570a98dc8a7a87470e7cc2290e
6
+ metadata.gz: 11c7f62e3e296c43bef8af4de71ecaaf01e2e4d7e034910403991b91bc2075778dde0d16e899bc3ddacc1351e38da0f61f43476272e3b90c9a2aee5b09d82599
7
+ data.tar.gz: 173dfc5d3bbc19846a2d27b077d2241e6f64473f8a8f16ba052d9fd021d514036af1cf42cade4ea8974f2d920e126d4c23e824ab0e2d49a9062857ecd7e93342
@@ -62,11 +62,26 @@ module Sequel
62
62
 
63
63
  # Mimic the file:// uri, by having 2 preceding slashes specify a relative
64
64
  # path, and 3 preceding slashes specify an absolute path.
65
+ # Also support no preceding slashes to specify a relative path.
65
66
  def self.uri_to_options(uri) # :nodoc:
66
- { :database => (uri.host.nil? && uri.path == '/') ? nil : "#{uri.host}#{uri.path}" }
67
+ database = if uri.host.nil?
68
+ case uri.path
69
+ when '/'
70
+ nil
71
+ when nil
72
+ uri.opaque
73
+ else
74
+ uri.path
75
+ end
76
+ else
77
+ "#{uri.host}#{uri.path}"
78
+ end
79
+
80
+ { :database => database }
67
81
  end
68
82
  private_class_method :uri_to_options
69
83
 
84
+
70
85
  # Connect to the database. Since SQLite is a file based database,
71
86
  # the only options available are :database (to specify the database
72
87
  # name), and :timeout, to specify how long to wait for the database to
@@ -516,6 +516,7 @@ module Sequel
516
516
  end
517
517
  ensure
518
518
  conn.execute("UNLISTEN *")
519
+ true while conn.wait_for_notify(0)
519
520
  end
520
521
  end
521
522
  end
@@ -571,6 +571,7 @@ module Sequel
571
571
  # :if_exists :: Don't raise an error if the schema doesn't exist.
572
572
  def drop_schema(name, opts=OPTS)
573
573
  self << drop_schema_sql(name, opts)
574
+ remove_all_cached_schemas
574
575
  end
575
576
 
576
577
  # Drops a trigger from the database. Arguments:
@@ -726,6 +727,14 @@ module Sequel
726
727
  Sequel.synchronize{@primary_key_sequences[quoted_table] = value} if value
727
728
  end
728
729
 
730
+ # Rename a schema in the database. Arguments:
731
+ # name :: Current name of the schema
732
+ # opts :: New name for the schema
733
+ def rename_schema(name, new_name)
734
+ self << rename_schema_sql(name, new_name)
735
+ remove_all_cached_schemas
736
+ end
737
+
729
738
  # Refresh the materialized view with the given name.
730
739
  #
731
740
  # DB.refresh_view(:items_view)
@@ -1806,6 +1815,18 @@ module Sequel
1806
1815
  super
1807
1816
  end
1808
1817
 
1818
+ # Clear all cached schema information
1819
+ def remove_all_cached_schemas
1820
+ @primary_keys.clear
1821
+ @primary_key_sequences.clear
1822
+ @schemas.clear
1823
+ end
1824
+
1825
+ # SQL for renaming a schema.
1826
+ def rename_schema_sql(name, new_name)
1827
+ "ALTER SCHEMA #{quote_identifier(name)} RENAME TO #{quote_identifier(new_name)}"
1828
+ end
1829
+
1809
1830
  # SQL DDL statement for renaming a table. PostgreSQL doesn't allow you to change a table's schema in
1810
1831
  # a rename table operation, so specifying a new schema in new_name will not have an effect.
1811
1832
  def rename_table_sql(name, new_name)
@@ -89,8 +89,22 @@ module Sequel
89
89
 
90
90
  # Mimic the file:// uri, by having 2 preceding slashes specify a relative
91
91
  # path, and 3 preceding slashes specify an absolute path.
92
+ # Also support no preceding slashes to specify a relative path.
92
93
  def self.uri_to_options(uri) # :nodoc:
93
- { :database => (uri.host.nil? && uri.path == '/') ? nil : "#{uri.host}#{uri.path}" }
94
+ database = if uri.host.nil?
95
+ case uri.path
96
+ when '/'
97
+ nil
98
+ when nil
99
+ uri.opaque
100
+ else
101
+ uri.path
102
+ end
103
+ else
104
+ "#{uri.host}#{uri.path}"
105
+ end
106
+
107
+ { :database => database }
94
108
  end
95
109
 
96
110
  private_class_method :uri_to_options
@@ -1256,8 +1256,6 @@ module Sequel
1256
1256
  # receiver's current order. This yields the row and the array of order expressions
1257
1257
  # to the block, which should return an array of values to use.
1258
1258
  def ignore_values_preceding(row)
1259
- @opts[:order].map{|v| v.is_a?(SQL::OrderedExpression) ? v.expression : v}
1260
-
1261
1259
  order_exprs = @opts[:order].map do |v|
1262
1260
  if v.is_a?(SQL::OrderedExpression)
1263
1261
  descending = v.descending
@@ -37,18 +37,11 @@ module Sequel
37
37
  value.to_s
38
38
  end
39
39
 
40
- if RUBY_VERSION >= '2.4'
41
- def _typecast_value_string_to_decimal(value)
42
- BigDecimal(value)
43
- rescue
44
- BigDecimal('0.0')
45
- end
46
- else
47
- # :nocov:
48
- def _typecast_value_string_to_decimal(value)
49
- BigDecimal(value)
50
- end
51
- # :nocov:
40
+ # Typecast invalid BigDecimal to 0.0.
41
+ def _typecast_value_string_to_decimal(value)
42
+ BigDecimal(value)
43
+ rescue
44
+ BigDecimal('0.0')
52
45
  end
53
46
  end
54
47
 
@@ -0,0 +1,191 @@
1
+ # frozen-string-literal: true
2
+ #
3
+ # The pg_auto_parameterize_duplicate_query_detection extension builds on the
4
+ # pg_auto_parameterize extension, adding support for detecting duplicate
5
+ # queries inside a block that occur at the same location. This is designed
6
+ # mostly to catch duplicate query issues (e.g. N+1 queries) during testing.
7
+ #
8
+ # To detect duplicate queries inside a block of code, wrap the code with
9
+ # +detect_duplicate_queries+:
10
+ #
11
+ # DB.detect_duplicate_queries{your_code}
12
+ #
13
+ # With this approach, if the test runs code where the same query is executed
14
+ # more than once with the same call stack, a
15
+ # Sequel::Postgres::AutoParameterizeDuplicateQueryDetection::DuplicateQueries
16
+ # exception will be raised.
17
+ #
18
+ # You can pass the +:warn+ option to +detect_duplicate_queries+ to warn
19
+ # instead of raising. Note that if the block passed to +detect_duplicate_queries+
20
+ # raises, this extension will warn, and raise the original exception.
21
+ #
22
+ # For more control, you can pass the +:handler+ option to
23
+ # +detect_duplicate_queries+. If the +:handler+ option is provided, this
24
+ # extension will call the +:handler+ option with the hash of duplicate
25
+ # query information, and will not raise or warn. This can be useful in
26
+ # production environments, to record duplicate queries for later analysis.
27
+ #
28
+ # For accuracy, the entire call stack is always used as part of the hash key
29
+ # to determine whether a query is duplicate. However, you can filter the
30
+ # displayed backtrace by using the +:backtrace_filter+ option.
31
+ #
32
+ # +detect_duplicate_queries+ is concurrency aware, it uses the same approach
33
+ # that Sequel's default connection pools use. So if you are running multiple
34
+ # threads, +detect_duplicate_queries+ will only report duplicate queries for
35
+ # the current thread (or fiber if the fiber_concurrency extension is used).
36
+ #
37
+ # When testing applications, it's probably best to use this to wrap the
38
+ # application being tested. For example, testing with rack-test, if your app
39
+ # is +App+, you would want to wrap it:
40
+ #
41
+ # include Rack::Test::Methods
42
+ #
43
+ # WrappedApp = lambda do |env|
44
+ # DB.detect_duplicate_queries{App.call(env)}
45
+ # end
46
+ #
47
+ # def app
48
+ # WrappedApp
49
+ # end
50
+ #
51
+ # It is possible to use this to wrap each separate spec using an around hook,
52
+ # but that can result in false positives when using libraries that have
53
+ # implicit retrying (such as Capybara), as you can have the same call stack
54
+ # for multiple requests.
55
+ #
56
+ # Related module: Sequel::Postgres::AutoParameterizeDuplicateQueryDetection
57
+
58
+ module Sequel
59
+ module Postgres
60
+ # Enable detecting duplicate queries inside a block
61
+ module AutoParameterizeDuplicateQueryDetection
62
+ def self.extended(db)
63
+ db.instance_exec do
64
+ @duplicate_query_detection_contexts = {}
65
+ @duplicate_query_detection_mutex = Mutex.new
66
+ end
67
+ end
68
+
69
+ # Exception class raised when duplicate queries are detected.
70
+ class DuplicateQueries < Sequel::Error
71
+ # A hash of queries that were duplicate. Keys are arrays
72
+ # with 2 entries, the first being the query SQL, and the
73
+ # second being the related call stack (backtrace).
74
+ # The values are the number of query executions.
75
+ attr_reader :queries
76
+
77
+ def initialize(message, queries)
78
+ @queries = queries
79
+ super(message)
80
+ end
81
+ end
82
+
83
+ # Record each query executed so duplicates can be detected,
84
+ # if queries are being recorded.
85
+ def execute(sql, opts=OPTS, &block)
86
+ record, queries = duplicate_query_recorder_state
87
+
88
+ if record
89
+ queries[[sql.is_a?(Symbol) ? sql : sql.to_s, caller].freeze] += 1
90
+ end
91
+
92
+ super
93
+ end
94
+
95
+ # Ignore (do not record) queries inside given block. This can
96
+ # be useful in situations where you want to run your entire test suite
97
+ # with duplicate query detection, but you have duplicate queries in
98
+ # some parts of your application where it is not trivial to use a
99
+ # different approach. You can mark those specific sections with
100
+ # +ignore_duplicate_queries+, and still get duplicate query detection
101
+ # for the rest of the application.
102
+ def ignore_duplicate_queries(&block)
103
+ if state = duplicate_query_recorder_state
104
+ change_duplicate_query_recorder_state(state, false, &block)
105
+ else
106
+ # If we are not inside a detect_duplicate_queries block, there is
107
+ # no need to do anything, since we are not recording queries.
108
+ yield
109
+ end
110
+ end
111
+
112
+ # Run the duplicate query detector during the block.
113
+ # Options:
114
+ #
115
+ # :backtrace_filter :: Regexp used to filter the displayed backtrace.
116
+ # :handler :: If present, called with hash of duplicate query information,
117
+ # instead of raising or warning.
118
+ # :warn :: Always warn instead of raising for duplicate queries.
119
+ #
120
+ # Note that if you nest calls to this method, only the top
121
+ # level call will respect the passed options.
122
+ def detect_duplicate_queries(opts=OPTS, &block)
123
+ current = Sequel.current
124
+ if state = duplicate_query_recorder_state(current)
125
+ return change_duplicate_query_recorder_state(state, true, &block)
126
+ end
127
+
128
+ @duplicate_query_detection_mutex.synchronize do
129
+ @duplicate_query_detection_contexts[current] = [true, Hash.new(0)]
130
+ end
131
+
132
+ begin
133
+ yield
134
+ rescue Exception => e
135
+ raise
136
+ ensure
137
+ _, queries = @duplicate_query_detection_mutex.synchronize do
138
+ @duplicate_query_detection_contexts.delete(current)
139
+ end
140
+ queries.delete_if{|_,v| v < 2}
141
+
142
+ unless queries.empty?
143
+ if handler = opts[:handler]
144
+ handler.call(queries)
145
+ else
146
+ backtrace_filter = opts[:backtrace_filter]
147
+ backtrace_filter_note = backtrace_filter ? " (filtered)" : ""
148
+ query_info = queries.map do |k,v|
149
+ backtrace = k[1]
150
+ backtrace = backtrace.grep(backtrace_filter) if backtrace_filter
151
+ "times:#{v}\nsql:#{k[0]}\nbacktrace#{backtrace_filter_note}:\n#{backtrace.join("\n")}\n"
152
+ end
153
+ message = "duplicate queries detected:\n\n#{query_info.join("\n")}"
154
+
155
+ if e || opts[:warn]
156
+ warn(message)
157
+ else
158
+ raise DuplicateQueries.new(message, queries)
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
164
+
165
+ private
166
+
167
+ # Get the query record state for the given context.
168
+ def duplicate_query_recorder_state(current=Sequel.current)
169
+ @duplicate_query_detection_mutex.synchronize{@duplicate_query_detection_contexts[current]}
170
+ end
171
+
172
+ # Temporarily change whether to record queries for the block, resetting the
173
+ # previous setting after the block exits.
174
+ def change_duplicate_query_recorder_state(state, setting)
175
+ prev = state[0]
176
+ state[0] = setting
177
+
178
+ begin
179
+ yield
180
+ ensure
181
+ state[0] = prev
182
+ end
183
+ end
184
+ end
185
+ end
186
+
187
+ Database.register_extension(:pg_auto_parameterize_duplicate_query_detection) do |db|
188
+ db.extension(:pg_auto_parameterize)
189
+ db.extend(Postgres::AutoParameterizeDuplicateQueryDetection)
190
+ end
191
+ end
@@ -421,6 +421,17 @@ module Sequel
421
421
  @exclude_end
422
422
  end
423
423
 
424
+ # Support a friendly output
425
+ def inspect
426
+ range = if empty?
427
+ "empty"
428
+ else
429
+ "#{@exclude_begin ? "(" : "["}#{@begin},#{@end}#{@exclude_end ? ")" : "]"}"
430
+ end
431
+
432
+ "#<#{self.class.name} #{range}#{"::#{@db_type}" if @db_type}>"
433
+ end
434
+
424
435
  # Append a literalize version of the receiver to the sql.
425
436
  def sql_literal_append(ds, sql)
426
437
  if (s = @db_type) && !empty?
@@ -302,7 +302,7 @@ module Sequel
302
302
 
303
303
  if strategy == :window_function
304
304
  delete_rn = ds.row_number_column
305
- objects.each{|obj| obj.values.delete(delete_rn)}
305
+ objects.each{|obj| obj.remove_key!(delete_rn)}
306
306
  end
307
307
  elsif strategy == :union
308
308
  objects = []
@@ -1291,11 +1291,11 @@ module Sequel
1291
1291
  name = self[:name]
1292
1292
 
1293
1293
  self[:model].eager_load_results(self, eo) do |assoc_record|
1294
- assoc_record.values.delete(delete_rn) if delete_rn
1294
+ assoc_record.remove_key!(delete_rn) if delete_rn
1295
1295
  hash_key = if uses_lcks
1296
- left_key_alias.map{|k| assoc_record.values.delete(k)}
1296
+ left_key_alias.map{|k| assoc_record.remove_key!(k)}
1297
1297
  else
1298
- assoc_record.values.delete(left_key_alias)
1298
+ assoc_record.remove_key!(left_key_alias)
1299
1299
  end
1300
1300
 
1301
1301
  objects = h[hash_key]
@@ -2387,7 +2387,7 @@ module Sequel
2387
2387
  delete_rn = opts.delete_row_number_column
2388
2388
 
2389
2389
  eager_load_results(opts, eo) do |assoc_record|
2390
- assoc_record.values.delete(delete_rn) if delete_rn
2390
+ assoc_record.remove_key!(delete_rn) if delete_rn
2391
2391
  hash_key = uses_cks ? km.map{|k| assoc_record.get_column_value(k)} : assoc_record.get_column_value(km)
2392
2392
  objects = h[hash_key]
2393
2393
  if assign_singular
@@ -3063,7 +3063,7 @@ module Sequel
3063
3063
  if (((op == :'=' || op == :'!=') && r.is_a?(Sequel::Model)) ||
3064
3064
  (multiple = ((op == :IN || op == :'NOT IN') && ((is_ds = r.is_a?(Sequel::Dataset)) || (r.respond_to?(:all?) && r.all?{|x| x.is_a?(Sequel::Model)})))))
3065
3065
  l = args[0]
3066
- if ar = model.association_reflections[l]
3066
+ if ar = model.association_reflection(l)
3067
3067
  raise Error, "filtering by associations is not allowed for #{ar.inspect}" if ar[:allow_filtering_by] == false
3068
3068
 
3069
3069
  if multiple
@@ -2,13 +2,15 @@
2
2
 
3
3
  module Sequel
4
4
  class Model
5
+ # SEQUEL6: Remove Enumerable here, and send all Enumerable methods to dataset
6
+ # by default, with a plugin for the current behavior.
5
7
  extend Enumerable
6
8
  extend Inflections
7
9
 
8
10
  # Class methods for Sequel::Model that implement basic model functionality.
9
11
  #
10
12
  # * All of the following methods have class methods created that send the method
11
- # to the model's dataset: all, as_hash, avg, count, cross_join, distinct, each,
13
+ # to the model's dataset: all, any?, as_hash, avg, count, cross_join, distinct, each,
12
14
  # each_server, empty?, except, exclude, exclude_having, fetch_rows,
13
15
  # filter, first, first!, for_update, from, from_self, full_join, full_outer_join,
14
16
  # get, graph, grep, group, group_and_count, group_append, group_by, having, import,
@@ -695,7 +697,7 @@ module Sequel
695
697
  end
696
698
 
697
699
  # Add model methods that call dataset methods
698
- Plugins.def_dataset_methods(self, (Dataset::ACTION_METHODS + Dataset::QUERY_METHODS + [:each_server]) - [:<<, :or, :[], :columns, :columns!, :delete, :update, :set_graph_aliases, :add_graph_aliases])
700
+ Plugins.def_dataset_methods(self, (Dataset::ACTION_METHODS + Dataset::QUERY_METHODS + [:any?, :each_server]) - [:<<, :or, :[], :columns, :columns!, :delete, :update, :set_graph_aliases, :add_graph_aliases])
699
701
 
700
702
  private
701
703
 
@@ -1499,6 +1501,20 @@ END
1499
1501
  refresh
1500
1502
  end
1501
1503
 
1504
+ # Remove a key from the instances values, and return the value
1505
+ # of the key.
1506
+ #
1507
+ # a = Album[1]
1508
+ # a.values
1509
+ # # => {id: 1, artist_id: 2}
1510
+ # a.remove_key!(:artist_id)
1511
+ # # => 2
1512
+ # a.values
1513
+ # # => {id: 1}
1514
+ def remove_key!(key)
1515
+ @values.delete(key)
1516
+ end
1517
+
1502
1518
  # Creates or updates the record, after making sure the record
1503
1519
  # is valid and before hooks execute successfully. Fails if:
1504
1520
  #
@@ -0,0 +1,151 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Sequel
4
+ module Plugins
5
+ # The deprecated_associations plugin adds support for
6
+ # deprecating associations. Attempts to use association
7
+ # methods and access association metadata for deprecated
8
+ # associations results in a warning.
9
+ #
10
+ # Album.plugin :deprecated_associations
11
+ # Album.many_to_one :artist, deprecated: true
12
+ # album = Album[1]
13
+ #
14
+ # # Warnings for all of the following calls
15
+ # album.artist
16
+ # album.artist_dataset
17
+ # album.artist = Artist[2]
18
+ # Album.association_reflection(:artist)
19
+ # Album.eager(:artist)
20
+ # Album.eager_graph(:artist)
21
+ # Album.where(artist: Artist[1]).all
22
+ #
23
+ # By default, the plugin issues a single warning per
24
+ # association method or association reflection. See
25
+ # DeprecatedAssociations.configure for options to make
26
+ # deprecated association issue warnings for every access,
27
+ # to include backtraces in warnings, or to raise an
28
+ # exception instead of warning.
29
+ #
30
+ # Note that Model.association_reflections and
31
+ # Model.all_association_reflections will include deprecated
32
+ # associations, and accessing the metadata for deprecated
33
+ # associations through these interfaces not issue warnings.
34
+ #
35
+ # Usage:
36
+ #
37
+ # # Make all models support deprecated associations
38
+ # # (called before loading subclasses)
39
+ # Sequel::Model.plugin :deprecated_associations
40
+ #
41
+ # # Make Album class support deprecated associations
42
+ # Album.plugin :deprecated_associations
43
+ module DeprecatedAssociations
44
+ # Exception class used for deprecated association use when
45
+ # raising exceptions instead of emitting warnings.
46
+ class Access < Sequel::Error; end
47
+
48
+ # Configure the deprecated associations plugin. Options:
49
+ #
50
+ # backtrace: Print backtrace with warning
51
+ # deduplicate: Set to false to emit warnings for every
52
+ # deprecated association method call/access
53
+ # (when not caching associations, this is always false)
54
+ # raise: Raise Sequel::Plugin::DeprecatedAssociations::Access
55
+ # instead of warning
56
+ def self.configure(model, opts = OPTS)
57
+ model.instance_exec do
58
+ (@deprecated_associations_config ||= {}).merge!(opts)
59
+ end
60
+ end
61
+
62
+ module ClassMethods
63
+ # Issue a deprecation warning if the association is deprecated.
64
+ def association_reflection(assoc)
65
+ ref = super
66
+ if ref && ref[:deprecated]
67
+ emit_deprecated_association_warning(ref, nil) do
68
+ "Access of association reflection for deprecated association: class:#{name} association:#{assoc}"
69
+ end
70
+ end
71
+ ref
72
+ end
73
+
74
+ private
75
+
76
+ # Issue a deprecation warning when the defined method is called if the
77
+ # association is deprecated and the method name does not start with the
78
+ # underscore (to avoid not warning twice, once for the public association
79
+ # method and once for the private association method).
80
+ def association_module_def(name, opts=OPTS, &block)
81
+ super
82
+ if opts[:deprecated] && name[0] != "_"
83
+ deprecated_associations_module.module_exec do
84
+ define_method(name) do |*a, &b|
85
+ self.class.send(:emit_deprecated_association_warning, opts, name) do
86
+ "Calling deprecated association method: class:#{self.class.name} association:#{opts[:name]} method:#{name}"
87
+ end
88
+ super(*a, &b)
89
+ end
90
+ alias_method name, name
91
+ end
92
+ end
93
+ nil
94
+ end
95
+
96
+ # Issue a deprecation warning when the defined method is called if the
97
+ # association is deprecated.
98
+ def association_module_delegate_def(name, opts, &block)
99
+ super
100
+ if opts[:deprecated]
101
+ deprecated_associations_module.module_exec do
102
+ define_method(name) do |*a, &b|
103
+ self.class.send(:emit_deprecated_association_warning, opts, name) do
104
+ "Calling deprecated association method: class:#{self.class.name} association:#{opts[:name]} method:#{name}"
105
+ end
106
+ super(*a, &b)
107
+ end
108
+ # :nocov:
109
+ ruby2_keywords(name) if respond_to?(:ruby2_keywords, true)
110
+ # :nocov:
111
+ alias_method(name, name)
112
+ end
113
+ end
114
+ nil
115
+ end
116
+
117
+ # A module to add deprecated association methods to. These methods
118
+ # handle issuing the deprecation warnings, and call super to get the
119
+ # default behavior.
120
+ def deprecated_associations_module
121
+ return @deprecated_associations_module if defined?(@deprecated_associations_module)
122
+ @deprecated_associations_module = Module.new
123
+ include(@deprecated_associations_module)
124
+ @deprecated_associations_module
125
+ end
126
+
127
+ # Emit a deprecation warning, or raise an exception if the :raise
128
+ # plugin option was used.
129
+ def emit_deprecated_association_warning(ref, method)
130
+ config = @deprecated_associations_config
131
+
132
+ raise Access, yield if config[:raise]
133
+
134
+ unless config[:deduplicate] == false
135
+ emit = false
136
+ ref.send(:cached_fetch, [:deprecated_associations, method]) do
137
+ emit = true
138
+ end
139
+ return unless emit
140
+ end
141
+
142
+ if config[:backtrace]
143
+ warn yield, caller(2)
144
+ else
145
+ warn yield, :uplevel => 2
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Sequel
4
4
  module Plugins
5
- # If the model's dataset selects explicit columns and the
5
+ # If the model's dataset selects explicit columns (or table.*) and the
6
6
  # database supports it, the insert_returning_select plugin will
7
7
  # automatically set the RETURNING clause on the dataset used to
8
8
  # insert rows to the columns selected, which allows the default model
@@ -45,6 +45,9 @@ module Sequel
45
45
  ret
46
46
  end
47
47
 
48
+ RETURN_ALL = [Sequel::Dataset::WILDCARD].freeze
49
+ private_constant :RETURN_ALL
50
+
48
51
  # Determine the columns to use for the returning clause, or return nil
49
52
  # if they can't be determined and a returning clause should not be
50
53
  # added automatically.
@@ -52,6 +55,12 @@ module Sequel
52
55
  return unless ds.supports_returning?(:insert)
53
56
  return unless values = ds.opts[:select]
54
57
 
58
+ # SELECT table.* -> RETURNING *
59
+ if values.length == 1 && values[0].is_a?(Sequel::SQL::ColumnAll)
60
+ return RETURN_ALL
61
+ end
62
+
63
+ # SELECT column1, table.column2, ... -> RETURNING column1, column2, ...
55
64
  values = values.map{|v| ds.unqualified_column_for(v)}
56
65
  if values.all?
57
66
  values
@@ -111,7 +111,7 @@ module Sequel
111
111
  ancestor_base_case_columns = prkey_array.zip(key_aliases).map{|k, ka_| SQL::AliasedExpression.new(k, ka_)} + c_all
112
112
  descendant_base_case_columns = key_array.zip(key_aliases).map{|k, ka_| SQL::AliasedExpression.new(k, ka_)} + c_all
113
113
  recursive_case_columns = prkey_array.zip(key_aliases).map{|k, ka_| SQL::QualifiedIdentifier.new(t, ka_)} + c_all
114
- extract_key_alias = lambda{|m| key_aliases.map{|ka_| bd_conv[m.values.delete(ka_)]}}
114
+ extract_key_alias = lambda{|m| key_aliases.map{|ka_| bd_conv[m.remove_key!(ka_)]}}
115
115
  else
116
116
  key_present = key_conv = lambda{|m| m[key]}
117
117
  prkey_conv = lambda{|m| m[prkey]}
@@ -119,7 +119,7 @@ module Sequel
119
119
  ancestor_base_case_columns = [SQL::AliasedExpression.new(prkey, ka)] + c_all
120
120
  descendant_base_case_columns = [SQL::AliasedExpression.new(key, ka)] + c_all
121
121
  recursive_case_columns = [SQL::QualifiedIdentifier.new(t, ka)] + c_all
122
- extract_key_alias = lambda{|m| bd_conv[m.values.delete(ka)]}
122
+ extract_key_alias = lambda{|m| bd_conv[m.remove_key!(ka)]}
123
123
  end
124
124
 
125
125
  parent = opts.merge(opts.fetch(:parent, OPTS)).fetch(:name, :parent)
@@ -200,7 +200,7 @@ module Sequel
200
200
  model.eager_load_results(r, eo.merge(:loader=>false, :initialize_rows=>false, :dataset=>ds, :id_map=>nil)) do |obj|
201
201
  opk = prkey_conv[obj]
202
202
  if idm_obj = parent_map[opk]
203
- key_aliases.each{|ka_| idm_obj.values[ka_] = obj.values[ka_]}
203
+ key_aliases.each{|ka_| idm_obj[ka_] = obj[ka_]}
204
204
  obj = idm_obj
205
205
  else
206
206
  obj.associations[parent] = nil
@@ -307,12 +307,12 @@ module Sequel
307
307
  ds = ds.select_append(ka) unless ds.opts[:select] == nil
308
308
  model.eager_load_results(r, eo.merge(:loader=>false, :initialize_rows=>false, :dataset=>ds, :id_map=>nil, :associations=>OPTS)) do |obj|
309
309
  if level
310
- no_cache = no_cache_level == obj.values.delete(la)
310
+ no_cache = no_cache_level == obj.remove_key!(la)
311
311
  end
312
312
 
313
313
  opk = prkey_conv[obj]
314
314
  if idm_obj = parent_map[opk]
315
- key_aliases.each{|ka_| idm_obj.values[ka_] = obj.values[ka_]}
315
+ key_aliases.each{|ka_| idm_obj[ka_] = obj[ka_]}
316
316
  obj = idm_obj
317
317
  else
318
318
  obj.associations[childrena] = [] unless no_cache
@@ -54,6 +54,16 @@ module Sequel
54
54
  end
55
55
  end
56
56
 
57
+ # Remove the key from noncolumn values if it is present there. If it is not
58
+ # present there, then use the default behavior of removing it from values.
59
+ def remove_key!(key)
60
+ if @noncolumn_values && @noncolumn_values.key?(key)
61
+ @noncolumn_values.delete(key)
62
+ else
63
+ super
64
+ end
65
+ end
66
+
57
67
  # Check all entries in the values hash. If any of the keys are not columns,
58
68
  # move the entry into the noncolumn_values hash.
59
69
  def split_noncolumn_values
@@ -9,6 +9,13 @@ module Sequel
9
9
  # in the result sets (and possibly overwrite columns in the
10
10
  # current model with the same name).
11
11
  #
12
+ # Note that by default on databases that supporting RETURNING,
13
+ # using this plugin will cause instance creations
14
+ # to use two queries (insert and refresh) instead of a single
15
+ # query using RETURNING. You can use the insert_returning_select
16
+ # plugin to automatically use RETURNING for instance creations
17
+ # for models using this plugin.
18
+ #
12
19
  # Usage:
13
20
  #
14
21
  # # Make all model subclasses select table.*
@@ -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 = 96
9
+ MINOR = 98
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,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequel
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.96.0
4
+ version: 5.98.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
@@ -250,6 +250,7 @@ files:
250
250
  - lib/sequel/extensions/pg_array.rb
251
251
  - lib/sequel/extensions/pg_array_ops.rb
252
252
  - lib/sequel/extensions/pg_auto_parameterize.rb
253
+ - lib/sequel/extensions/pg_auto_parameterize_duplicate_query_detection.rb
253
254
  - lib/sequel/extensions/pg_auto_parameterize_in_array.rb
254
255
  - lib/sequel/extensions/pg_enum.rb
255
256
  - lib/sequel/extensions/pg_extended_date_support.rb
@@ -342,6 +343,7 @@ files:
342
343
  - lib/sequel/plugins/def_dataset_method.rb
343
344
  - lib/sequel/plugins/defaults_setter.rb
344
345
  - lib/sequel/plugins/delay_add_association.rb
346
+ - lib/sequel/plugins/deprecated_associations.rb
345
347
  - lib/sequel/plugins/dirty.rb
346
348
  - lib/sequel/plugins/eager_each.rb
347
349
  - lib/sequel/plugins/eager_graph_eager.rb