Skip to content

Bump sequel from 5.101.0 to 5.104.0#921

Closed
dependabot[bot] wants to merge 1 commit intomainfrom
dependabot/bundler/sequel-5.104.0
Closed

Bump sequel from 5.101.0 to 5.104.0#921
dependabot[bot] wants to merge 1 commit intomainfrom
dependabot/bundler/sequel-5.104.0

Conversation

@dependabot
Copy link
Copy Markdown
Contributor

@dependabot dependabot Bot commented on behalf of github May 4, 2026

Bumps sequel from 5.101.0 to 5.104.0.

Changelog

Sourced from sequel's changelog.

=== 5.104.0 (2026-05-01)

  • Support :lateral_subquery as a filter by associations limit strategy in the dataset associations plugin (jeremyevans)

  • Support :lateral_subquery as an eager limit strategy for many_through_many/one_through_many associations (jeremyevans)

  • Support :lateral_subquery as a filter by associations limit strategy for many_through_many/one_through_many associations (jeremyevans)

  • Support :lateral_subquery as an eager_graph limit strategy for many_through_many/one_through_many associations (jeremyevans)

  • Support :lateral_subquery as an eager limit strategy for many_to_many/one_through_one associations (jeremyevans)

  • Support :lateral_subquery as a filter by associations limit strategy for many_to_many/one_through_one associations (jeremyevans)

  • Support :lateral_subquery as an eager_graph limit strategy for many_to_many/one_through_one associations (jeremyevans)

  • Support :lateral_subquery as an eager limit strategy for one_to_many/one_to_one associations (jeremyevans)

  • Support :lateral_subquery as a filter by associations limit strategy for one_to_many/one_to_one associations (jeremyevans)

  • Support :lateral_subquery as an eager_graph limit strategy for one_to_many/one_to_one associations (jeremyevans)

=== 5.103.0 (2026-04-01)

  • Have column and add_column in the generators warn if option values override argument values (jeremyevans)

  • Show method name causing change migration to be irreversible in exception message (jeremyevans)

  • Make dirty plugin handle the Model#[]= calls with keys that are not model columns (jeremyevans) (#2357)

  • Soft-deprecate the jdbc/derby adapter, as Derby is no longer supported upstream (jeremyevans)

  • Add lit_require_frozen extension, for disallowing usage of unfrozen strings as literal SQL (jeremyevans)

  • Avoid creating multiple strings when passing literal string to dataset filtering method (jeremyevans)

=== 5.102.0 (2026-03-01)

  • Make connection_validator and connection_expiration extensions correct handle sharded_timed_queue connection pool (jeremyevans) (#2354)

  • Add connection_checkout_event_callback extension to assist in collecting telemetry information (jeremyevans)

Commits
  • 4642b4a Bump version to 5.104.0
  • 67d897c Fix some minor issues in the Sequel for SQL users guide
  • bdf0c1e Add JRuby 10.1 to matrix
  • f060c6c test: fix must_raise assertions on minitest 6.0.5
  • d40f4d3 Skip lateral_subquery association tests on MSSQL and SQLAnywhere
  • 025bdf7 Simplify lateral_subquery integration testing
  • b826e2b Support :lateral_subquery as a filter by associations limit strategy in the d...
  • 98a70d2 Refactor :lateral_subquery eager limit strategy support
  • 1ad2e96 Support :lateral_subquery as an eager limit strategy for many_through_many/on...
  • 49c7f74 Refactor :lateral_subquery filter by associations limit strategy code
  • Additional commits viewable in compare view

Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)

Bumps [sequel](https://github.com/jeremyevans/sequel) from 5.101.0 to 5.104.0.
- [Changelog](https://github.com/jeremyevans/sequel/blob/master/CHANGELOG)
- [Commits](jeremyevans/sequel@5.101.0...5.104.0)

---
updated-dependencies:
- dependency-name: sequel
  dependency-version: 5.104.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot added dependencies ruby Pull requests that update Ruby code labels May 4, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

1 similar comment
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

gem compare bigdecimal 4.0.1 4.1.2

Compared versions: ["4.0.1", "4.1.2"]
  DIFFERENT require_paths:
    4.0.1: ["/opt/hostedtoolcache/Ruby/4.0.3/x64/lib/ruby/gems/4.0.0/extensions/x86_64-linux/4.0.0/bigdecimal-4.0.1", "lib"]
    4.1.2: ["/opt/hostedtoolcache/Ruby/4.0.3/x64/lib/ruby/gems/4.0.0/extensions/x86_64-linux/4.0.0/bigdecimal-4.1.2", "lib"]
  DIFFERENT required_ruby_version:
    4.0.1: >= 2.5.0
    4.1.2: >= 2.6.0
  DIFFERENT version:
    4.0.1: 4.0.1
    4.1.2: 4.1.2
  DIFFERENT files:
    4.0.1->4.1.2:
      * Added:
            ext/bigdecimal/div.h +192/-0
            ext/bigdecimal/ntt.h +191/-0
            sig/big_decimal.rbs +1502/-0
            sig/big_decimal_util.rbs +158/-0
            sig/big_math.rbs +423/-0
      * Changed:
            bigdecimal.gemspec +6/-1
            ext/bigdecimal/bigdecimal.c +263/-262
            ext/bigdecimal/bigdecimal.h +43/-37
            ext/bigdecimal/extconf.rb +5/-2
            ext/bigdecimal/missing.h +4/-2
            ext/bigdecimal/missing/dtoa.c +183/-136
            lib/bigdecimal.rb +104/-60
            lib/bigdecimal/math.rb +103/-124
            lib/bigdecimal/util.rb +1/-1
            sample/linear.rb +73/-37
              (!) Shebang probably lost: #!/usr/local/bin/ruby
            sample/nlsolve.rb +47/-30
              (!) Shebang probably lost: #!/usr/local/bin/ruby
            sample/pi.rb +2/-7
              (!) Shebang probably lost: #!/usr/local/bin/ruby

1 similar comment
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

gem compare bigdecimal 4.0.1 4.1.2

Compared versions: ["4.0.1", "4.1.2"]
  DIFFERENT require_paths:
    4.0.1: ["/opt/hostedtoolcache/Ruby/4.0.3/x64/lib/ruby/gems/4.0.0/extensions/x86_64-linux/4.0.0/bigdecimal-4.0.1", "lib"]
    4.1.2: ["/opt/hostedtoolcache/Ruby/4.0.3/x64/lib/ruby/gems/4.0.0/extensions/x86_64-linux/4.0.0/bigdecimal-4.1.2", "lib"]
  DIFFERENT required_ruby_version:
    4.0.1: >= 2.5.0
    4.1.2: >= 2.6.0
  DIFFERENT version:
    4.0.1: 4.0.1
    4.1.2: 4.1.2
  DIFFERENT files:
    4.0.1->4.1.2:
      * Added:
            ext/bigdecimal/div.h +192/-0
            ext/bigdecimal/ntt.h +191/-0
            sig/big_decimal.rbs +1502/-0
            sig/big_decimal_util.rbs +158/-0
            sig/big_math.rbs +423/-0
      * Changed:
            bigdecimal.gemspec +6/-1
            ext/bigdecimal/bigdecimal.c +263/-262
            ext/bigdecimal/bigdecimal.h +43/-37
            ext/bigdecimal/extconf.rb +5/-2
            ext/bigdecimal/missing.h +4/-2
            ext/bigdecimal/missing/dtoa.c +183/-136
            lib/bigdecimal.rb +104/-60
            lib/bigdecimal/math.rb +103/-124
            lib/bigdecimal/util.rb +1/-1
            sample/linear.rb +73/-37
              (!) Shebang probably lost: #!/usr/local/bin/ruby
            sample/nlsolve.rb +47/-30
              (!) Shebang probably lost: #!/usr/local/bin/ruby
            sample/pi.rb +2/-7
              (!) Shebang probably lost: #!/usr/local/bin/ruby

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

gem compare --diff bigdecimal 4.0.1 4.1.2

Diff too large (211479 chars)

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

gem compare --diff bigdecimal 4.0.1 4.1.2

Diff too large (211478 chars)

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

gem compare sequel 5.101.0 5.104.0

Compared versions: ["5.101.0", "5.104.0"]
  DIFFERENT version:
    5.101.0: 5.101.0
    5.104.0: 5.104.0
  DIFFERENT files:
    5.101.0->5.104.0:
      * Added:
            lib/sequel/extensions/connection_checkout_event_callback.rb +151/-0
            lib/sequel/extensions/lit_require_frozen.rb +131/-0
      * Changed:
            MIT-LICENSE +1/-1
            lib/sequel/adapters/jdbc/derby.rb +2/-0
            lib/sequel/adapters/jdbc/h2.rb +2/-2
            lib/sequel/adapters/postgres.rb +1/-1
            lib/sequel/adapters/shared/mssql.rb +3/-3
            lib/sequel/adapters/shared/mysql.rb +5/-4
            lib/sequel/adapters/shared/postgres.rb +16/-16
            lib/sequel/adapters/shared/sqlite.rb +3/-3
            lib/sequel/adapters/sqlite.rb +1/-1
            lib/sequel/adapters/tinytds.rb +1/-1
            lib/sequel/connection_pool.rb +6/-0
            lib/sequel/connection_pool/sharded_timed_queue.rb +36/-11
            lib/sequel/connection_pool/timed_queue.rb +27/-9
            lib/sequel/database/schema_generator.rb +36/-5
            lib/sequel/database/schema_methods.rb +1/-1
            lib/sequel/dataset/placeholder_literalizer.rb +3/-0
            lib/sequel/dataset/prepared_statements.rb +7/-4
            lib/sequel/dataset/query.rb +6/-3
            lib/sequel/dataset/sql.rb +6/-1
            lib/sequel/extensions/connection_expiration.rb +1/-1
            lib/sequel/extensions/connection_validator.rb +1/-1
            lib/sequel/extensions/dataset_run.rb +2/-2
            lib/sequel/extensions/date_arithmetic.rb +6/-6
            lib/sequel/extensions/migration.rb +14/-17
            lib/sequel/extensions/pg_enum.rb +1/-1
            lib/sequel/model/associations.rb +180/-6
            lib/sequel/plugins/dataset_associations.rb +20/-1
            lib/sequel/plugins/dirty.rb +5/-2
            lib/sequel/plugins/many_through_many.rb +21/-0
            lib/sequel/plugins/mssql_optimistic_locking.rb +1/-1
            lib/sequel/plugins/pg_xmin_optimistic_locking.rb +1/-1
            lib/sequel/plugins/serialization.rb +10/-1
            lib/sequel/version.rb +1/-1
  DIFFERENT extra_rdoc_files:
    5.101.0->5.104.0:
      * Changed:
            MIT-LICENSE +1/-1

1 similar comment
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

gem compare sequel 5.101.0 5.104.0

Compared versions: ["5.101.0", "5.104.0"]
  DIFFERENT version:
    5.101.0: 5.101.0
    5.104.0: 5.104.0
  DIFFERENT files:
    5.101.0->5.104.0:
      * Added:
            lib/sequel/extensions/connection_checkout_event_callback.rb +151/-0
            lib/sequel/extensions/lit_require_frozen.rb +131/-0
      * Changed:
            MIT-LICENSE +1/-1
            lib/sequel/adapters/jdbc/derby.rb +2/-0
            lib/sequel/adapters/jdbc/h2.rb +2/-2
            lib/sequel/adapters/postgres.rb +1/-1
            lib/sequel/adapters/shared/mssql.rb +3/-3
            lib/sequel/adapters/shared/mysql.rb +5/-4
            lib/sequel/adapters/shared/postgres.rb +16/-16
            lib/sequel/adapters/shared/sqlite.rb +3/-3
            lib/sequel/adapters/sqlite.rb +1/-1
            lib/sequel/adapters/tinytds.rb +1/-1
            lib/sequel/connection_pool.rb +6/-0
            lib/sequel/connection_pool/sharded_timed_queue.rb +36/-11
            lib/sequel/connection_pool/timed_queue.rb +27/-9
            lib/sequel/database/schema_generator.rb +36/-5
            lib/sequel/database/schema_methods.rb +1/-1
            lib/sequel/dataset/placeholder_literalizer.rb +3/-0
            lib/sequel/dataset/prepared_statements.rb +7/-4
            lib/sequel/dataset/query.rb +6/-3
            lib/sequel/dataset/sql.rb +6/-1
            lib/sequel/extensions/connection_expiration.rb +1/-1
            lib/sequel/extensions/connection_validator.rb +1/-1
            lib/sequel/extensions/dataset_run.rb +2/-2
            lib/sequel/extensions/date_arithmetic.rb +6/-6
            lib/sequel/extensions/migration.rb +14/-17
            lib/sequel/extensions/pg_enum.rb +1/-1
            lib/sequel/model/associations.rb +180/-6
            lib/sequel/plugins/dataset_associations.rb +20/-1
            lib/sequel/plugins/dirty.rb +5/-2
            lib/sequel/plugins/many_through_many.rb +21/-0
            lib/sequel/plugins/mssql_optimistic_locking.rb +1/-1
            lib/sequel/plugins/pg_xmin_optimistic_locking.rb +1/-1
            lib/sequel/plugins/serialization.rb +10/-1
            lib/sequel/version.rb +1/-1
  DIFFERENT extra_rdoc_files:
    5.101.0->5.104.0:
      * Changed:
            MIT-LICENSE +1/-1

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

1 similar comment
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

gem compare bigdecimal 4.0.1 4.1.2

Compared versions: ["4.0.1", "4.1.2"]
  DIFFERENT require_paths:
    4.0.1: ["/opt/hostedtoolcache/Ruby/4.0.3/x64/lib/ruby/gems/4.0.0/extensions/x86_64-linux/4.0.0/bigdecimal-4.0.1", "lib"]
    4.1.2: ["/opt/hostedtoolcache/Ruby/4.0.3/x64/lib/ruby/gems/4.0.0/extensions/x86_64-linux/4.0.0/bigdecimal-4.1.2", "lib"]
  DIFFERENT required_ruby_version:
    4.0.1: >= 2.5.0
    4.1.2: >= 2.6.0
  DIFFERENT version:
    4.0.1: 4.0.1
    4.1.2: 4.1.2
  DIFFERENT files:
    4.0.1->4.1.2:
      * Added:
            ext/bigdecimal/div.h +192/-0
            ext/bigdecimal/ntt.h +191/-0
            sig/big_decimal.rbs +1502/-0
            sig/big_decimal_util.rbs +158/-0
            sig/big_math.rbs +423/-0
      * Changed:
            bigdecimal.gemspec +6/-1
            ext/bigdecimal/bigdecimal.c +263/-262
            ext/bigdecimal/bigdecimal.h +43/-37
            ext/bigdecimal/extconf.rb +5/-2
            ext/bigdecimal/missing.h +4/-2
            ext/bigdecimal/missing/dtoa.c +183/-136
            lib/bigdecimal.rb +104/-60
            lib/bigdecimal/math.rb +103/-124
            lib/bigdecimal/util.rb +1/-1
            sample/linear.rb +73/-37
              (!) Shebang probably lost: #!/usr/local/bin/ruby
            sample/nlsolve.rb +47/-30
              (!) Shebang probably lost: #!/usr/local/bin/ruby
            sample/pi.rb +2/-7
              (!) Shebang probably lost: #!/usr/local/bin/ruby

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

gem compare --diff sequel 5.101.0 5.104.0

Compared versions: ["5.101.0", "5.104.0"]
  DIFFERENT files:
    5.101.0->5.104.0:
      * Added:
        lib/sequel/extensions/connection_checkout_event_callback.rb
                --- /tmp/20260504-1933-b1sazu	2026-05-04 02:36:11.455864284 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/extensions/connection_checkout_event_callback.rb	2026-05-04 02:36:11.432863773 +0000
                @@ -0,0 +1,151 @@
                +# frozen-string-literal: true
                +#
                +# The connection_checkout_event_callback extension modifies a database's
                +# connection pool to allow for a checkout event callback. This callback is
                +# called with the following arguments:
                +#
                +# :immediately_available :: Connection immediately available and returned
                +# :not_immediately_available :: Connection not immediately available
                +# :new_connection :: New connection created and returned
                +# Float :: Number of seconds waiting to acquire a connection
                +#
                +# This is a low-level extension that allows for building telemetry
                +# information. It doesn't implement any telemetry reporting itself. The
                +# main reason for recording this information is to use it to determine the
                +# appropriate size for the connection pool. Having too large a connection
                +# pool can waste resources, while having too small a connection pool can
                +# result in substantial time to check out a connection. In general, you
                +# want to use as small a pool as possible while keeping the time to
                +# checkout a connection low.
                +#
                +# To use the connection checkout event callback, you must first load the
                +# extension:
                +#
                +#   DB.extension(:connection_checkout_event_callback)
                +#
                +# By default, an empty proc is used as the callback so that loading the
                +# support doesn't break anything. If you are using the extension, you
                +# should set the callback at some point during application startup:
                +#
                +#   DB.pool.on_checkout_event = proc do |event|
                +#     # ...
                +#   end
                +#
                +# When using the sharded connection pool, the callback is also
                +# passed a second argument, the requested server shard (generally a
                +# symbol), allowing for collection of per-shard telemetry:
                +#
                +#   DB.pool.on_checkout_event = proc do |event, server|
                +#     # ...
                +#   end
                +#
                +# Note that the callback may be called currently by multiple threads.
                +# You should use some form of concurrency control inside the callback,
                +# such as a mutex or queue.
                +#
                +# Below is a brief example of usage to determine the percentage of
                +# connection requests where a connection was immediately available:
                +#
                +#   mutex = Mutex.new
                +#   total = immediates = 0
                +#
                +#   DB.pool.on_checkout_event = proc do |event|
                +#     case event
                +#     when :immediately_available
                +#       mutex.synchronize do
                +#         total += 1
                +#         immediates += 1
                +#       end
                +#     when :not_immediately_available
                +#       mutex.synchronize do
                +#         total += 1
                +#       end
                +#     end
                +#   end
                +#
                +#   immediate_percentage = lambda do
                +#     mutex.synchronize do
                +#       100.0 * immediates / total
                +#     end
                +#   end
                +#   
                +# Note that this extension only works with the timed_queue
                +# and sharded_timed_queue connection pools (the default
                +# connection pools when using Ruby 3.2+).
                +#
                +# Related modules: Sequel::ConnectionCheckoutEventCallbacks::TimedQueue,
                +# Sequel::ConnectionCheckoutEventCallbacks::ShardedTimedQueue
                +
                +#
                +module Sequel
                +  module ConnectionCheckoutEventCallbacks
                +    module TimedQueue
                +      # The callback that is called with connection checkout events.
                +      attr_accessor :on_checkout_event
                +
                +      private
                +
                +      def available
                +        conn = super
                +        @on_checkout_event.call(conn ? :immediately_available : :not_immediately_available)
                +        conn
                +      end
                +
                +      def try_make_new
                +        conn = super
                +        @on_checkout_event.call(:new_connection) if conn
                +        conn
                +      end
                +
                +      def wait_until_available
                +        timer = Sequel.start_timer
                +        conn = super
                +        @on_checkout_event.call(Sequel.elapsed_seconds_since(timer))
                +        conn
                +      end
                +    end
                +
                +    module ShardedTimedQueue
                +      # The callback that is called with connection checkout events.
                +      attr_accessor :on_checkout_event
                +
                +      private
                +
                +      def available(queue, server)
                +        conn = super
                +        @on_checkout_event.call(conn ? :immediately_available : :not_immediately_available, server)
                +        conn
                +      end
                +
                +      def try_make_new(server)
                +        conn = super
                +        @on_checkout_event.call(:new_connection, server) if conn
                +        conn
                +      end
                +
                +      def wait_until_available(queue, server)
                +        timer = Sequel.start_timer
                +        conn = super
                +        @on_checkout_event.call(Sequel.elapsed_seconds_since(timer), server)
                +        conn
                +      end
                +    end
                +  end
                +
                +  default_callback = proc{}
                +
                +  Database.register_extension(:connection_checkout_event_callback) do |db|
                +    pool = db.pool
                +
                +    case pool.pool_type
                +    when :timed_queue
                +      db.pool.extend(ConnectionCheckoutEventCallbacks::TimedQueue)
                +    when :sharded_timed_queue
                +      db.pool.extend(ConnectionCheckoutEventCallbacks::ShardedTimedQueue)
                +    else
                +      raise Error, "the connection_checkout_event_callback extension is only supported when using a timed_queue connection pool"
                +    end
                +
                +    pool.on_checkout_event ||= default_callback
                +  end
                +end
        lib/sequel/extensions/lit_require_frozen.rb
                --- /tmp/20260504-1933-fkhhey	2026-05-04 02:36:11.458864350 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/extensions/lit_require_frozen.rb	2026-05-04 02:36:11.435863839 +0000
                @@ -0,0 +1,131 @@
                +# frozen-string-literal: true
                +#
                +# The lit_require_frozen extension disallows the use of unfrozen strings
                +# as literal strings in database and dataset methods. If you try to use an
                +# unfrozen string as a literal string for a dataset using this extension,
                +# an exception will be raised.
                +#
                +# While this works for all Ruby versions, it is designed for use on Ruby 3+
                +# where all files are using the frozen-string-literal magic comment. In this
                +# case, uninterpolated literal strings are frozen, but interpolated strings
                +# are not frozen. This allows you to catch potentially dangerous code:
                +#
                +#   # Probably safe, no exception raised
                +#   DB["SELECT * FROM t WHERE c > :v", v: user_provided_string)
                +#
                +#   # Potentially unsafe, raises Sequel::LitRequireFrozen::Error
                +#   DB["SELECT * FROM t WHERE c > '#{user_provided_string}'"]
                +#
                +# The assumption made is that a frozen string is unlikely to contain unsafe
                +# input, while an unfrozen string has potentially been interpolated and may
                +# contain unsafe input.
                +#
                +# This disallows the the following cases:
                +#
                +# * Sequel::LiteralString instances that are unfrozen and are not based on a
                +#   frozen string
                +# * Sequel::SQL::PlaceholderLiteralString instances when the placeholder string
                +#   is not frozen
                +# * Unfrozen strings passed to Database#<< or #[] or Dataset#with_sql
                +#
                +# To use this extension, load it into the database:
                +#
                +#   DB.extension :lit_require_frozen
                +#
                +# It can also be loaded into individual datasets:
                +#
                +#  ds = DB[:t].extension(:lit_require_frozen)
                +#
                +# Assuming you have good test coverage, it is recommended to only load
                +# this extension when testing.
                +#
                +# Related module: Sequel::LitRequireFrozen
                +
                +#
                +module Sequel
                +  class LiteralString
                +    # The string used when creating the literal string (first argument to
                +    # Sequel::LiteralString.new). This may be nil if no string was provided,
                +    # or if the litral string was created before this extension was required.
                +    attr_reader :source
                +
                +    def initialize(*a)
                +      @source = a.first
                +      super
                +    end
                +    # :nocov:
                +    ruby2_keywords :initialize if respond_to?(:ruby2_keywords, true)
                +    # :nocov:
                +  end
                +
                +  module LitRequireFrozen
                +    # Error class raised for using unfrozen literal string.
                +    class Error < Sequel::Error
                +    end
                +
                +    module DatabaseMethods
                +      def self.extended(db)
                +        db.extend_datasets(DatasetMethods)
                +      end
                +
                +      # Check given SQL is frozen before running it.
                +      def run(sql, opts=OPTS)
                +        @default_dataset.with_sql(sql)
                +        super
                +      end
                +    end
                +
                +    module DatasetMethods
                +      # Check given SQL is not an unfrozen string.
                +      def with_sql(sql, *args)
                +        _check_unfrozen_literal_string(sql)
                +        super
                +      end
                +
                +      # Check that placeholder string is frozen (or all entries
                +      # in placeholder array are frozen).
                +      def placeholder_literal_string_sql_append(sql, pls)
                +        str = pls.str
                +
                +        if str.is_a?(Array)
                +          str.each do |s|
                +            _check_unfrozen_literal_string(s)
                +          end
                +        else
                +          _check_unfrozen_literal_string(str)
                +        end
                +
                +        super
                +      end
                +
                +      private
                +
                +      # Base method that other methods used to check for whether a string should be allowed
                +      # as literal SQL. Allows non-strings as well as frozen strings.
                +      def _check_unfrozen_literal_string(str)
                +        return if !str.is_a?(String) || str.frozen?
                +
                +        if str.is_a?(LiteralString)
                +          _check_unfrozen_literal_string(str.source)
                +        else
                +          raise Error, "cannot treat unfrozen string as literal SQL: #{str.inspect}"
                +        end
                +      end
                +
                +      # Check literal strings appended to SQL.
                +      def literal_literal_string_append(sql, v)
                +        _check_unfrozen_literal_string(v)
                +        super
                +      end
                +
                +      # Check static SQL is not frozen.
                +      def static_sql(sql)
                +        _check_unfrozen_literal_string(sql)
                +        super
                +      end
                +    end
                +  end
                +
                +  Dataset.register_extension(:lit_require_frozen, LitRequireFrozen::DatasetMethods)
                +  Database.register_extension(:lit_require_frozen, LitRequireFrozen::DatabaseMethods)
                +end
      * Changed:
        MIT-LICENSE
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/MIT-LICENSE	2026-05-04 02:36:11.385862728 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/MIT-LICENSE	2026-05-04 02:36:11.420863506 +0000
                @@ -2 +2 @@
                -Copyright (c) 2008-2023 Jeremy Evans
                +Copyright (c) 2008-2026 Jeremy Evans and contributors
        lib/sequel/adapters/jdbc/derby.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/adapters/jdbc/derby.rb	2026-05-04 02:36:11.387862772 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/adapters/jdbc/derby.rb	2026-05-04 02:36:11.421863528 +0000
                @@ -2,0 +3,2 @@
                +# SEQUEL6: Remove
                +
        lib/sequel/adapters/jdbc/h2.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/adapters/jdbc/h2.rb	2026-05-04 02:36:11.387862772 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/adapters/jdbc/h2.rb	2026-05-04 02:36:11.421863528 +0000
                @@ -21 +21 @@
                -          run("COMMIT TRANSACTION #{transaction_id}", opts)
                +          run("COMMIT TRANSACTION #{transaction_id}".freeze, opts)
                @@ -39 +39 @@
                -          run("ROLLBACK TRANSACTION #{transaction_id}", opts)
                +          run("ROLLBACK TRANSACTION #{transaction_id}".freeze, opts)
        lib/sequel/adapters/postgres.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/adapters/postgres.rb	2026-05-04 02:36:11.390862839 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/adapters/postgres.rb	2026-05-04 02:36:11.423863572 +0000
                @@ -730 +730 @@
                -            LiteralString.new("#{prepared_arg_placeholder}#{i}")
                +            LiteralString.new("#{prepared_arg_placeholder}#{i}".freeze)
        lib/sequel/adapters/shared/mssql.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/adapters/shared/mssql.rb	2026-05-04 02:36:11.390862839 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/adapters/shared/mssql.rb	2026-05-04 02:36:11.424863595 +0000
                @@ -109 +109 @@
                -        sql = "DECLARE #{declarations.join(', ')}; EXECUTE @RC = #{name} #{values.join(', ')}; SELECT #{names.join(', ')}"
                +        sql = "DECLARE #{declarations.join(', ')}; EXECUTE @RC = #{name} #{values.join(', ')}; SELECT #{names.join(', ')}".freeze
                @@ -403 +403 @@
                -        run(ds.into(name).sql)
                +        run(ds.into(name).sql.freeze)
                @@ -881 +881 @@
                -          sql = multi_insert_sql(columns, values)[0]
                +          sql = multi_insert_sql(columns, values)[0].freeze
        lib/sequel/adapters/shared/mysql.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/adapters/shared/mysql.rb	2026-05-04 02:36:11.391862861 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/adapters/shared/mysql.rb	2026-05-04 02:36:11.424863595 +0000
                @@ -43 +43 @@
                -        run("XA COMMIT #{literal(transaction_id)}", opts)
                +        run("XA COMMIT #{literal(transaction_id)}".freeze, opts)
                @@ -106 +106 @@
                -        metadata_dataset.with_sql(sql).each do |r|
                +        metadata_dataset.with_sql(sql.freeze).each do |r|
                @@ -118 +118 @@
                -        run("XA ROLLBACK #{literal(transaction_id)}", opts)
                +        run("XA ROLLBACK #{literal(transaction_id)}".freeze, opts)
                @@ -783 +783,2 @@
                -        ds = db.send(:metadata_dataset).with_sql(((opts[:extended] && (db.mariadb? || db.server_version < 50700)) ? 'EXPLAIN EXTENDED ' : 'EXPLAIN ') + select_sql).naked
                +        sql = ((opts[:extended] && (db.mariadb? || db.server_version < 50700)) ? 'EXPLAIN EXTENDED ' : 'EXPLAIN ') + select_sql
                +        ds = db.send(:metadata_dataset).with_sql(sql.freeze).naked
        lib/sequel/adapters/shared/postgres.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/adapters/shared/postgres.rb	2026-05-04 02:36:11.392862883 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/adapters/shared/postgres.rb	2026-05-04 02:36:11.425863617 +0000
                @@ -342 +342 @@
                -        run("COMMIT PREPARED #{literal(transaction_id)}", opts)
                +        run("COMMIT PREPARED #{literal(transaction_id)}".freeze, opts)
                @@ -419 +419 @@
                -          run("ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(column)} DROP DEFAULT", server_hash)
                +          run("ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(column)} DROP DEFAULT".freeze, server_hash)
                @@ -456 +456 @@
                -        self << create_function_sql(name, definition, opts)
                +        self << create_function_sql(name, definition, opts).freeze
                @@ -467 +467 @@
                -        self << create_language_sql(name, opts)
                +        self << create_language_sql(name, opts).freeze
                @@ -476 +476 @@
                -        self << create_schema_sql(name, opts)
                +        self << create_schema_sql(name, opts).freeze
                @@ -512 +512 @@
                -        self << create_trigger_sql(table, name, function, opts)
                +        self << create_trigger_sql(table, name, function, opts).freeze
                @@ -545 +545 @@
                -        run "DO #{"LANGUAGE #{literal(language.to_s)} " if language}#{literal(code)}"
                +        run "DO #{"LANGUAGE #{literal(language.to_s)} " if language}#{literal(code)}".freeze
                @@ -555 +555 @@
                -        self << drop_function_sql(name, opts)
                +        self << drop_function_sql(name, opts).freeze
                @@ -564 +564 @@
                -        self << drop_language_sql(name, opts)
                +        self << drop_language_sql(name, opts).freeze
                @@ -573 +573 @@
                -        self << drop_schema_sql(name, opts)
                +        self << drop_schema_sql(name, opts).freeze
                @@ -584 +584 @@
                -        self << drop_trigger_sql(table, name, opts)
                +        self << drop_trigger_sql(table, name, opts).freeze
                @@ -748 +748 @@
                -        self << rename_schema_sql(name, new_name)
                +        self << rename_schema_sql(name, new_name).freeze
                @@ -759 +759 @@
                -        run "REFRESH MATERIALIZED VIEW#{' CONCURRENTLY' if opts[:concurrently]} #{quote_schema_table(name)}"
                +        run "REFRESH MATERIALIZED VIEW#{' CONCURRENTLY' if opts[:concurrently]} #{quote_schema_table(name)}".freeze
                @@ -772 +772 @@
                -          seq_ds = metadata_dataset.from(:pg_sequence).where(:seqrelid=>regclass_oid(LiteralString.new(seq)))
                +          seq_ds = metadata_dataset.from(:pg_sequence).where(:seqrelid=>regclass_oid(LiteralString.new(seq.freeze)))
                @@ -787 +787 @@
                -        run("ROLLBACK PREPARED #{literal(transaction_id)}", opts)
                +        run("ROLLBACK PREPARED #{literal(transaction_id)}".freeze, opts)
                @@ -1472 +1472 @@
                -        cols[-1] = Sequel.lit("PERIOD #{quote_identifier(cols[-1])}")
                +        cols[-1] = Sequel.lit("PERIOD #{quote_identifier(cols[-1])}".freeze)
        lib/sequel/adapters/shared/sqlite.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/adapters/shared/sqlite.rb	2026-05-04 02:36:11.392862883 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/adapters/shared/sqlite.rb	2026-05-04 02:36:11.425863617 +0000
                @@ -198 +198 @@
                -        metadata_dataset.with_sql("PRAGMA table_#{'x' if sqlite_version > 33100}info(?)", input_identifier_meth(opts[:dataset]).call(table_name))
                +        metadata_dataset.with_sql("PRAGMA table_#{'x' if sqlite_version > 33100}info(?)".freeze, input_identifier_meth(opts[:dataset]).call(table_name))
                @@ -410 +410 @@
                -          c[:default] = LiteralString.new(c[:default]) if c[:default]
                +          c[:default] = LiteralString.new(c[:default]).freeze if c[:default]
                @@ -687 +687 @@
                -        ds = db.send(:metadata_dataset).clone(:sql=>"EXPLAIN #{select_sql}")
                +        ds = db.send(:metadata_dataset).clone(:sql=>"EXPLAIN #{select_sql}".freeze)
        lib/sequel/adapters/sqlite.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/adapters/sqlite.rb	2026-05-04 02:36:11.392862883 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/adapters/sqlite.rb	2026-05-04 02:36:11.426863639 +0000
                @@ -391 +391 @@
                -          LiteralString.new("#{prepared_arg_placeholder}#{k.to_s.gsub('.', '__')}")
                +          LiteralString.new("#{prepared_arg_placeholder}#{k.to_s.gsub('.', '__')}".freeze)
        lib/sequel/adapters/tinytds.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/adapters/tinytds.rb	2026-05-04 02:36:11.392862883 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/adapters/tinytds.rb	2026-05-04 02:36:11.426863639 +0000
                @@ -208 +208 @@
                -          LiteralString.new("@#{k.to_s.gsub('.', '__')}")
                +          LiteralString.new("@#{k.to_s.gsub('.', '__')}".freeze)
        lib/sequel/connection_pool.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/connection_pool.rb	2026-05-04 02:36:11.394862928 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/connection_pool.rb	2026-05-04 02:36:11.427863661 +0000
                @@ -133,0 +134,6 @@
                +  # Only for use by extension that need to disconnect a connection inside acquire.
                +  # Takes the connection and any arguments accepted by acquire.
                +  def disconnect_acquired_connection(conn, *)
                +    disconnect_connection(conn)
                +  end
                +
        lib/sequel/connection_pool/sharded_timed_queue.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/connection_pool/sharded_timed_queue.rb	2026-05-04 02:36:11.394862928 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/connection_pool/sharded_timed_queue.rb	2026-05-04 02:36:11.427863661 +0000
                @@ -74 +74 @@
                -          break unless (conn = queue.pop(timeout: 0)) && !conns[conn]
                +          break unless (conn = available(queue, server)) && !conns[conn]
                @@ -94 +94 @@
                -  # and then the method will only disconnect connectsion from those specified shards.
                +  # and then the method will only disconnect connections from those specified shards.
                @@ -98 +98,3 @@
                -      while conn = queue.pop(timeout: 0)
                +      nconns = 0
                +      while conn = available(queue, server)
                +        nconns += 1
                @@ -101 +103 @@
                -      fill_queue(server)
                +      fill_queue(server, nconns)
                @@ -135 +137 @@
                -        fill_queue(server)
                +        fill_queue(server, 1)
                @@ -170 +172 @@
                -        while conn = queue.pop(timeout: 0)
                +        while conn = available(queue, server)
                @@ -227,0 +230,6 @@
                +  # Only for use by extension that need to disconnect a connection inside acquire.
                +  # Takes the connection and any arguments accepted by acquire.
                +  def disconnect_acquired_connection(conn, _, server)
                +    disconnect_pool_connection(conn, server)
                +  end
                +
                @@ -247 +255,4 @@
                -  def fill_queue(server)
                +  #
                +  # nconns specifies the maximum number of connections to add, which should
                +  # be the number of connections that were disconnected.
                +  def fill_queue(server, nconns)
                @@ -249 +260 @@
                -    if queue.num_waiting > 0
                +    if nconns > 0 && queue.num_waiting > 0
                @@ -251 +262,2 @@
                -        while queue.num_waiting > 0 && (conn = try_make_new(server))
                +        while nconns > 0 && queue.num_waiting > 0 && (conn = try_make_new(server))
                +          nconns -= 1
                @@ -300 +312 @@
                -        fill_queue(server)
                +        fill_queue(server, to_disconnect.size)
                @@ -314 +326 @@
                -    if conn = queue.pop(timeout: 0) || try_make_new(server) || queue.pop(timeout: @timeout)
                +    if conn = available(queue, server) || try_make_new(server) || wait_until_available(queue, server)
                @@ -319,0 +332,13 @@
                +  end
                +
                +  # Return the next connection in the pool if there is one available. Returns nil
                +  # if no connection is currently available.
                +  def available(queue, _server)
                +    queue.pop(timeout: 0)
                +  end
                +
                +  # Return the next connection in the pool if there is one available. If not, wait
                +  # until the timeout for a connection to become available. If there is still no
                +  # available connection, return nil.
                +  def wait_until_available(queue, _server)
                +    queue.pop(timeout: @timeout)
        lib/sequel/connection_pool/timed_queue.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/connection_pool/timed_queue.rb	2026-05-04 02:36:11.394862928 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/connection_pool/timed_queue.rb	2026-05-04 02:36:11.427863661 +0000
                @@ -45 +45 @@
                -          break unless (conn = @queue.pop(timeout: 0)) && !conns[conn]
                +          break unless (conn = available) && !conns[conn]
                @@ -62 +62,3 @@
                -    while conn = @queue.pop(timeout: 0)
                +    nconns = 0
                +    while conn = available
                +      nconns += 1
                @@ -65 +67 @@
                -    fill_queue
                +    fill_queue(nconns)
                @@ -97 +99 @@
                -        fill_queue
                +        fill_queue(1)
                @@ -159,2 +161,5 @@
                -  def fill_queue
                -    if @queue.num_waiting > 0
                +  #
                +  # nconns specifies the maximum number of connections to add, which should
                +  # be the number of connections that were disconnected.
                +  def fill_queue(nconns)
                +    if nconns > 0 && @queue.num_waiting > 0
                @@ -162 +167 @@
                -        while @queue.num_waiting > 0 && (conn = try_make_new)
                +        while nconns > 0 && @queue.num_waiting > 0 && (conn = try_make_new)
                @@ -210 +215 @@
                -        fill_queue
                +        fill_queue(to_disconnect.size)
                @@ -223 +228 @@
                -    if conn = @queue.pop(timeout: 0) || try_make_new || @queue.pop(timeout: @timeout)
                +    if conn = available || try_make_new || wait_until_available
                @@ -228,0 +234,13 @@
                +  end
                +
                +  # Return the next connection in the pool if there is one available. Returns nil
                +  # if no connection is currently available.
                +  def available
                +    @queue.pop(timeout: 0)
                +  end
                +
                +  # Return the next connection in the pool if there is one available. If not, wait
                +  # until the timeout for a connection to become available. If there is still no
                +  # available connection, return nil.
                +  def wait_until_available
                +    @queue.pop(timeout: @timeout)
        lib/sequel/database/schema_generator.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/database/schema_generator.rb	2026-05-04 02:36:11.396862972 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/database/schema_generator.rb	2026-05-04 02:36:11.429863706 +0000
                @@ -5,0 +6,27 @@
                +    module ColumnOptionMerger
                +      private
                +
                +      # Merge given options into the column's default options. For backwards compatibility,
                +      # the options take priority, but in cases where the option value overrides the argument
                +      # value, and the values are different, we warn as this is likely to be an error in the
                +      # code.
                +      def _merge_column_options(defaults, opts)
                +        defaults.merge!(opts) do |k, defv, v|
                +          unless defv == v
                +            # :nocov:
                +            if RUBY_VERSION >= "3.2"
                +            # :nocov:
                +              caller_loc = Thread.each_caller_location do |loc|
                +                break loc unless loc.path == __FILE__
                +              end
                +              caller_loc &&= "#{caller_loc.path}:#{caller_loc.lineno}: "
                +            end
                +            warn("#{caller_loc}#{k.inspect} option value (#{v.inspect}) overrides argument value (#{defv.inspect})")
                +          end
                +
                +          v
                +        end
                +      end
                +    end
                +    private_constant :ColumnOptionMerger
                +
                @@ -19,0 +47,2 @@
                +      include ColumnOptionMerger
                +
                @@ -176 +205 @@
                -        columns << {:name => name, :type => type}.merge!(opts)
                +        columns << _merge_column_options({:name => name, :type => type}, opts)
                @@ -182 +211 @@
                -      
                +
                @@ -249 +278 @@
                -        column(name, Integer, opts)
                +        column(name, opts.fetch(:type, Integer), opts)
                @@ -431,0 +461,2 @@
                +      include ColumnOptionMerger
                +
                @@ -457 +488 @@
                -        op = {:op => :add_column, :name => name, :type => type}.merge!(opts)
                +        op = _merge_column_options({:op => :add_column, :name => name, :type => type}, opts)
                @@ -522 +553 @@
                -        add_column(name, Integer, {:table=>table}.merge!(opts))
                +        add_column(name, opts.fetch(:type, Integer), {:table=>table}.merge!(opts))
        lib/sequel/database/schema_methods.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/database/schema_methods.rb	2026-05-04 02:36:11.396862972 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/database/schema_methods.rb	2026-05-04 02:36:11.429863706 +0000
                @@ -822 +822 @@
                -      run(create_table_as_sql(name, sql, options))
                +      run(create_table_as_sql(name, sql, options).freeze)
        lib/sequel/dataset/placeholder_literalizer.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/dataset/placeholder_literalizer.rb	2026-05-04 02:36:11.397862995 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/dataset/placeholder_literalizer.rb	2026-05-04 02:36:11.430863728 +0000
                @@ -119,0 +120,2 @@
                +          frags.each(&:freeze)
                +          frags.freeze
                @@ -127,0 +130 @@
                +          ds.opts[:sql].freeze
        lib/sequel/dataset/prepared_statements.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/dataset/prepared_statements.rb	2026-05-04 02:36:11.397862995 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/dataset/prepared_statements.rb	2026-05-04 02:36:11.430863728 +0000
                @@ -57 +57 @@
                -        sql = prepared_sql
                +        sql = prepared_sql.freeze
                @@ -226 +226 @@
                -          with_sql(prepared_sql)
                +          with_sql(prepared_sql.freeze)
                @@ -290 +290 @@
                -          sql = literal(Sequel::SQL::PlaceholderLiteralString.new(@opts[:prepared_sql_frags], @opts[:bind_arguments], false))
                +          sql = literal(Sequel::SQL::PlaceholderLiteralString.new(@opts[:prepared_sql_frags], @opts[:bind_arguments], false)).freeze
                @@ -322,0 +323,3 @@
                +        frags.freeze
                +        frags.each(&:freeze)
                +        prepared_sql.freeze
                @@ -412 +415 @@
                -        sql = ps.prepared_sql
                +        sql = ps.prepared_sql.freeze
        lib/sequel/dataset/query.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/dataset/query.rb	2026-05-04 02:36:11.398863017 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/dataset/query.rb	2026-05-04 02:36:11.431863750 +0000
                @@ -1304 +1304 @@
                -        sql = public_send(sql, *args)
                +        sql = public_send(sql, *args).freeze
                @@ -1477 +1477,4 @@
                -    
                +
                +    PAREN_WRAPPER = ["(".freeze, ")".freeze].freeze
                +    private_constant :PAREN_WRAPPER
                +
                @@ -1498 +1501 @@
                -        LiteralString.new("(#{expr})")
                +        SQL::PlaceholderLiteralString.new(PAREN_WRAPPER, [expr])
        lib/sequel/dataset/sql.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/dataset/sql.rb	2026-05-04 02:36:11.398863017 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/dataset/sql.rb	2026-05-04 02:36:11.431863750 +0000
                @@ -56 +56 @@
                -          sql << v
                +          literal_literal_string_append(sql, v)
                @@ -1424,0 +1425,5 @@
                +    end
                +
                +    # Append string to SQL string.
                +    def literal_literal_string_append(sql, v)
                +      sql << v
        lib/sequel/extensions/connection_expiration.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/extensions/connection_expiration.rb	2026-05-04 02:36:11.399863039 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/extensions/connection_expiration.rb	2026-05-04 02:36:11.432863773 +0000
                @@ -94 +94 @@
                -          disconnect_connection(conn)
                +          disconnect_acquired_connection(conn, *a)
        lib/sequel/extensions/connection_validator.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/extensions/connection_validator.rb	2026-05-04 02:36:11.399863039 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/extensions/connection_validator.rb	2026-05-04 02:36:11.432863773 +0000
                @@ -121 +121 @@
                -              disconnect_connection(conn)
                +              disconnect_acquired_connection(conn, *a)
        lib/sequel/extensions/dataset_run.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/extensions/dataset_run.rb	2026-05-04 02:36:11.400863061 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/extensions/dataset_run.rb	2026-05-04 02:36:11.433863795 +0000
                @@ -33 +33 @@
                -        db.run(sql, :server=>server)
                +        db.run(sql.freeze, :server=>server)
                @@ -35 +35 @@
                -        db.run(sql)
                +        db.run(sql.freeze)
        lib/sequel/extensions/date_arithmetic.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/extensions/date_arithmetic.rb	2026-05-04 02:36:11.400863061 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/extensions/date_arithmetic.rb	2026-05-04 02:36:11.433863795 +0000
                @@ -85,2 +85,2 @@
                -        MYSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s.upcase[0...-1]).freeze}).freeze
                -        MSSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s[0...-1]).freeze}).freeze
                +        MYSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s.upcase[0...-1].freeze).freeze}).freeze
                +        MSSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s[0...-1].freeze).freeze}).freeze
                @@ -88 +88 @@
                -        DERBY_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit("SQL_TSI_#{s.to_s.upcase[0...-1]}").freeze}).freeze
                +        DERBY_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit("SQL_TSI_#{s.to_s.upcase[0...-1]}".freeze).freeze}).freeze
                @@ -90 +90 @@
                -        DB2_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s).freeze}).freeze
                +        DB2_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s.freeze).freeze}).freeze
                @@ -110 +110 @@
                -                placeholder << "#{', ' unless placeholder.empty?}#{sql_unit} := "
                +                placeholder << "#{', ' unless placeholder.empty?}#{sql_unit} := ".freeze
                @@ -159 +159 @@
                -              expr = Sequel.lit(["{fn timestampadd(#{sql_unit}, ", ", timestamp(", "))}"], value, expr)
                +              expr = Sequel.lit(["{fn timestampadd(#{sql_unit}, ".freeze, ", timestamp(", "))}"], value, expr)
        lib/sequel/extensions/migration.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/extensions/migration.rb	2026-05-04 02:36:11.402863106 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/extensions/migration.rb	2026-05-04 02:36:11.435863839 +0000
                @@ -189,16 +189,13 @@
                -      begin
                -        instance_exec(&block)
                -      rescue
                -        just_raise = true
                -      end
                -      if just_raise
                -        Proc.new{raise Sequel::Error, "irreversible migration method used in #{block.source_location.first}, you may need to write your own down method"}
                -      else
                -        actions = @actions.reverse
                -        Proc.new do
                -          actions.each do |a|
                -            pr = a.last.is_a?(Proc) ? a.pop : nil
                -            # Allow calling private methods as the reversing methods are private
                -            send(*a, &pr)
                -          end
                -        end
                +      instance_exec(&block)
                +    rescue NoMethodError => e
                +      Proc.new{raise Sequel::Error, "irreversible migration method \"#{e.name}\" used in #{block.source_location.first}, you may need to write your own down method"}
                +    rescue => e
                +      Proc.new{raise Sequel::Error, "unable to reverse migration due to #{e.class} in #{block.source_location.first}, you may need to write your own down method"}
                +    else
                +      actions = @actions.reverse
                +      Proc.new do
                +       actions.each do |a|
                +        pr = a.last.is_a?(Proc) ? a.pop : nil
                +        # Allow calling private methods as the reversing methods are private
                +        send(*a, &pr)
                +       end
                @@ -273 +270 @@
                -      raise if args.first.is_a?(Array)
                +      super if args.first.is_a?(Array)
        lib/sequel/extensions/pg_enum.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/extensions/pg_enum.rb	2026-05-04 02:36:11.403863128 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/extensions/pg_enum.rb	2026-05-04 02:36:11.436863861 +0000
                @@ -130 +130 @@
                -        run(sql)
                +        run(sql.freeze)
        lib/sequel/model/associations.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/model/associations.rb	2026-05-04 02:36:11.409863261 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/model/associations.rb	2026-05-04 02:36:11.443864017 +0000
                @@ -114,0 +115,2 @@
                +          when :lateral_subquery
                +            apply_lateral_subquery_eager_graph_limit_strategy(ds)
                @@ -301 +303,6 @@
                -            objects = apply_eager_limit_strategy(ds, strategy, eager_limit).all
                +
                +            objects = if strategy == :lateral_subquery
                +              apply_lateral_subquery_eager_limit_strategy(ds, ids, eager_limit).all
                +            else
                +              apply_eager_limit_strategy(ds, strategy, eager_limit).all
                +            end
                @@ -323 +330 @@
                -              objects.concat(ds.with_sql(sql).to_a)
                +              objects.concat(ds.with_sql(sql.freeze).to_a)
                @@ -369 +376,6 @@
                -          ds = filter_by_associations_conditions_dataset.where(filter_by_associations_conditions_subquery_conditions(obj))
                +          ds = if filter_by_associations_limit_strategy == :lateral_subquery
                +            apply_lateral_subquery_filter_limit_strategy(associated_eager_dataset, obj)
                +          else
                +            filter_by_associations_conditions_dataset.where(filter_by_associations_conditions_subquery_conditions(obj))
                +          end
                +
                @@ -756,0 +769,55 @@
                +        def lateral_subquery_eager_limit_strategy_lateral_dataset(ds, limit_and_offset)
                +          ds.
                +            where(Array(filter_by_associations_conditions_key).zip(Array(filter_by_associations_conditions_associated_keys))).
                +            limit(*limit_and_offset).
                +            lateral
                +        end
                +
                +        def apply_lateral_subquery_eager_limit_strategy(ds, ids, limit_and_offset)
                +          table_name = self[:model].table_name
                +          associated_table_name = associated_class.table_name
                +
                +          associated_class.
                +            from(table_name).
                +            select_all(associated_table_name).
                +            join(lateral_subquery_eager_limit_strategy_lateral_dataset(ds, limit_and_offset).as(associated_table_name), true).
                +            order(*self[:order])
                +        end
                +
                +        # Return an expression to filter the filter by associations dataset to only
                +        # rows related to given objects.
                +        def _lateral_subquery_filter_limit_strategy_conditions(obj, key, value_method, value_column)
                +          value = case obj
                +          when Array
                +            if key.is_a?(Array)
                +              key_methods = Array(value_method)
                +              obj.map{|o| key_methods.map{|meth| o.send(meth)}}
                +            else
                +              obj.map{|o| o.send(value_method)}
                +            end
                +          when Sequel::Dataset
                +            obj.select(*Array(qualify(associated_class.table_name, value_column)))
                +          else
                +            if key.is_a?(Array)
                +              return Array(key).zip(Array(value_method).map{|meth| obj.send(meth)})
                +            else
                +              obj.send(value_method)
                +            end
                +          end
                +
                +          {key => value}
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_lateral_dataset(ds, obj)
                +          lateral_subquery_filter_limit_strategy_filter_lateral_dataset(ds.
                +            select(*qualify(associated_class.table_name, associated_class.primary_key)).
                +            limit(*limit_and_offset).
                +            lateral)
                +        end
                +
                +        def apply_lateral_subquery_filter_limit_strategy(ds, obj)
                +          lateral_subquery_filter_limit_strategy_filter_dataset(self[:model].
                +            select(*lateral_subquery_filter_limit_strategy_lateral_dataset_select).
                +            join(lateral_subquery_filter_limit_strategy_lateral_dataset(ds, obj).as(associated_class.table_name), filter_by_associations_conditions_subquery_conditions(obj)), obj)
                +        end
                +
                @@ -779 +846,6 @@
                -              apply_eager_limit_strategy(ds.where(predicate_key=>arg), eager_limit_strategy)
                +              strategy = eager_limit_strategy
                +              if strategy == :lateral_subquery
                +                apply_lateral_subquery_eager_limit_strategy(ds, arg, limit_and_offset)
                +              else
                +                apply_eager_limit_strategy(ds.where(predicate_key=>arg), strategy)
                +              end
                @@ -1021 +1093 @@
                -        # Support a correlated subquery limit strategy when using eager_graph.
                +        # Support a lateral_subquery and correlated_subquery limit strategy when using eager_graph.
                @@ -1120,0 +1193,48 @@
                +        # Use a LATERAL subquery to limit the dataset.  Note that this will not
                +        # work correctly if the associated dataset uses qualified identifers in the WHERE clause,
                +        # as they would reference the containing query instead of the subquery.
                +        #
                +        # This does not contain the conditions that are necessary to join to the
                +        # query, since the necessary qualifier is not passed as an argument.
                +        def apply_lateral_subquery_eager_graph_limit_strategy(ds)
                +          table_name = ds.first_source_alias
                +          qualifier = ds.opts[:eager_options][:implicit_qualifier]
                +          graph_conditions = self[:_graph_conditions]
                +
                +          unless Sequel.condition_specifier?(graph_conditions)
                +            raise Error, "lateral_subquery eager graph limit strategy only supported when graph conditions are a hash or array of pairs"
                +          end
                +
                +          ds.
                +            limit(*limit_and_offset).
                +            order(*self[:order]).
                +            where(graph_conditions.map{|k, v| [qualify(table_name, k), qualify(qualifier, v)]}).
                +            lateral
                +        end
                +
                +        # Avoid setting duplicate predicate condition when using the lateral subquery
                +        # eager limit strategy.
                +        def eager_loading_set_predicate_condition(ds, eo)
                +          eager_limit_strategy == :lateral_subquery ? ds : super
                +        end
                +
                +        def apply_lateral_subquery_eager_limit_strategy(ds, ids, limit_and_offset)
                +          super.where(qualify(self[:model].table_name, self[:primary_key]) => ids)
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_conditions(obj)
                +          _lateral_subquery_filter_limit_strategy_conditions(obj, filter_by_associations_conditions_key, self[:key_method], self[:key])
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_filter_lateral_dataset(ds)
                +          ds.where(Array(filter_by_associations_conditions_key).zip(Array(filter_by_associations_conditions_associated_keys)))
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_filter_dataset(ds, obj)
                +          ds.where(lateral_subquery_filter_limit_strategy_conditions(obj))
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_lateral_dataset_select
                +          qualified_primary_key
                +        end
                +
                @@ -1434,0 +1555,46 @@
                +        def lateral_subquery_filter_limit_strategy_conditions(obj)
                +          _lateral_subquery_filter_limit_strategy_conditions(obj, lateral_subquery_filter_limit_strategy_conditions_key, right_primary_key_method, right_primary_key)
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_conditions_key
                +          self[:right_key]
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_filter_lateral_dataset(ds)
                +          ds.where(Array(filter_by_associations_conditions_key).zip(Array(filter_by_associations_conditions_associated_keys)))
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_filter_dataset(ds, obj)
                +          ds.where(filter_by_associations_conditions_key => ds.db.from(self[:join_table]).
                +            select(*self[:left_key]).
                +            where(lateral_subquery_filter_limit_strategy_conditions(obj)))
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_lateral_dataset_select
                +          qualify(self[:model].table_name, self[:left_primary_key])
                +        end
                +
                +        def lateral_subquery_eager_limit_strategy_lateral_dataset(ds, limit_and_offset)
                +          ds = super
                +          select = ds.opts[:select].dup
                +          left_key_alias = self[:left_key_alias]
                +          select.pop while (s = select.last).is_a?(Sequel::SQL::AliasedExpression) && (left_key_alias.is_a?(Array) ? left_key_alias.include?(s.alias) : s.alias == left_key_alias)
                +          ds = ds.clone(:select=>select)
                +        end
                +
                +        def apply_lateral_subquery_eager_limit_strategy(ds, ids, limit_and_offset)
                +          table_name = self[:model].table_name
                +          super.
                +            select_all(associated_class.table_name).
                +            select_append(*qualify(table_name, self[:left_primary_keys]).zip(Array(self[:left_key_alias])).map{|id, aliaz| Sequel.as(id, aliaz)}).
                +            where(qualify(table_name, self[:left_primary_key]) => ids)
                +        end
                +
                +        def apply_lateral_subquery_eager_graph_limit_strategy(ds)
                +          ds.
                +            limit(*limit_and_offset).
                +            where(qualify(join_table_alias, self[:left_keys]).zip(qualify(ds.opts[:eager_options][:implicit_qualifier], self[:left_primary_key_columns]))).
                +            order(*self[:order]).
                +            lateral
                +        end
                +
                @@ -2415,0 +2582 @@
                +          graph_conditions = opts[:_graph_conditions] = use_only_conditions ? only_conditions : cks.zip(pkcs) + conditions
                @@ -2418 +2585,5 @@
                -            ds = ds.graph(opts.apply_eager_graph_limit_strategy(eo[:limit_strategy], eager_graph_dataset(opts, eo)), use_only_conditions ? only_conditions : cks.zip(pkcs) + conditions, eo.merge(:select=>select, :join_type=>eo[:join_type]||join_type, :qualify=>:deep), &graph_block)
                +            graph_limit_strategy = eo[:limit_strategy]
                +            egds = opts.apply_eager_graph_limit_strategy(graph_limit_strategy, eager_graph_dataset(opts, eo))
                +            graph_conditions_true = true if graph_limit_strategy == :lateral_subquery
                +
                +            ds = ds.graph(egds, graph_conditions_true || graph_conditions, eo.merge(:select=>select, :join_type=>eo[:join_type]||join_type, :qualify=>:deep), &graph_block)
                @@ -2500,0 +2672,3 @@
                +          if eager_options[:limit_strategy] == :lateral_subquery
                +            ds = ds.clone(:eager_options=>eager_options)
                +          end
        lib/sequel/plugins/dataset_associations.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/plugins/dataset_associations.rb	2026-05-04 02:36:11.412863328 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/plugins/dataset_associations.rb	2026-05-04 02:36:11.447864106 +0000
                @@ -85 +85 @@
                -          ds = r.associated_class.dataset
                +          klass = r.associated_class
                @@ -86,0 +87,19 @@
                +
                +          if r.send(:filter_by_associations_limit_strategy) == :lateral_subquery
                +            ds = r.send(:associated_eager_dataset)
                +
                +            case r[:type]
                +            when :one_to_one, :one_to_many
                +              sds = sds.select(*Array(r.qualified_primary_key))
                +            else
                +              sds = sds.select(*r[:left_primary_keys])
                +              ds = ds.select_all(klass.table_name)
                +              update_select = true
                +            end
                +
                +            ds = r.send(:apply_lateral_subquery_eager_limit_strategy, ds, sds, r.limit_and_offset)
                +            ds = ds.clone(:select=>ds.opts[:select][0,1]) if update_select
                +            return ds.clone(:eager=>nil, :eager_graph=>nil)
                +          end
                +
                +          ds = klass.dataset
        lib/sequel/plugins/dirty.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/plugins/dirty.rb	2026-05-04 02:36:11.413863350 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/plugins/dirty.rb	2026-05-04 02:36:11.447864106 +0000
                @@ -237,2 +237,5 @@
                -            check_missing_initial_value(column)
                -            iv[column] = get_column_value(column)
                +            if db_schema[column]
                +              check_missing_initial_value(column)
                +              iv[column] = get_column_value(column)
                +            end
                +
        lib/sequel/plugins/many_through_many.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/plugins/many_through_many.rb	2026-05-04 02:36:11.415863395 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/plugins/many_through_many.rb	2026-05-04 02:36:11.449864150 +0000
                @@ -148,0 +149,21 @@
                +        def lateral_subquery_filter_limit_strategy_conditions_key
                +          qualify(reverse_edges.first[:table], reverse_edges.first[:left])
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_filter_lateral_dataset(ds)
                +          ds.where(Array(qualify(edges.first[:table], edges.first[:right])).zip(Array(qualify(self[:model].table_name, edges.first[:left]))))
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_filter_dataset(ds, obj)
                +          first_edge, *remaining_edges = edges
                +          filter_ds = ds.db.from(first_edge[:table]).
                +            select(*qualify(first_edge[:table], first_edge[:right])).
                +            where(lateral_subquery_filter_limit_strategy_conditions(obj))
                +
                +          remaining_edges.each do |edge|
                +            filter_ds = filter_ds.join(edge[:table], Array(edge[:right]).zip(Array(edge[:left])))
                +          end
                +
                +          ds.where(qualify(self[:model].table_name, self[:left_primary_key]) => filter_ds)
                +        end
                +
        lib/sequel/plugins/mssql_optimistic_locking.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/plugins/mssql_optimistic_locking.rb	2026-05-04 02:36:11.415863395 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/plugins/mssql_optimistic_locking.rb	2026-05-04 02:36:11.449864150 +0000
                @@ -58 +58 @@
                -          rows = ds.clone(ds.send(:default_server_opts, :sql=>ds.output(nil, [Sequel[:inserted][lc]]).update_sql(columns))).all
                +          rows = ds.clone(ds.send(:default_server_opts, :sql=>ds.output(nil, [Sequel[:inserted][lc]]).update_sql(columns).freeze)).all
        lib/sequel/plugins/pg_xmin_optimistic_locking.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/plugins/pg_xmin_optimistic_locking.rb	2026-05-04 02:36:11.416863417 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/plugins/pg_xmin_optimistic_locking.rb	2026-05-04 02:36:11.450864173 +0000
                @@ -102 +102 @@
                -          rows = ds.clone(ds.send(:default_server_opts, :sql=>ds.returning(:xmin).update_sql(columns))).all
                +          rows = ds.clone(ds.send(:default_server_opts, :sql=>ds.returning(:xmin).update_sql(columns).freeze)).all
        lib/sequel/plugins/serialization.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/plugins/serialization.rb	2026-05-04 02:36:11.416863417 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/plugins/serialization.rb	2026-05-04 02:36:11.451864195 +0000
                @@ -32,0 +33,9 @@
                +    # Note that use of an unsafe serialization method can result in an attack vector
                +    # (potentially allowing remote code execution) if an attacker has the ability to
                +    # store data directly in the underlying column. This would affect the marshal
                +    # serialization format, and on older versions of Ruby, potentially the yaml and
                +    # json serialization formats as well. It can also affect custom formats. You
                +    # should ensure that attackers do not have access to store data directly in the
                +    # underlying column when using this plugin (especially when using an unsafe
                +    # serialization method).
                +    #
                @@ -100 +109 @@
                -          v = v.unpack('m')[0] unless v[0..1] == "\x04\x08"
                +          v = v.unpack('m')[0] unless v.start_with?("\x04\x08")
        lib/sequel/version.rb
                --- /tmp/d20260504-1933-il36x8/sequel-5.101.0/lib/sequel/version.rb	2026-05-04 02:36:11.420863506 +0000
                +++ /tmp/d20260504-1933-il36x8/sequel-5.104.0/lib/sequel/version.rb	2026-05-04 02:36:11.454864261 +0000
                @@ -9 +9 @@
                -  MINOR = 101
                +  MINOR = 104

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

gem compare bigdecimal 4.0.1 4.1.2

Compared versions: ["4.0.1", "4.1.2"]
  DIFFERENT require_paths:
    4.0.1: ["/opt/hostedtoolcache/Ruby/4.0.3/x64/lib/ruby/gems/4.0.0/extensions/x86_64-linux/4.0.0/bigdecimal-4.0.1", "lib"]
    4.1.2: ["/opt/hostedtoolcache/Ruby/4.0.3/x64/lib/ruby/gems/4.0.0/extensions/x86_64-linux/4.0.0/bigdecimal-4.1.2", "lib"]
  DIFFERENT required_ruby_version:
    4.0.1: >= 2.5.0
    4.1.2: >= 2.6.0
  DIFFERENT version:
    4.0.1: 4.0.1
    4.1.2: 4.1.2
  DIFFERENT files:
    4.0.1->4.1.2:
      * Added:
            ext/bigdecimal/div.h +192/-0
            ext/bigdecimal/ntt.h +191/-0
            sig/big_decimal.rbs +1502/-0
            sig/big_decimal_util.rbs +158/-0
            sig/big_math.rbs +423/-0
      * Changed:
            bigdecimal.gemspec +6/-1
            ext/bigdecimal/bigdecimal.c +263/-262
            ext/bigdecimal/bigdecimal.h +43/-37
            ext/bigdecimal/extconf.rb +5/-2
            ext/bigdecimal/missing.h +4/-2
            ext/bigdecimal/missing/dtoa.c +183/-136
            lib/bigdecimal.rb +104/-60
            lib/bigdecimal/math.rb +103/-124
            lib/bigdecimal/util.rb +1/-1
            sample/linear.rb +73/-37
              (!) Shebang probably lost: #!/usr/local/bin/ruby
            sample/nlsolve.rb +47/-30
              (!) Shebang probably lost: #!/usr/local/bin/ruby
            sample/pi.rb +2/-7
              (!) Shebang probably lost: #!/usr/local/bin/ruby

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

gem compare --diff bigdecimal 4.0.1 4.1.2

Diff too large (211450 chars)

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

gem compare --diff bigdecimal 4.0.1 4.1.2

Diff too large (211479 chars)

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

gem compare --diff sequel 5.101.0 5.104.0

Compared versions: ["5.101.0", "5.104.0"]
  DIFFERENT files:
    5.101.0->5.104.0:
      * Added:
        lib/sequel/extensions/connection_checkout_event_callback.rb
                --- /tmp/20260504-1831-1webe1	2026-05-04 02:36:32.135216379 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/extensions/connection_checkout_event_callback.rb	2026-05-04 02:36:32.110216596 +0000
                @@ -0,0 +1,151 @@
                +# frozen-string-literal: true
                +#
                +# The connection_checkout_event_callback extension modifies a database's
                +# connection pool to allow for a checkout event callback. This callback is
                +# called with the following arguments:
                +#
                +# :immediately_available :: Connection immediately available and returned
                +# :not_immediately_available :: Connection not immediately available
                +# :new_connection :: New connection created and returned
                +# Float :: Number of seconds waiting to acquire a connection
                +#
                +# This is a low-level extension that allows for building telemetry
                +# information. It doesn't implement any telemetry reporting itself. The
                +# main reason for recording this information is to use it to determine the
                +# appropriate size for the connection pool. Having too large a connection
                +# pool can waste resources, while having too small a connection pool can
                +# result in substantial time to check out a connection. In general, you
                +# want to use as small a pool as possible while keeping the time to
                +# checkout a connection low.
                +#
                +# To use the connection checkout event callback, you must first load the
                +# extension:
                +#
                +#   DB.extension(:connection_checkout_event_callback)
                +#
                +# By default, an empty proc is used as the callback so that loading the
                +# support doesn't break anything. If you are using the extension, you
                +# should set the callback at some point during application startup:
                +#
                +#   DB.pool.on_checkout_event = proc do |event|
                +#     # ...
                +#   end
                +#
                +# When using the sharded connection pool, the callback is also
                +# passed a second argument, the requested server shard (generally a
                +# symbol), allowing for collection of per-shard telemetry:
                +#
                +#   DB.pool.on_checkout_event = proc do |event, server|
                +#     # ...
                +#   end
                +#
                +# Note that the callback may be called currently by multiple threads.
                +# You should use some form of concurrency control inside the callback,
                +# such as a mutex or queue.
                +#
                +# Below is a brief example of usage to determine the percentage of
                +# connection requests where a connection was immediately available:
                +#
                +#   mutex = Mutex.new
                +#   total = immediates = 0
                +#
                +#   DB.pool.on_checkout_event = proc do |event|
                +#     case event
                +#     when :immediately_available
                +#       mutex.synchronize do
                +#         total += 1
                +#         immediates += 1
                +#       end
                +#     when :not_immediately_available
                +#       mutex.synchronize do
                +#         total += 1
                +#       end
                +#     end
                +#   end
                +#
                +#   immediate_percentage = lambda do
                +#     mutex.synchronize do
                +#       100.0 * immediates / total
                +#     end
                +#   end
                +#   
                +# Note that this extension only works with the timed_queue
                +# and sharded_timed_queue connection pools (the default
                +# connection pools when using Ruby 3.2+).
                +#
                +# Related modules: Sequel::ConnectionCheckoutEventCallbacks::TimedQueue,
                +# Sequel::ConnectionCheckoutEventCallbacks::ShardedTimedQueue
                +
                +#
                +module Sequel
                +  module ConnectionCheckoutEventCallbacks
                +    module TimedQueue
                +      # The callback that is called with connection checkout events.
                +      attr_accessor :on_checkout_event
                +
                +      private
                +
                +      def available
                +        conn = super
                +        @on_checkout_event.call(conn ? :immediately_available : :not_immediately_available)
                +        conn
                +      end
                +
                +      def try_make_new
                +        conn = super
                +        @on_checkout_event.call(:new_connection) if conn
                +        conn
                +      end
                +
                +      def wait_until_available
                +        timer = Sequel.start_timer
                +        conn = super
                +        @on_checkout_event.call(Sequel.elapsed_seconds_since(timer))
                +        conn
                +      end
                +    end
                +
                +    module ShardedTimedQueue
                +      # The callback that is called with connection checkout events.
                +      attr_accessor :on_checkout_event
                +
                +      private
                +
                +      def available(queue, server)
                +        conn = super
                +        @on_checkout_event.call(conn ? :immediately_available : :not_immediately_available, server)
                +        conn
                +      end
                +
                +      def try_make_new(server)
                +        conn = super
                +        @on_checkout_event.call(:new_connection, server) if conn
                +        conn
                +      end
                +
                +      def wait_until_available(queue, server)
                +        timer = Sequel.start_timer
                +        conn = super
                +        @on_checkout_event.call(Sequel.elapsed_seconds_since(timer), server)
                +        conn
                +      end
                +    end
                +  end
                +
                +  default_callback = proc{}
                +
                +  Database.register_extension(:connection_checkout_event_callback) do |db|
                +    pool = db.pool
                +
                +    case pool.pool_type
                +    when :timed_queue
                +      db.pool.extend(ConnectionCheckoutEventCallbacks::TimedQueue)
                +    when :sharded_timed_queue
                +      db.pool.extend(ConnectionCheckoutEventCallbacks::ShardedTimedQueue)
                +    else
                +      raise Error, "the connection_checkout_event_callback extension is only supported when using a timed_queue connection pool"
                +    end
                +
                +    pool.on_checkout_event ||= default_callback
                +  end
                +end
        lib/sequel/extensions/lit_require_frozen.rb
                --- /tmp/20260504-1831-mpybwq	2026-05-04 02:36:32.137216361 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/extensions/lit_require_frozen.rb	2026-05-04 02:36:32.113216570 +0000
                @@ -0,0 +1,131 @@
                +# frozen-string-literal: true
                +#
                +# The lit_require_frozen extension disallows the use of unfrozen strings
                +# as literal strings in database and dataset methods. If you try to use an
                +# unfrozen string as a literal string for a dataset using this extension,
                +# an exception will be raised.
                +#
                +# While this works for all Ruby versions, it is designed for use on Ruby 3+
                +# where all files are using the frozen-string-literal magic comment. In this
                +# case, uninterpolated literal strings are frozen, but interpolated strings
                +# are not frozen. This allows you to catch potentially dangerous code:
                +#
                +#   # Probably safe, no exception raised
                +#   DB["SELECT * FROM t WHERE c > :v", v: user_provided_string)
                +#
                +#   # Potentially unsafe, raises Sequel::LitRequireFrozen::Error
                +#   DB["SELECT * FROM t WHERE c > '#{user_provided_string}'"]
                +#
                +# The assumption made is that a frozen string is unlikely to contain unsafe
                +# input, while an unfrozen string has potentially been interpolated and may
                +# contain unsafe input.
                +#
                +# This disallows the the following cases:
                +#
                +# * Sequel::LiteralString instances that are unfrozen and are not based on a
                +#   frozen string
                +# * Sequel::SQL::PlaceholderLiteralString instances when the placeholder string
                +#   is not frozen
                +# * Unfrozen strings passed to Database#<< or #[] or Dataset#with_sql
                +#
                +# To use this extension, load it into the database:
                +#
                +#   DB.extension :lit_require_frozen
                +#
                +# It can also be loaded into individual datasets:
                +#
                +#  ds = DB[:t].extension(:lit_require_frozen)
                +#
                +# Assuming you have good test coverage, it is recommended to only load
                +# this extension when testing.
                +#
                +# Related module: Sequel::LitRequireFrozen
                +
                +#
                +module Sequel
                +  class LiteralString
                +    # The string used when creating the literal string (first argument to
                +    # Sequel::LiteralString.new). This may be nil if no string was provided,
                +    # or if the litral string was created before this extension was required.
                +    attr_reader :source
                +
                +    def initialize(*a)
                +      @source = a.first
                +      super
                +    end
                +    # :nocov:
                +    ruby2_keywords :initialize if respond_to?(:ruby2_keywords, true)
                +    # :nocov:
                +  end
                +
                +  module LitRequireFrozen
                +    # Error class raised for using unfrozen literal string.
                +    class Error < Sequel::Error
                +    end
                +
                +    module DatabaseMethods
                +      def self.extended(db)
                +        db.extend_datasets(DatasetMethods)
                +      end
                +
                +      # Check given SQL is frozen before running it.
                +      def run(sql, opts=OPTS)
                +        @default_dataset.with_sql(sql)
                +        super
                +      end
                +    end
                +
                +    module DatasetMethods
                +      # Check given SQL is not an unfrozen string.
                +      def with_sql(sql, *args)
                +        _check_unfrozen_literal_string(sql)
                +        super
                +      end
                +
                +      # Check that placeholder string is frozen (or all entries
                +      # in placeholder array are frozen).
                +      def placeholder_literal_string_sql_append(sql, pls)
                +        str = pls.str
                +
                +        if str.is_a?(Array)
                +          str.each do |s|
                +            _check_unfrozen_literal_string(s)
                +          end
                +        else
                +          _check_unfrozen_literal_string(str)
                +        end
                +
                +        super
                +      end
                +
                +      private
                +
                +      # Base method that other methods used to check for whether a string should be allowed
                +      # as literal SQL. Allows non-strings as well as frozen strings.
                +      def _check_unfrozen_literal_string(str)
                +        return if !str.is_a?(String) || str.frozen?
                +
                +        if str.is_a?(LiteralString)
                +          _check_unfrozen_literal_string(str.source)
                +        else
                +          raise Error, "cannot treat unfrozen string as literal SQL: #{str.inspect}"
                +        end
                +      end
                +
                +      # Check literal strings appended to SQL.
                +      def literal_literal_string_append(sql, v)
                +        _check_unfrozen_literal_string(v)
                +        super
                +      end
                +
                +      # Check static SQL is not frozen.
                +      def static_sql(sql)
                +        _check_unfrozen_literal_string(sql)
                +        super
                +      end
                +    end
                +  end
                +
                +  Dataset.register_extension(:lit_require_frozen, LitRequireFrozen::DatasetMethods)
                +  Database.register_extension(:lit_require_frozen, LitRequireFrozen::DatabaseMethods)
                +end
      * Changed:
        MIT-LICENSE
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/MIT-LICENSE	2026-05-04 02:36:32.058217047 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/MIT-LICENSE	2026-05-04 02:36:32.098216700 +0000
                @@ -2 +2 @@
                -Copyright (c) 2008-2023 Jeremy Evans
                +Copyright (c) 2008-2026 Jeremy Evans and contributors
        lib/sequel/adapters/jdbc/derby.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/adapters/jdbc/derby.rb	2026-05-04 02:36:32.059217039 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/adapters/jdbc/derby.rb	2026-05-04 02:36:32.099216691 +0000
                @@ -2,0 +3,2 @@
                +# SEQUEL6: Remove
                +
        lib/sequel/adapters/jdbc/h2.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/adapters/jdbc/h2.rb	2026-05-04 02:36:32.059217039 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/adapters/jdbc/h2.rb	2026-05-04 02:36:32.100216683 +0000
                @@ -21 +21 @@
                -          run("COMMIT TRANSACTION #{transaction_id}", opts)
                +          run("COMMIT TRANSACTION #{transaction_id}".freeze, opts)
                @@ -39 +39 @@
                -          run("ROLLBACK TRANSACTION #{transaction_id}", opts)
                +          run("ROLLBACK TRANSACTION #{transaction_id}".freeze, opts)
        lib/sequel/adapters/postgres.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/adapters/postgres.rb	2026-05-04 02:36:32.062217012 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/adapters/postgres.rb	2026-05-04 02:36:32.101216674 +0000
                @@ -730 +730 @@
                -            LiteralString.new("#{prepared_arg_placeholder}#{i}")
                +            LiteralString.new("#{prepared_arg_placeholder}#{i}".freeze)
        lib/sequel/adapters/shared/mssql.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/adapters/shared/mssql.rb	2026-05-04 02:36:32.065216987 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/adapters/shared/mssql.rb	2026-05-04 02:36:32.102216665 +0000
                @@ -109 +109 @@
                -        sql = "DECLARE #{declarations.join(', ')}; EXECUTE @RC = #{name} #{values.join(', ')}; SELECT #{names.join(', ')}"
                +        sql = "DECLARE #{declarations.join(', ')}; EXECUTE @RC = #{name} #{values.join(', ')}; SELECT #{names.join(', ')}".freeze
                @@ -403 +403 @@
                -        run(ds.into(name).sql)
                +        run(ds.into(name).sql.freeze)
                @@ -881 +881 @@
                -          sql = multi_insert_sql(columns, values)[0]
                +          sql = multi_insert_sql(columns, values)[0].freeze
        lib/sequel/adapters/shared/mysql.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/adapters/shared/mysql.rb	2026-05-04 02:36:32.065216987 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/adapters/shared/mysql.rb	2026-05-04 02:36:32.102216665 +0000
                @@ -43 +43 @@
                -        run("XA COMMIT #{literal(transaction_id)}", opts)
                +        run("XA COMMIT #{literal(transaction_id)}".freeze, opts)
                @@ -106 +106 @@
                -        metadata_dataset.with_sql(sql).each do |r|
                +        metadata_dataset.with_sql(sql.freeze).each do |r|
                @@ -118 +118 @@
                -        run("XA ROLLBACK #{literal(transaction_id)}", opts)
                +        run("XA ROLLBACK #{literal(transaction_id)}".freeze, opts)
                @@ -783 +783,2 @@
                -        ds = db.send(:metadata_dataset).with_sql(((opts[:extended] && (db.mariadb? || db.server_version < 50700)) ? 'EXPLAIN EXTENDED ' : 'EXPLAIN ') + select_sql).naked
                +        sql = ((opts[:extended] && (db.mariadb? || db.server_version < 50700)) ? 'EXPLAIN EXTENDED ' : 'EXPLAIN ') + select_sql
                +        ds = db.send(:metadata_dataset).with_sql(sql.freeze).naked
        lib/sequel/adapters/shared/postgres.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/adapters/shared/postgres.rb	2026-05-04 02:36:32.066216978 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/adapters/shared/postgres.rb	2026-05-04 02:36:32.103216657 +0000
                @@ -342 +342 @@
                -        run("COMMIT PREPARED #{literal(transaction_id)}", opts)
                +        run("COMMIT PREPARED #{literal(transaction_id)}".freeze, opts)
                @@ -419 +419 @@
                -          run("ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(column)} DROP DEFAULT", server_hash)
                +          run("ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(column)} DROP DEFAULT".freeze, server_hash)
                @@ -456 +456 @@
                -        self << create_function_sql(name, definition, opts)
                +        self << create_function_sql(name, definition, opts).freeze
                @@ -467 +467 @@
                -        self << create_language_sql(name, opts)
                +        self << create_language_sql(name, opts).freeze
                @@ -476 +476 @@
                -        self << create_schema_sql(name, opts)
                +        self << create_schema_sql(name, opts).freeze
                @@ -512 +512 @@
                -        self << create_trigger_sql(table, name, function, opts)
                +        self << create_trigger_sql(table, name, function, opts).freeze
                @@ -545 +545 @@
                -        run "DO #{"LANGUAGE #{literal(language.to_s)} " if language}#{literal(code)}"
                +        run "DO #{"LANGUAGE #{literal(language.to_s)} " if language}#{literal(code)}".freeze
                @@ -555 +555 @@
                -        self << drop_function_sql(name, opts)
                +        self << drop_function_sql(name, opts).freeze
                @@ -564 +564 @@
                -        self << drop_language_sql(name, opts)
                +        self << drop_language_sql(name, opts).freeze
                @@ -573 +573 @@
                -        self << drop_schema_sql(name, opts)
                +        self << drop_schema_sql(name, opts).freeze
                @@ -584 +584 @@
                -        self << drop_trigger_sql(table, name, opts)
                +        self << drop_trigger_sql(table, name, opts).freeze
                @@ -748 +748 @@
                -        self << rename_schema_sql(name, new_name)
                +        self << rename_schema_sql(name, new_name).freeze
                @@ -759 +759 @@
                -        run "REFRESH MATERIALIZED VIEW#{' CONCURRENTLY' if opts[:concurrently]} #{quote_schema_table(name)}"
                +        run "REFRESH MATERIALIZED VIEW#{' CONCURRENTLY' if opts[:concurrently]} #{quote_schema_table(name)}".freeze
                @@ -772 +772 @@
                -          seq_ds = metadata_dataset.from(:pg_sequence).where(:seqrelid=>regclass_oid(LiteralString.new(seq)))
                +          seq_ds = metadata_dataset.from(:pg_sequence).where(:seqrelid=>regclass_oid(LiteralString.new(seq.freeze)))
                @@ -787 +787 @@
                -        run("ROLLBACK PREPARED #{literal(transaction_id)}", opts)
                +        run("ROLLBACK PREPARED #{literal(transaction_id)}".freeze, opts)
                @@ -1472 +1472 @@
                -        cols[-1] = Sequel.lit("PERIOD #{quote_identifier(cols[-1])}")
                +        cols[-1] = Sequel.lit("PERIOD #{quote_identifier(cols[-1])}".freeze)
        lib/sequel/adapters/shared/sqlite.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/adapters/shared/sqlite.rb	2026-05-04 02:36:32.067216969 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/adapters/shared/sqlite.rb	2026-05-04 02:36:32.104216648 +0000
                @@ -198 +198 @@
                -        metadata_dataset.with_sql("PRAGMA table_#{'x' if sqlite_version > 33100}info(?)", input_identifier_meth(opts[:dataset]).call(table_name))
                +        metadata_dataset.with_sql("PRAGMA table_#{'x' if sqlite_version > 33100}info(?)".freeze, input_identifier_meth(opts[:dataset]).call(table_name))
                @@ -410 +410 @@
                -          c[:default] = LiteralString.new(c[:default]) if c[:default]
                +          c[:default] = LiteralString.new(c[:default]).freeze if c[:default]
                @@ -687 +687 @@
                -        ds = db.send(:metadata_dataset).clone(:sql=>"EXPLAIN #{select_sql}")
                +        ds = db.send(:metadata_dataset).clone(:sql=>"EXPLAIN #{select_sql}".freeze)
        lib/sequel/adapters/sqlite.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/adapters/sqlite.rb	2026-05-04 02:36:32.067216969 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/adapters/sqlite.rb	2026-05-04 02:36:32.104216648 +0000
                @@ -391 +391 @@
                -          LiteralString.new("#{prepared_arg_placeholder}#{k.to_s.gsub('.', '__')}")
                +          LiteralString.new("#{prepared_arg_placeholder}#{k.to_s.gsub('.', '__')}".freeze)
        lib/sequel/adapters/tinytds.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/adapters/tinytds.rb	2026-05-04 02:36:32.067216969 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/adapters/tinytds.rb	2026-05-04 02:36:32.104216648 +0000
                @@ -208 +208 @@
                -          LiteralString.new("@#{k.to_s.gsub('.', '__')}")
                +          LiteralString.new("@#{k.to_s.gsub('.', '__')}".freeze)
        lib/sequel/connection_pool.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/connection_pool.rb	2026-05-04 02:36:32.072216926 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/connection_pool.rb	2026-05-04 02:36:32.105216639 +0000
                @@ -133,0 +134,6 @@
                +  # Only for use by extension that need to disconnect a connection inside acquire.
                +  # Takes the connection and any arguments accepted by acquire.
                +  def disconnect_acquired_connection(conn, *)
                +    disconnect_connection(conn)
                +  end
                +
        lib/sequel/connection_pool/sharded_timed_queue.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/connection_pool/sharded_timed_queue.rb	2026-05-04 02:36:32.072216926 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/connection_pool/sharded_timed_queue.rb	2026-05-04 02:36:32.105216639 +0000
                @@ -74 +74 @@
                -          break unless (conn = queue.pop(timeout: 0)) && !conns[conn]
                +          break unless (conn = available(queue, server)) && !conns[conn]
                @@ -94 +94 @@
                -  # and then the method will only disconnect connectsion from those specified shards.
                +  # and then the method will only disconnect connections from those specified shards.
                @@ -98 +98,3 @@
                -      while conn = queue.pop(timeout: 0)
                +      nconns = 0
                +      while conn = available(queue, server)
                +        nconns += 1
                @@ -101 +103 @@
                -      fill_queue(server)
                +      fill_queue(server, nconns)
                @@ -135 +137 @@
                -        fill_queue(server)
                +        fill_queue(server, 1)
                @@ -170 +172 @@
                -        while conn = queue.pop(timeout: 0)
                +        while conn = available(queue, server)
                @@ -227,0 +230,6 @@
                +  # Only for use by extension that need to disconnect a connection inside acquire.
                +  # Takes the connection and any arguments accepted by acquire.
                +  def disconnect_acquired_connection(conn, _, server)
                +    disconnect_pool_connection(conn, server)
                +  end
                +
                @@ -247 +255,4 @@
                -  def fill_queue(server)
                +  #
                +  # nconns specifies the maximum number of connections to add, which should
                +  # be the number of connections that were disconnected.
                +  def fill_queue(server, nconns)
                @@ -249 +260 @@
                -    if queue.num_waiting > 0
                +    if nconns > 0 && queue.num_waiting > 0
                @@ -251 +262,2 @@
                -        while queue.num_waiting > 0 && (conn = try_make_new(server))
                +        while nconns > 0 && queue.num_waiting > 0 && (conn = try_make_new(server))
                +          nconns -= 1
                @@ -300 +312 @@
                -        fill_queue(server)
                +        fill_queue(server, to_disconnect.size)
                @@ -314 +326 @@
                -    if conn = queue.pop(timeout: 0) || try_make_new(server) || queue.pop(timeout: @timeout)
                +    if conn = available(queue, server) || try_make_new(server) || wait_until_available(queue, server)
                @@ -319,0 +332,13 @@
                +  end
                +
                +  # Return the next connection in the pool if there is one available. Returns nil
                +  # if no connection is currently available.
                +  def available(queue, _server)
                +    queue.pop(timeout: 0)
                +  end
                +
                +  # Return the next connection in the pool if there is one available. If not, wait
                +  # until the timeout for a connection to become available. If there is still no
                +  # available connection, return nil.
                +  def wait_until_available(queue, _server)
                +    queue.pop(timeout: @timeout)
        lib/sequel/connection_pool/timed_queue.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/connection_pool/timed_queue.rb	2026-05-04 02:36:32.072216926 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/connection_pool/timed_queue.rb	2026-05-04 02:36:32.105216639 +0000
                @@ -45 +45 @@
                -          break unless (conn = @queue.pop(timeout: 0)) && !conns[conn]
                +          break unless (conn = available) && !conns[conn]
                @@ -62 +62,3 @@
                -    while conn = @queue.pop(timeout: 0)
                +    nconns = 0
                +    while conn = available
                +      nconns += 1
                @@ -65 +67 @@
                -    fill_queue
                +    fill_queue(nconns)
                @@ -97 +99 @@
                -        fill_queue
                +        fill_queue(1)
                @@ -159,2 +161,5 @@
                -  def fill_queue
                -    if @queue.num_waiting > 0
                +  #
                +  # nconns specifies the maximum number of connections to add, which should
                +  # be the number of connections that were disconnected.
                +  def fill_queue(nconns)
                +    if nconns > 0 && @queue.num_waiting > 0
                @@ -162 +167 @@
                -        while @queue.num_waiting > 0 && (conn = try_make_new)
                +        while nconns > 0 && @queue.num_waiting > 0 && (conn = try_make_new)
                @@ -210 +215 @@
                -        fill_queue
                +        fill_queue(to_disconnect.size)
                @@ -223 +228 @@
                -    if conn = @queue.pop(timeout: 0) || try_make_new || @queue.pop(timeout: @timeout)
                +    if conn = available || try_make_new || wait_until_available
                @@ -228,0 +234,13 @@
                +  end
                +
                +  # Return the next connection in the pool if there is one available. Returns nil
                +  # if no connection is currently available.
                +  def available
                +    @queue.pop(timeout: 0)
                +  end
                +
                +  # Return the next connection in the pool if there is one available. If not, wait
                +  # until the timeout for a connection to become available. If there is still no
                +  # available connection, return nil.
                +  def wait_until_available
                +    @queue.pop(timeout: @timeout)
        lib/sequel/database/schema_generator.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/database/schema_generator.rb	2026-05-04 02:36:32.073216917 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/database/schema_generator.rb	2026-05-04 02:36:32.107216622 +0000
                @@ -5,0 +6,27 @@
                +    module ColumnOptionMerger
                +      private
                +
                +      # Merge given options into the column's default options. For backwards compatibility,
                +      # the options take priority, but in cases where the option value overrides the argument
                +      # value, and the values are different, we warn as this is likely to be an error in the
                +      # code.
                +      def _merge_column_options(defaults, opts)
                +        defaults.merge!(opts) do |k, defv, v|
                +          unless defv == v
                +            # :nocov:
                +            if RUBY_VERSION >= "3.2"
                +            # :nocov:
                +              caller_loc = Thread.each_caller_location do |loc|
                +                break loc unless loc.path == __FILE__
                +              end
                +              caller_loc &&= "#{caller_loc.path}:#{caller_loc.lineno}: "
                +            end
                +            warn("#{caller_loc}#{k.inspect} option value (#{v.inspect}) overrides argument value (#{defv.inspect})")
                +          end
                +
                +          v
                +        end
                +      end
                +    end
                +    private_constant :ColumnOptionMerger
                +
                @@ -19,0 +47,2 @@
                +      include ColumnOptionMerger
                +
                @@ -176 +205 @@
                -        columns << {:name => name, :type => type}.merge!(opts)
                +        columns << _merge_column_options({:name => name, :type => type}, opts)
                @@ -182 +211 @@
                -      
                +
                @@ -249 +278 @@
                -        column(name, Integer, opts)
                +        column(name, opts.fetch(:type, Integer), opts)
                @@ -431,0 +461,2 @@
                +      include ColumnOptionMerger
                +
                @@ -457 +488 @@
                -        op = {:op => :add_column, :name => name, :type => type}.merge!(opts)
                +        op = _merge_column_options({:op => :add_column, :name => name, :type => type}, opts)
                @@ -522 +553 @@
                -        add_column(name, Integer, {:table=>table}.merge!(opts))
                +        add_column(name, opts.fetch(:type, Integer), {:table=>table}.merge!(opts))
        lib/sequel/database/schema_methods.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/database/schema_methods.rb	2026-05-04 02:36:32.074216909 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/database/schema_methods.rb	2026-05-04 02:36:32.107216622 +0000
                @@ -822 +822 @@
                -      run(create_table_as_sql(name, sql, options))
                +      run(create_table_as_sql(name, sql, options).freeze)
        lib/sequel/dataset/placeholder_literalizer.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/dataset/placeholder_literalizer.rb	2026-05-04 02:36:32.075216900 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/dataset/placeholder_literalizer.rb	2026-05-04 02:36:32.108216613 +0000
                @@ -119,0 +120,2 @@
                +          frags.each(&:freeze)
                +          frags.freeze
                @@ -127,0 +130 @@
                +          ds.opts[:sql].freeze
        lib/sequel/dataset/prepared_statements.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/dataset/prepared_statements.rb	2026-05-04 02:36:32.075216900 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/dataset/prepared_statements.rb	2026-05-04 02:36:32.108216613 +0000
                @@ -57 +57 @@
                -        sql = prepared_sql
                +        sql = prepared_sql.freeze
                @@ -226 +226 @@
                -          with_sql(prepared_sql)
                +          with_sql(prepared_sql.freeze)
                @@ -290 +290 @@
                -          sql = literal(Sequel::SQL::PlaceholderLiteralString.new(@opts[:prepared_sql_frags], @opts[:bind_arguments], false))
                +          sql = literal(Sequel::SQL::PlaceholderLiteralString.new(@opts[:prepared_sql_frags], @opts[:bind_arguments], false)).freeze
                @@ -322,0 +323,3 @@
                +        frags.freeze
                +        frags.each(&:freeze)
                +        prepared_sql.freeze
                @@ -412 +415 @@
                -        sql = ps.prepared_sql
                +        sql = ps.prepared_sql.freeze
        lib/sequel/dataset/query.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/dataset/query.rb	2026-05-04 02:36:32.075216900 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/dataset/query.rb	2026-05-04 02:36:32.109216604 +0000
                @@ -1304 +1304 @@
                -        sql = public_send(sql, *args)
                +        sql = public_send(sql, *args).freeze
                @@ -1477 +1477,4 @@
                -    
                +
                +    PAREN_WRAPPER = ["(".freeze, ")".freeze].freeze
                +    private_constant :PAREN_WRAPPER
                +
                @@ -1498 +1501 @@
                -        LiteralString.new("(#{expr})")
                +        SQL::PlaceholderLiteralString.new(PAREN_WRAPPER, [expr])
        lib/sequel/dataset/sql.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/dataset/sql.rb	2026-05-04 02:36:32.076216891 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/dataset/sql.rb	2026-05-04 02:36:32.109216604 +0000
                @@ -56 +56 @@
                -          sql << v
                +          literal_literal_string_append(sql, v)
                @@ -1424,0 +1425,5 @@
                +    end
                +
                +    # Append string to SQL string.
                +    def literal_literal_string_append(sql, v)
                +      sql << v
        lib/sequel/extensions/connection_expiration.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/extensions/connection_expiration.rb	2026-05-04 02:36:32.077216882 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/extensions/connection_expiration.rb	2026-05-04 02:36:32.110216596 +0000
                @@ -94 +94 @@
                -          disconnect_connection(conn)
                +          disconnect_acquired_connection(conn, *a)
        lib/sequel/extensions/connection_validator.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/extensions/connection_validator.rb	2026-05-04 02:36:32.077216882 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/extensions/connection_validator.rb	2026-05-04 02:36:32.111216587 +0000
                @@ -121 +121 @@
                -              disconnect_connection(conn)
                +              disconnect_acquired_connection(conn, *a)
        lib/sequel/extensions/dataset_run.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/extensions/dataset_run.rb	2026-05-04 02:36:32.079216865 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/extensions/dataset_run.rb	2026-05-04 02:36:32.111216587 +0000
                @@ -33 +33 @@
                -        db.run(sql, :server=>server)
                +        db.run(sql.freeze, :server=>server)
                @@ -35 +35 @@
                -        db.run(sql)
                +        db.run(sql.freeze)
        lib/sequel/extensions/date_arithmetic.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/extensions/date_arithmetic.rb	2026-05-04 02:36:32.079216865 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/extensions/date_arithmetic.rb	2026-05-04 02:36:32.111216587 +0000
                @@ -85,2 +85,2 @@
                -        MYSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s.upcase[0...-1]).freeze}).freeze
                -        MSSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s[0...-1]).freeze}).freeze
                +        MYSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s.upcase[0...-1].freeze).freeze}).freeze
                +        MSSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s[0...-1].freeze).freeze}).freeze
                @@ -88 +88 @@
                -        DERBY_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit("SQL_TSI_#{s.to_s.upcase[0...-1]}").freeze}).freeze
                +        DERBY_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit("SQL_TSI_#{s.to_s.upcase[0...-1]}".freeze).freeze}).freeze
                @@ -90 +90 @@
                -        DB2_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s).freeze}).freeze
                +        DB2_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s.freeze).freeze}).freeze
                @@ -110 +110 @@
                -                placeholder << "#{', ' unless placeholder.empty?}#{sql_unit} := "
                +                placeholder << "#{', ' unless placeholder.empty?}#{sql_unit} := ".freeze
                @@ -159 +159 @@
                -              expr = Sequel.lit(["{fn timestampadd(#{sql_unit}, ", ", timestamp(", "))}"], value, expr)
                +              expr = Sequel.lit(["{fn timestampadd(#{sql_unit}, ".freeze, ", timestamp(", "))}"], value, expr)
        lib/sequel/extensions/migration.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/extensions/migration.rb	2026-05-04 02:36:32.080216856 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/extensions/migration.rb	2026-05-04 02:36:32.113216570 +0000
                @@ -189,16 +189,13 @@
                -      begin
                -        instance_exec(&block)
                -      rescue
                -        just_raise = true
                -      end
                -      if just_raise
                -        Proc.new{raise Sequel::Error, "irreversible migration method used in #{block.source_location.first}, you may need to write your own down method"}
                -      else
                -        actions = @actions.reverse
                -        Proc.new do
                -          actions.each do |a|
                -            pr = a.last.is_a?(Proc) ? a.pop : nil
                -            # Allow calling private methods as the reversing methods are private
                -            send(*a, &pr)
                -          end
                -        end
                +      instance_exec(&block)
                +    rescue NoMethodError => e
                +      Proc.new{raise Sequel::Error, "irreversible migration method \"#{e.name}\" used in #{block.source_location.first}, you may need to write your own down method"}
                +    rescue => e
                +      Proc.new{raise Sequel::Error, "unable to reverse migration due to #{e.class} in #{block.source_location.first}, you may need to write your own down method"}
                +    else
                +      actions = @actions.reverse
                +      Proc.new do
                +       actions.each do |a|
                +        pr = a.last.is_a?(Proc) ? a.pop : nil
                +        # Allow calling private methods as the reversing methods are private
                +        send(*a, &pr)
                +       end
                @@ -273 +270 @@
                -      raise if args.first.is_a?(Array)
                +      super if args.first.is_a?(Array)
        lib/sequel/extensions/pg_enum.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/extensions/pg_enum.rb	2026-05-04 02:36:32.081216848 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/extensions/pg_enum.rb	2026-05-04 02:36:32.114216561 +0000
                @@ -130 +130 @@
                -        run(sql)
                +        run(sql.freeze)
        lib/sequel/model/associations.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/model/associations.rb	2026-05-04 02:36:32.087216796 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/model/associations.rb	2026-05-04 02:36:32.122216492 +0000
                @@ -114,0 +115,2 @@
                +          when :lateral_subquery
                +            apply_lateral_subquery_eager_graph_limit_strategy(ds)
                @@ -301 +303,6 @@
                -            objects = apply_eager_limit_strategy(ds, strategy, eager_limit).all
                +
                +            objects = if strategy == :lateral_subquery
                +              apply_lateral_subquery_eager_limit_strategy(ds, ids, eager_limit).all
                +            else
                +              apply_eager_limit_strategy(ds, strategy, eager_limit).all
                +            end
                @@ -323 +330 @@
                -              objects.concat(ds.with_sql(sql).to_a)
                +              objects.concat(ds.with_sql(sql.freeze).to_a)
                @@ -369 +376,6 @@
                -          ds = filter_by_associations_conditions_dataset.where(filter_by_associations_conditions_subquery_conditions(obj))
                +          ds = if filter_by_associations_limit_strategy == :lateral_subquery
                +            apply_lateral_subquery_filter_limit_strategy(associated_eager_dataset, obj)
                +          else
                +            filter_by_associations_conditions_dataset.where(filter_by_associations_conditions_subquery_conditions(obj))
                +          end
                +
                @@ -756,0 +769,55 @@
                +        def lateral_subquery_eager_limit_strategy_lateral_dataset(ds, limit_and_offset)
                +          ds.
                +            where(Array(filter_by_associations_conditions_key).zip(Array(filter_by_associations_conditions_associated_keys))).
                +            limit(*limit_and_offset).
                +            lateral
                +        end
                +
                +        def apply_lateral_subquery_eager_limit_strategy(ds, ids, limit_and_offset)
                +          table_name = self[:model].table_name
                +          associated_table_name = associated_class.table_name
                +
                +          associated_class.
                +            from(table_name).
                +            select_all(associated_table_name).
                +            join(lateral_subquery_eager_limit_strategy_lateral_dataset(ds, limit_and_offset).as(associated_table_name), true).
                +            order(*self[:order])
                +        end
                +
                +        # Return an expression to filter the filter by associations dataset to only
                +        # rows related to given objects.
                +        def _lateral_subquery_filter_limit_strategy_conditions(obj, key, value_method, value_column)
                +          value = case obj
                +          when Array
                +            if key.is_a?(Array)
                +              key_methods = Array(value_method)
                +              obj.map{|o| key_methods.map{|meth| o.send(meth)}}
                +            else
                +              obj.map{|o| o.send(value_method)}
                +            end
                +          when Sequel::Dataset
                +            obj.select(*Array(qualify(associated_class.table_name, value_column)))
                +          else
                +            if key.is_a?(Array)
                +              return Array(key).zip(Array(value_method).map{|meth| obj.send(meth)})
                +            else
                +              obj.send(value_method)
                +            end
                +          end
                +
                +          {key => value}
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_lateral_dataset(ds, obj)
                +          lateral_subquery_filter_limit_strategy_filter_lateral_dataset(ds.
                +            select(*qualify(associated_class.table_name, associated_class.primary_key)).
                +            limit(*limit_and_offset).
                +            lateral)
                +        end
                +
                +        def apply_lateral_subquery_filter_limit_strategy(ds, obj)
                +          lateral_subquery_filter_limit_strategy_filter_dataset(self[:model].
                +            select(*lateral_subquery_filter_limit_strategy_lateral_dataset_select).
                +            join(lateral_subquery_filter_limit_strategy_lateral_dataset(ds, obj).as(associated_class.table_name), filter_by_associations_conditions_subquery_conditions(obj)), obj)
                +        end
                +
                @@ -779 +846,6 @@
                -              apply_eager_limit_strategy(ds.where(predicate_key=>arg), eager_limit_strategy)
                +              strategy = eager_limit_strategy
                +              if strategy == :lateral_subquery
                +                apply_lateral_subquery_eager_limit_strategy(ds, arg, limit_and_offset)
                +              else
                +                apply_eager_limit_strategy(ds.where(predicate_key=>arg), strategy)
                +              end
                @@ -1021 +1093 @@
                -        # Support a correlated subquery limit strategy when using eager_graph.
                +        # Support a lateral_subquery and correlated_subquery limit strategy when using eager_graph.
                @@ -1120,0 +1193,48 @@
                +        # Use a LATERAL subquery to limit the dataset.  Note that this will not
                +        # work correctly if the associated dataset uses qualified identifers in the WHERE clause,
                +        # as they would reference the containing query instead of the subquery.
                +        #
                +        # This does not contain the conditions that are necessary to join to the
                +        # query, since the necessary qualifier is not passed as an argument.
                +        def apply_lateral_subquery_eager_graph_limit_strategy(ds)
                +          table_name = ds.first_source_alias
                +          qualifier = ds.opts[:eager_options][:implicit_qualifier]
                +          graph_conditions = self[:_graph_conditions]
                +
                +          unless Sequel.condition_specifier?(graph_conditions)
                +            raise Error, "lateral_subquery eager graph limit strategy only supported when graph conditions are a hash or array of pairs"
                +          end
                +
                +          ds.
                +            limit(*limit_and_offset).
                +            order(*self[:order]).
                +            where(graph_conditions.map{|k, v| [qualify(table_name, k), qualify(qualifier, v)]}).
                +            lateral
                +        end
                +
                +        # Avoid setting duplicate predicate condition when using the lateral subquery
                +        # eager limit strategy.
                +        def eager_loading_set_predicate_condition(ds, eo)
                +          eager_limit_strategy == :lateral_subquery ? ds : super
                +        end
                +
                +        def apply_lateral_subquery_eager_limit_strategy(ds, ids, limit_and_offset)
                +          super.where(qualify(self[:model].table_name, self[:primary_key]) => ids)
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_conditions(obj)
                +          _lateral_subquery_filter_limit_strategy_conditions(obj, filter_by_associations_conditions_key, self[:key_method], self[:key])
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_filter_lateral_dataset(ds)
                +          ds.where(Array(filter_by_associations_conditions_key).zip(Array(filter_by_associations_conditions_associated_keys)))
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_filter_dataset(ds, obj)
                +          ds.where(lateral_subquery_filter_limit_strategy_conditions(obj))
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_lateral_dataset_select
                +          qualified_primary_key
                +        end
                +
                @@ -1434,0 +1555,46 @@
                +        def lateral_subquery_filter_limit_strategy_conditions(obj)
                +          _lateral_subquery_filter_limit_strategy_conditions(obj, lateral_subquery_filter_limit_strategy_conditions_key, right_primary_key_method, right_primary_key)
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_conditions_key
                +          self[:right_key]
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_filter_lateral_dataset(ds)
                +          ds.where(Array(filter_by_associations_conditions_key).zip(Array(filter_by_associations_conditions_associated_keys)))
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_filter_dataset(ds, obj)
                +          ds.where(filter_by_associations_conditions_key => ds.db.from(self[:join_table]).
                +            select(*self[:left_key]).
                +            where(lateral_subquery_filter_limit_strategy_conditions(obj)))
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_lateral_dataset_select
                +          qualify(self[:model].table_name, self[:left_primary_key])
                +        end
                +
                +        def lateral_subquery_eager_limit_strategy_lateral_dataset(ds, limit_and_offset)
                +          ds = super
                +          select = ds.opts[:select].dup
                +          left_key_alias = self[:left_key_alias]
                +          select.pop while (s = select.last).is_a?(Sequel::SQL::AliasedExpression) && (left_key_alias.is_a?(Array) ? left_key_alias.include?(s.alias) : s.alias == left_key_alias)
                +          ds = ds.clone(:select=>select)
                +        end
                +
                +        def apply_lateral_subquery_eager_limit_strategy(ds, ids, limit_and_offset)
                +          table_name = self[:model].table_name
                +          super.
                +            select_all(associated_class.table_name).
                +            select_append(*qualify(table_name, self[:left_primary_keys]).zip(Array(self[:left_key_alias])).map{|id, aliaz| Sequel.as(id, aliaz)}).
                +            where(qualify(table_name, self[:left_primary_key]) => ids)
                +        end
                +
                +        def apply_lateral_subquery_eager_graph_limit_strategy(ds)
                +          ds.
                +            limit(*limit_and_offset).
                +            where(qualify(join_table_alias, self[:left_keys]).zip(qualify(ds.opts[:eager_options][:implicit_qualifier], self[:left_primary_key_columns]))).
                +            order(*self[:order]).
                +            lateral
                +        end
                +
                @@ -2415,0 +2582 @@
                +          graph_conditions = opts[:_graph_conditions] = use_only_conditions ? only_conditions : cks.zip(pkcs) + conditions
                @@ -2418 +2585,5 @@
                -            ds = ds.graph(opts.apply_eager_graph_limit_strategy(eo[:limit_strategy], eager_graph_dataset(opts, eo)), use_only_conditions ? only_conditions : cks.zip(pkcs) + conditions, eo.merge(:select=>select, :join_type=>eo[:join_type]||join_type, :qualify=>:deep), &graph_block)
                +            graph_limit_strategy = eo[:limit_strategy]
                +            egds = opts.apply_eager_graph_limit_strategy(graph_limit_strategy, eager_graph_dataset(opts, eo))
                +            graph_conditions_true = true if graph_limit_strategy == :lateral_subquery
                +
                +            ds = ds.graph(egds, graph_conditions_true || graph_conditions, eo.merge(:select=>select, :join_type=>eo[:join_type]||join_type, :qualify=>:deep), &graph_block)
                @@ -2500,0 +2672,3 @@
                +          if eager_options[:limit_strategy] == :lateral_subquery
                +            ds = ds.clone(:eager_options=>eager_options)
                +          end
        lib/sequel/plugins/dataset_associations.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/plugins/dataset_associations.rb	2026-05-04 02:36:32.090216769 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/plugins/dataset_associations.rb	2026-05-04 02:36:32.126216457 +0000
                @@ -85 +85 @@
                -          ds = r.associated_class.dataset
                +          klass = r.associated_class
                @@ -86,0 +87,19 @@
                +
                +          if r.send(:filter_by_associations_limit_strategy) == :lateral_subquery
                +            ds = r.send(:associated_eager_dataset)
                +
                +            case r[:type]
                +            when :one_to_one, :one_to_many
                +              sds = sds.select(*Array(r.qualified_primary_key))
                +            else
                +              sds = sds.select(*r[:left_primary_keys])
                +              ds = ds.select_all(klass.table_name)
                +              update_select = true
                +            end
                +
                +            ds = r.send(:apply_lateral_subquery_eager_limit_strategy, ds, sds, r.limit_and_offset)
                +            ds = ds.clone(:select=>ds.opts[:select][0,1]) if update_select
                +            return ds.clone(:eager=>nil, :eager_graph=>nil)
                +          end
                +
                +          ds = klass.dataset
        lib/sequel/plugins/dirty.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/plugins/dirty.rb	2026-05-04 02:36:32.091216761 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/plugins/dirty.rb	2026-05-04 02:36:32.126216457 +0000
                @@ -237,2 +237,5 @@
                -            check_missing_initial_value(column)
                -            iv[column] = get_column_value(column)
                +            if db_schema[column]
                +              check_missing_initial_value(column)
                +              iv[column] = get_column_value(column)
                +            end
                +
        lib/sequel/plugins/many_through_many.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/plugins/many_through_many.rb	2026-05-04 02:36:32.092216752 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/plugins/many_through_many.rb	2026-05-04 02:36:32.128216440 +0000
                @@ -148,0 +149,21 @@
                +        def lateral_subquery_filter_limit_strategy_conditions_key
                +          qualify(reverse_edges.first[:table], reverse_edges.first[:left])
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_filter_lateral_dataset(ds)
                +          ds.where(Array(qualify(edges.first[:table], edges.first[:right])).zip(Array(qualify(self[:model].table_name, edges.first[:left]))))
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_filter_dataset(ds, obj)
                +          first_edge, *remaining_edges = edges
                +          filter_ds = ds.db.from(first_edge[:table]).
                +            select(*qualify(first_edge[:table], first_edge[:right])).
                +            where(lateral_subquery_filter_limit_strategy_conditions(obj))
                +
                +          remaining_edges.each do |edge|
                +            filter_ds = filter_ds.join(edge[:table], Array(edge[:right]).zip(Array(edge[:left])))
                +          end
                +
                +          ds.where(qualify(self[:model].table_name, self[:left_primary_key]) => filter_ds)
                +        end
                +
        lib/sequel/plugins/mssql_optimistic_locking.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/plugins/mssql_optimistic_locking.rb	2026-05-04 02:36:32.092216752 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/plugins/mssql_optimistic_locking.rb	2026-05-04 02:36:32.128216440 +0000
                @@ -58 +58 @@
                -          rows = ds.clone(ds.send(:default_server_opts, :sql=>ds.output(nil, [Sequel[:inserted][lc]]).update_sql(columns))).all
                +          rows = ds.clone(ds.send(:default_server_opts, :sql=>ds.output(nil, [Sequel[:inserted][lc]]).update_sql(columns).freeze)).all
        lib/sequel/plugins/pg_xmin_optimistic_locking.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/plugins/pg_xmin_optimistic_locking.rb	2026-05-04 02:36:32.093216743 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/plugins/pg_xmin_optimistic_locking.rb	2026-05-04 02:36:32.129216431 +0000
                @@ -102 +102 @@
                -          rows = ds.clone(ds.send(:default_server_opts, :sql=>ds.returning(:xmin).update_sql(columns))).all
                +          rows = ds.clone(ds.send(:default_server_opts, :sql=>ds.returning(:xmin).update_sql(columns).freeze)).all
        lib/sequel/plugins/serialization.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/plugins/serialization.rb	2026-05-04 02:36:32.094216735 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/plugins/serialization.rb	2026-05-04 02:36:32.130216422 +0000
                @@ -32,0 +33,9 @@
                +    # Note that use of an unsafe serialization method can result in an attack vector
                +    # (potentially allowing remote code execution) if an attacker has the ability to
                +    # store data directly in the underlying column. This would affect the marshal
                +    # serialization format, and on older versions of Ruby, potentially the yaml and
                +    # json serialization formats as well. It can also affect custom formats. You
                +    # should ensure that attackers do not have access to store data directly in the
                +    # underlying column when using this plugin (especially when using an unsafe
                +    # serialization method).
                +    #
                @@ -100 +109 @@
                -          v = v.unpack('m')[0] unless v[0..1] == "\x04\x08"
                +          v = v.unpack('m')[0] unless v.start_with?("\x04\x08")
        lib/sequel/version.rb
                --- /tmp/d20260504-1831-hv93hc/sequel-5.101.0/lib/sequel/version.rb	2026-05-04 02:36:32.097216709 +0000
                +++ /tmp/d20260504-1831-hv93hc/sequel-5.104.0/lib/sequel/version.rb	2026-05-04 02:36:32.134216387 +0000
                @@ -9 +9 @@
                -  MINOR = 101
                +  MINOR = 104

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

gem compare bigdecimal 4.0.1 4.1.2

Compared versions: ["4.0.1", "4.1.2"]
  DIFFERENT require_paths:
    4.0.1: ["/opt/hostedtoolcache/Ruby/4.0.3/x64/lib/ruby/gems/4.0.0/extensions/x86_64-linux/4.0.0/bigdecimal-4.0.1", "lib"]
    4.1.2: ["/opt/hostedtoolcache/Ruby/4.0.3/x64/lib/ruby/gems/4.0.0/extensions/x86_64-linux/4.0.0/bigdecimal-4.1.2", "lib"]
  DIFFERENT required_ruby_version:
    4.0.1: >= 2.5.0
    4.1.2: >= 2.6.0
  DIFFERENT version:
    4.0.1: 4.0.1
    4.1.2: 4.1.2
  DIFFERENT files:
    4.0.1->4.1.2:
      * Added:
            ext/bigdecimal/div.h +192/-0
            ext/bigdecimal/ntt.h +191/-0
            sig/big_decimal.rbs +1502/-0
            sig/big_decimal_util.rbs +158/-0
            sig/big_math.rbs +423/-0
      * Changed:
            bigdecimal.gemspec +6/-1
            ext/bigdecimal/bigdecimal.c +263/-262
            ext/bigdecimal/bigdecimal.h +43/-37
            ext/bigdecimal/extconf.rb +5/-2
            ext/bigdecimal/missing.h +4/-2
            ext/bigdecimal/missing/dtoa.c +183/-136
            lib/bigdecimal.rb +104/-60
            lib/bigdecimal/math.rb +103/-124
            lib/bigdecimal/util.rb +1/-1
            sample/linear.rb +73/-37
              (!) Shebang probably lost: #!/usr/local/bin/ruby
            sample/nlsolve.rb +47/-30
              (!) Shebang probably lost: #!/usr/local/bin/ruby
            sample/pi.rb +2/-7
              (!) Shebang probably lost: #!/usr/local/bin/ruby

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

gem compare sequel 5.101.0 5.104.0

Compared versions: ["5.101.0", "5.104.0"]
  DIFFERENT version:
    5.101.0: 5.101.0
    5.104.0: 5.104.0
  DIFFERENT files:
    5.101.0->5.104.0:
      * Added:
            lib/sequel/extensions/connection_checkout_event_callback.rb +151/-0
            lib/sequel/extensions/lit_require_frozen.rb +131/-0
      * Changed:
            MIT-LICENSE +1/-1
            lib/sequel/adapters/jdbc/derby.rb +2/-0
            lib/sequel/adapters/jdbc/h2.rb +2/-2
            lib/sequel/adapters/postgres.rb +1/-1
            lib/sequel/adapters/shared/mssql.rb +3/-3
            lib/sequel/adapters/shared/mysql.rb +5/-4
            lib/sequel/adapters/shared/postgres.rb +16/-16
            lib/sequel/adapters/shared/sqlite.rb +3/-3
            lib/sequel/adapters/sqlite.rb +1/-1
            lib/sequel/adapters/tinytds.rb +1/-1
            lib/sequel/connection_pool.rb +6/-0
            lib/sequel/connection_pool/sharded_timed_queue.rb +36/-11
            lib/sequel/connection_pool/timed_queue.rb +27/-9
            lib/sequel/database/schema_generator.rb +36/-5
            lib/sequel/database/schema_methods.rb +1/-1
            lib/sequel/dataset/placeholder_literalizer.rb +3/-0
            lib/sequel/dataset/prepared_statements.rb +7/-4
            lib/sequel/dataset/query.rb +6/-3
            lib/sequel/dataset/sql.rb +6/-1
            lib/sequel/extensions/connection_expiration.rb +1/-1
            lib/sequel/extensions/connection_validator.rb +1/-1
            lib/sequel/extensions/dataset_run.rb +2/-2
            lib/sequel/extensions/date_arithmetic.rb +6/-6
            lib/sequel/extensions/migration.rb +14/-17
            lib/sequel/extensions/pg_enum.rb +1/-1
            lib/sequel/model/associations.rb +180/-6
            lib/sequel/plugins/dataset_associations.rb +20/-1
            lib/sequel/plugins/dirty.rb +5/-2
            lib/sequel/plugins/many_through_many.rb +21/-0
            lib/sequel/plugins/mssql_optimistic_locking.rb +1/-1
            lib/sequel/plugins/pg_xmin_optimistic_locking.rb +1/-1
            lib/sequel/plugins/serialization.rb +10/-1
            lib/sequel/version.rb +1/-1
  DIFFERENT extra_rdoc_files:
    5.101.0->5.104.0:
      * Changed:
            MIT-LICENSE +1/-1

1 similar comment
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

gem compare sequel 5.101.0 5.104.0

Compared versions: ["5.101.0", "5.104.0"]
  DIFFERENT version:
    5.101.0: 5.101.0
    5.104.0: 5.104.0
  DIFFERENT files:
    5.101.0->5.104.0:
      * Added:
            lib/sequel/extensions/connection_checkout_event_callback.rb +151/-0
            lib/sequel/extensions/lit_require_frozen.rb +131/-0
      * Changed:
            MIT-LICENSE +1/-1
            lib/sequel/adapters/jdbc/derby.rb +2/-0
            lib/sequel/adapters/jdbc/h2.rb +2/-2
            lib/sequel/adapters/postgres.rb +1/-1
            lib/sequel/adapters/shared/mssql.rb +3/-3
            lib/sequel/adapters/shared/mysql.rb +5/-4
            lib/sequel/adapters/shared/postgres.rb +16/-16
            lib/sequel/adapters/shared/sqlite.rb +3/-3
            lib/sequel/adapters/sqlite.rb +1/-1
            lib/sequel/adapters/tinytds.rb +1/-1
            lib/sequel/connection_pool.rb +6/-0
            lib/sequel/connection_pool/sharded_timed_queue.rb +36/-11
            lib/sequel/connection_pool/timed_queue.rb +27/-9
            lib/sequel/database/schema_generator.rb +36/-5
            lib/sequel/database/schema_methods.rb +1/-1
            lib/sequel/dataset/placeholder_literalizer.rb +3/-0
            lib/sequel/dataset/prepared_statements.rb +7/-4
            lib/sequel/dataset/query.rb +6/-3
            lib/sequel/dataset/sql.rb +6/-1
            lib/sequel/extensions/connection_expiration.rb +1/-1
            lib/sequel/extensions/connection_validator.rb +1/-1
            lib/sequel/extensions/dataset_run.rb +2/-2
            lib/sequel/extensions/date_arithmetic.rb +6/-6
            lib/sequel/extensions/migration.rb +14/-17
            lib/sequel/extensions/pg_enum.rb +1/-1
            lib/sequel/model/associations.rb +180/-6
            lib/sequel/plugins/dataset_associations.rb +20/-1
            lib/sequel/plugins/dirty.rb +5/-2
            lib/sequel/plugins/many_through_many.rb +21/-0
            lib/sequel/plugins/mssql_optimistic_locking.rb +1/-1
            lib/sequel/plugins/pg_xmin_optimistic_locking.rb +1/-1
            lib/sequel/plugins/serialization.rb +10/-1
            lib/sequel/version.rb +1/-1
  DIFFERENT extra_rdoc_files:
    5.101.0->5.104.0:
      * Changed:
            MIT-LICENSE +1/-1

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

gem compare --diff sequel 5.101.0 5.104.0

Compared versions: ["5.101.0", "5.104.0"]
  DIFFERENT files:
    5.101.0->5.104.0:
      * Added:
        lib/sequel/extensions/connection_checkout_event_callback.rb
                --- /tmp/20260504-1904-qas754	2026-05-04 02:36:52.185733780 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/extensions/connection_checkout_event_callback.rb	2026-05-04 02:36:52.144733761 +0000
                @@ -0,0 +1,151 @@
                +# frozen-string-literal: true
                +#
                +# The connection_checkout_event_callback extension modifies a database's
                +# connection pool to allow for a checkout event callback. This callback is
                +# called with the following arguments:
                +#
                +# :immediately_available :: Connection immediately available and returned
                +# :not_immediately_available :: Connection not immediately available
                +# :new_connection :: New connection created and returned
                +# Float :: Number of seconds waiting to acquire a connection
                +#
                +# This is a low-level extension that allows for building telemetry
                +# information. It doesn't implement any telemetry reporting itself. The
                +# main reason for recording this information is to use it to determine the
                +# appropriate size for the connection pool. Having too large a connection
                +# pool can waste resources, while having too small a connection pool can
                +# result in substantial time to check out a connection. In general, you
                +# want to use as small a pool as possible while keeping the time to
                +# checkout a connection low.
                +#
                +# To use the connection checkout event callback, you must first load the
                +# extension:
                +#
                +#   DB.extension(:connection_checkout_event_callback)
                +#
                +# By default, an empty proc is used as the callback so that loading the
                +# support doesn't break anything. If you are using the extension, you
                +# should set the callback at some point during application startup:
                +#
                +#   DB.pool.on_checkout_event = proc do |event|
                +#     # ...
                +#   end
                +#
                +# When using the sharded connection pool, the callback is also
                +# passed a second argument, the requested server shard (generally a
                +# symbol), allowing for collection of per-shard telemetry:
                +#
                +#   DB.pool.on_checkout_event = proc do |event, server|
                +#     # ...
                +#   end
                +#
                +# Note that the callback may be called currently by multiple threads.
                +# You should use some form of concurrency control inside the callback,
                +# such as a mutex or queue.
                +#
                +# Below is a brief example of usage to determine the percentage of
                +# connection requests where a connection was immediately available:
                +#
                +#   mutex = Mutex.new
                +#   total = immediates = 0
                +#
                +#   DB.pool.on_checkout_event = proc do |event|
                +#     case event
                +#     when :immediately_available
                +#       mutex.synchronize do
                +#         total += 1
                +#         immediates += 1
                +#       end
                +#     when :not_immediately_available
                +#       mutex.synchronize do
                +#         total += 1
                +#       end
                +#     end
                +#   end
                +#
                +#   immediate_percentage = lambda do
                +#     mutex.synchronize do
                +#       100.0 * immediates / total
                +#     end
                +#   end
                +#   
                +# Note that this extension only works with the timed_queue
                +# and sharded_timed_queue connection pools (the default
                +# connection pools when using Ruby 3.2+).
                +#
                +# Related modules: Sequel::ConnectionCheckoutEventCallbacks::TimedQueue,
                +# Sequel::ConnectionCheckoutEventCallbacks::ShardedTimedQueue
                +
                +#
                +module Sequel
                +  module ConnectionCheckoutEventCallbacks
                +    module TimedQueue
                +      # The callback that is called with connection checkout events.
                +      attr_accessor :on_checkout_event
                +
                +      private
                +
                +      def available
                +        conn = super
                +        @on_checkout_event.call(conn ? :immediately_available : :not_immediately_available)
                +        conn
                +      end
                +
                +      def try_make_new
                +        conn = super
                +        @on_checkout_event.call(:new_connection) if conn
                +        conn
                +      end
                +
                +      def wait_until_available
                +        timer = Sequel.start_timer
                +        conn = super
                +        @on_checkout_event.call(Sequel.elapsed_seconds_since(timer))
                +        conn
                +      end
                +    end
                +
                +    module ShardedTimedQueue
                +      # The callback that is called with connection checkout events.
                +      attr_accessor :on_checkout_event
                +
                +      private
                +
                +      def available(queue, server)
                +        conn = super
                +        @on_checkout_event.call(conn ? :immediately_available : :not_immediately_available, server)
                +        conn
                +      end
                +
                +      def try_make_new(server)
                +        conn = super
                +        @on_checkout_event.call(:new_connection, server) if conn
                +        conn
                +      end
                +
                +      def wait_until_available(queue, server)
                +        timer = Sequel.start_timer
                +        conn = super
                +        @on_checkout_event.call(Sequel.elapsed_seconds_since(timer), server)
                +        conn
                +      end
                +    end
                +  end
                +
                +  default_callback = proc{}
                +
                +  Database.register_extension(:connection_checkout_event_callback) do |db|
                +    pool = db.pool
                +
                +    case pool.pool_type
                +    when :timed_queue
                +      db.pool.extend(ConnectionCheckoutEventCallbacks::TimedQueue)
                +    when :sharded_timed_queue
                +      db.pool.extend(ConnectionCheckoutEventCallbacks::ShardedTimedQueue)
                +    else
                +      raise Error, "the connection_checkout_event_callback extension is only supported when using a timed_queue connection pool"
                +    end
                +
                +    pool.on_checkout_event ||= default_callback
                +  end
                +end
        lib/sequel/extensions/lit_require_frozen.rb
                --- /tmp/20260504-1904-x5a8vc	2026-05-04 02:36:52.191733783 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/extensions/lit_require_frozen.rb	2026-05-04 02:36:52.147733763 +0000
                @@ -0,0 +1,131 @@
                +# frozen-string-literal: true
                +#
                +# The lit_require_frozen extension disallows the use of unfrozen strings
                +# as literal strings in database and dataset methods. If you try to use an
                +# unfrozen string as a literal string for a dataset using this extension,
                +# an exception will be raised.
                +#
                +# While this works for all Ruby versions, it is designed for use on Ruby 3+
                +# where all files are using the frozen-string-literal magic comment. In this
                +# case, uninterpolated literal strings are frozen, but interpolated strings
                +# are not frozen. This allows you to catch potentially dangerous code:
                +#
                +#   # Probably safe, no exception raised
                +#   DB["SELECT * FROM t WHERE c > :v", v: user_provided_string)
                +#
                +#   # Potentially unsafe, raises Sequel::LitRequireFrozen::Error
                +#   DB["SELECT * FROM t WHERE c > '#{user_provided_string}'"]
                +#
                +# The assumption made is that a frozen string is unlikely to contain unsafe
                +# input, while an unfrozen string has potentially been interpolated and may
                +# contain unsafe input.
                +#
                +# This disallows the the following cases:
                +#
                +# * Sequel::LiteralString instances that are unfrozen and are not based on a
                +#   frozen string
                +# * Sequel::SQL::PlaceholderLiteralString instances when the placeholder string
                +#   is not frozen
                +# * Unfrozen strings passed to Database#<< or #[] or Dataset#with_sql
                +#
                +# To use this extension, load it into the database:
                +#
                +#   DB.extension :lit_require_frozen
                +#
                +# It can also be loaded into individual datasets:
                +#
                +#  ds = DB[:t].extension(:lit_require_frozen)
                +#
                +# Assuming you have good test coverage, it is recommended to only load
                +# this extension when testing.
                +#
                +# Related module: Sequel::LitRequireFrozen
                +
                +#
                +module Sequel
                +  class LiteralString
                +    # The string used when creating the literal string (first argument to
                +    # Sequel::LiteralString.new). This may be nil if no string was provided,
                +    # or if the litral string was created before this extension was required.
                +    attr_reader :source
                +
                +    def initialize(*a)
                +      @source = a.first
                +      super
                +    end
                +    # :nocov:
                +    ruby2_keywords :initialize if respond_to?(:ruby2_keywords, true)
                +    # :nocov:
                +  end
                +
                +  module LitRequireFrozen
                +    # Error class raised for using unfrozen literal string.
                +    class Error < Sequel::Error
                +    end
                +
                +    module DatabaseMethods
                +      def self.extended(db)
                +        db.extend_datasets(DatasetMethods)
                +      end
                +
                +      # Check given SQL is frozen before running it.
                +      def run(sql, opts=OPTS)
                +        @default_dataset.with_sql(sql)
                +        super
                +      end
                +    end
                +
                +    module DatasetMethods
                +      # Check given SQL is not an unfrozen string.
                +      def with_sql(sql, *args)
                +        _check_unfrozen_literal_string(sql)
                +        super
                +      end
                +
                +      # Check that placeholder string is frozen (or all entries
                +      # in placeholder array are frozen).
                +      def placeholder_literal_string_sql_append(sql, pls)
                +        str = pls.str
                +
                +        if str.is_a?(Array)
                +          str.each do |s|
                +            _check_unfrozen_literal_string(s)
                +          end
                +        else
                +          _check_unfrozen_literal_string(str)
                +        end
                +
                +        super
                +      end
                +
                +      private
                +
                +      # Base method that other methods used to check for whether a string should be allowed
                +      # as literal SQL. Allows non-strings as well as frozen strings.
                +      def _check_unfrozen_literal_string(str)
                +        return if !str.is_a?(String) || str.frozen?
                +
                +        if str.is_a?(LiteralString)
                +          _check_unfrozen_literal_string(str.source)
                +        else
                +          raise Error, "cannot treat unfrozen string as literal SQL: #{str.inspect}"
                +        end
                +      end
                +
                +      # Check literal strings appended to SQL.
                +      def literal_literal_string_append(sql, v)
                +        _check_unfrozen_literal_string(v)
                +        super
                +      end
                +
                +      # Check static SQL is not frozen.
                +      def static_sql(sql)
                +        _check_unfrozen_literal_string(sql)
                +        super
                +      end
                +    end
                +  end
                +
                +  Dataset.register_extension(:lit_require_frozen, LitRequireFrozen::DatasetMethods)
                +  Database.register_extension(:lit_require_frozen, LitRequireFrozen::DatabaseMethods)
                +end
      * Changed:
        MIT-LICENSE
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/MIT-LICENSE	2026-05-04 02:36:52.091733736 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/MIT-LICENSE	2026-05-04 02:36:52.130733755 +0000
                @@ -2 +2 @@
                -Copyright (c) 2008-2023 Jeremy Evans
                +Copyright (c) 2008-2026 Jeremy Evans and contributors
        lib/sequel/adapters/jdbc/derby.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/adapters/jdbc/derby.rb	2026-05-04 02:36:52.093733737 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/adapters/jdbc/derby.rb	2026-05-04 02:36:52.131733755 +0000
                @@ -2,0 +3,2 @@
                +# SEQUEL6: Remove
                +
        lib/sequel/adapters/jdbc/h2.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/adapters/jdbc/h2.rb	2026-05-04 02:36:52.093733737 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/adapters/jdbc/h2.rb	2026-05-04 02:36:52.131733755 +0000
                @@ -21 +21 @@
                -          run("COMMIT TRANSACTION #{transaction_id}", opts)
                +          run("COMMIT TRANSACTION #{transaction_id}".freeze, opts)
                @@ -39 +39 @@
                -          run("ROLLBACK TRANSACTION #{transaction_id}", opts)
                +          run("ROLLBACK TRANSACTION #{transaction_id}".freeze, opts)
        lib/sequel/adapters/postgres.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/adapters/postgres.rb	2026-05-04 02:36:52.096733739 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/adapters/postgres.rb	2026-05-04 02:36:52.133733756 +0000
                @@ -730 +730 @@
                -            LiteralString.new("#{prepared_arg_placeholder}#{i}")
                +            LiteralString.new("#{prepared_arg_placeholder}#{i}".freeze)
        lib/sequel/adapters/shared/mssql.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/adapters/shared/mssql.rb	2026-05-04 02:36:52.096733739 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/adapters/shared/mssql.rb	2026-05-04 02:36:52.134733757 +0000
                @@ -109 +109 @@
                -        sql = "DECLARE #{declarations.join(', ')}; EXECUTE @RC = #{name} #{values.join(', ')}; SELECT #{names.join(', ')}"
                +        sql = "DECLARE #{declarations.join(', ')}; EXECUTE @RC = #{name} #{values.join(', ')}; SELECT #{names.join(', ')}".freeze
                @@ -403 +403 @@
                -        run(ds.into(name).sql)
                +        run(ds.into(name).sql.freeze)
                @@ -881 +881 @@
                -          sql = multi_insert_sql(columns, values)[0]
                +          sql = multi_insert_sql(columns, values)[0].freeze
        lib/sequel/adapters/shared/mysql.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/adapters/shared/mysql.rb	2026-05-04 02:36:52.097733739 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/adapters/shared/mysql.rb	2026-05-04 02:36:52.134733757 +0000
                @@ -43 +43 @@
                -        run("XA COMMIT #{literal(transaction_id)}", opts)
                +        run("XA COMMIT #{literal(transaction_id)}".freeze, opts)
                @@ -106 +106 @@
                -        metadata_dataset.with_sql(sql).each do |r|
                +        metadata_dataset.with_sql(sql.freeze).each do |r|
                @@ -118 +118 @@
                -        run("XA ROLLBACK #{literal(transaction_id)}", opts)
                +        run("XA ROLLBACK #{literal(transaction_id)}".freeze, opts)
                @@ -783 +783,2 @@
                -        ds = db.send(:metadata_dataset).with_sql(((opts[:extended] && (db.mariadb? || db.server_version < 50700)) ? 'EXPLAIN EXTENDED ' : 'EXPLAIN ') + select_sql).naked
                +        sql = ((opts[:extended] && (db.mariadb? || db.server_version < 50700)) ? 'EXPLAIN EXTENDED ' : 'EXPLAIN ') + select_sql
                +        ds = db.send(:metadata_dataset).with_sql(sql.freeze).naked
        lib/sequel/adapters/shared/postgres.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/adapters/shared/postgres.rb	2026-05-04 02:36:52.098733740 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/adapters/shared/postgres.rb	2026-05-04 02:36:52.136733757 +0000
                @@ -342 +342 @@
                -        run("COMMIT PREPARED #{literal(transaction_id)}", opts)
                +        run("COMMIT PREPARED #{literal(transaction_id)}".freeze, opts)
                @@ -419 +419 @@
                -          run("ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(column)} DROP DEFAULT", server_hash)
                +          run("ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(column)} DROP DEFAULT".freeze, server_hash)
                @@ -456 +456 @@
                -        self << create_function_sql(name, definition, opts)
                +        self << create_function_sql(name, definition, opts).freeze
                @@ -467 +467 @@
                -        self << create_language_sql(name, opts)
                +        self << create_language_sql(name, opts).freeze
                @@ -476 +476 @@
                -        self << create_schema_sql(name, opts)
                +        self << create_schema_sql(name, opts).freeze
                @@ -512 +512 @@
                -        self << create_trigger_sql(table, name, function, opts)
                +        self << create_trigger_sql(table, name, function, opts).freeze
                @@ -545 +545 @@
                -        run "DO #{"LANGUAGE #{literal(language.to_s)} " if language}#{literal(code)}"
                +        run "DO #{"LANGUAGE #{literal(language.to_s)} " if language}#{literal(code)}".freeze
                @@ -555 +555 @@
                -        self << drop_function_sql(name, opts)
                +        self << drop_function_sql(name, opts).freeze
                @@ -564 +564 @@
                -        self << drop_language_sql(name, opts)
                +        self << drop_language_sql(name, opts).freeze
                @@ -573 +573 @@
                -        self << drop_schema_sql(name, opts)
                +        self << drop_schema_sql(name, opts).freeze
                @@ -584 +584 @@
                -        self << drop_trigger_sql(table, name, opts)
                +        self << drop_trigger_sql(table, name, opts).freeze
                @@ -748 +748 @@
                -        self << rename_schema_sql(name, new_name)
                +        self << rename_schema_sql(name, new_name).freeze
                @@ -759 +759 @@
                -        run "REFRESH MATERIALIZED VIEW#{' CONCURRENTLY' if opts[:concurrently]} #{quote_schema_table(name)}"
                +        run "REFRESH MATERIALIZED VIEW#{' CONCURRENTLY' if opts[:concurrently]} #{quote_schema_table(name)}".freeze
                @@ -772 +772 @@
                -          seq_ds = metadata_dataset.from(:pg_sequence).where(:seqrelid=>regclass_oid(LiteralString.new(seq)))
                +          seq_ds = metadata_dataset.from(:pg_sequence).where(:seqrelid=>regclass_oid(LiteralString.new(seq.freeze)))
                @@ -787 +787 @@
                -        run("ROLLBACK PREPARED #{literal(transaction_id)}", opts)
                +        run("ROLLBACK PREPARED #{literal(transaction_id)}".freeze, opts)
                @@ -1472 +1472 @@
                -        cols[-1] = Sequel.lit("PERIOD #{quote_identifier(cols[-1])}")
                +        cols[-1] = Sequel.lit("PERIOD #{quote_identifier(cols[-1])}".freeze)
        lib/sequel/adapters/shared/sqlite.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/adapters/shared/sqlite.rb	2026-05-04 02:36:52.098733740 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/adapters/shared/sqlite.rb	2026-05-04 02:36:52.136733757 +0000
                @@ -198 +198 @@
                -        metadata_dataset.with_sql("PRAGMA table_#{'x' if sqlite_version > 33100}info(?)", input_identifier_meth(opts[:dataset]).call(table_name))
                +        metadata_dataset.with_sql("PRAGMA table_#{'x' if sqlite_version > 33100}info(?)".freeze, input_identifier_meth(opts[:dataset]).call(table_name))
                @@ -410 +410 @@
                -          c[:default] = LiteralString.new(c[:default]) if c[:default]
                +          c[:default] = LiteralString.new(c[:default]).freeze if c[:default]
                @@ -687 +687 @@
                -        ds = db.send(:metadata_dataset).clone(:sql=>"EXPLAIN #{select_sql}")
                +        ds = db.send(:metadata_dataset).clone(:sql=>"EXPLAIN #{select_sql}".freeze)
        lib/sequel/adapters/sqlite.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/adapters/sqlite.rb	2026-05-04 02:36:52.098733740 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/adapters/sqlite.rb	2026-05-04 02:36:52.137733758 +0000
                @@ -391 +391 @@
                -          LiteralString.new("#{prepared_arg_placeholder}#{k.to_s.gsub('.', '__')}")
                +          LiteralString.new("#{prepared_arg_placeholder}#{k.to_s.gsub('.', '__')}".freeze)
        lib/sequel/adapters/tinytds.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/adapters/tinytds.rb	2026-05-04 02:36:52.099733740 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/adapters/tinytds.rb	2026-05-04 02:36:52.137733758 +0000
                @@ -208 +208 @@
                -          LiteralString.new("@#{k.to_s.gsub('.', '__')}")
                +          LiteralString.new("@#{k.to_s.gsub('.', '__')}".freeze)
        lib/sequel/connection_pool.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/connection_pool.rb	2026-05-04 02:36:52.100733741 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/connection_pool.rb	2026-05-04 02:36:52.138733759 +0000
                @@ -133,0 +134,6 @@
                +  # Only for use by extension that need to disconnect a connection inside acquire.
                +  # Takes the connection and any arguments accepted by acquire.
                +  def disconnect_acquired_connection(conn, *)
                +    disconnect_connection(conn)
                +  end
                +
        lib/sequel/connection_pool/sharded_timed_queue.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/connection_pool/sharded_timed_queue.rb	2026-05-04 02:36:52.100733741 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/connection_pool/sharded_timed_queue.rb	2026-05-04 02:36:52.138733759 +0000
                @@ -74 +74 @@
                -          break unless (conn = queue.pop(timeout: 0)) && !conns[conn]
                +          break unless (conn = available(queue, server)) && !conns[conn]
                @@ -94 +94 @@
                -  # and then the method will only disconnect connectsion from those specified shards.
                +  # and then the method will only disconnect connections from those specified shards.
                @@ -98 +98,3 @@
                -      while conn = queue.pop(timeout: 0)
                +      nconns = 0
                +      while conn = available(queue, server)
                +        nconns += 1
                @@ -101 +103 @@
                -      fill_queue(server)
                +      fill_queue(server, nconns)
                @@ -135 +137 @@
                -        fill_queue(server)
                +        fill_queue(server, 1)
                @@ -170 +172 @@
                -        while conn = queue.pop(timeout: 0)
                +        while conn = available(queue, server)
                @@ -227,0 +230,6 @@
                +  # Only for use by extension that need to disconnect a connection inside acquire.
                +  # Takes the connection and any arguments accepted by acquire.
                +  def disconnect_acquired_connection(conn, _, server)
                +    disconnect_pool_connection(conn, server)
                +  end
                +
                @@ -247 +255,4 @@
                -  def fill_queue(server)
                +  #
                +  # nconns specifies the maximum number of connections to add, which should
                +  # be the number of connections that were disconnected.
                +  def fill_queue(server, nconns)
                @@ -249 +260 @@
                -    if queue.num_waiting > 0
                +    if nconns > 0 && queue.num_waiting > 0
                @@ -251 +262,2 @@
                -        while queue.num_waiting > 0 && (conn = try_make_new(server))
                +        while nconns > 0 && queue.num_waiting > 0 && (conn = try_make_new(server))
                +          nconns -= 1
                @@ -300 +312 @@
                -        fill_queue(server)
                +        fill_queue(server, to_disconnect.size)
                @@ -314 +326 @@
                -    if conn = queue.pop(timeout: 0) || try_make_new(server) || queue.pop(timeout: @timeout)
                +    if conn = available(queue, server) || try_make_new(server) || wait_until_available(queue, server)
                @@ -319,0 +332,13 @@
                +  end
                +
                +  # Return the next connection in the pool if there is one available. Returns nil
                +  # if no connection is currently available.
                +  def available(queue, _server)
                +    queue.pop(timeout: 0)
                +  end
                +
                +  # Return the next connection in the pool if there is one available. If not, wait
                +  # until the timeout for a connection to become available. If there is still no
                +  # available connection, return nil.
                +  def wait_until_available(queue, _server)
                +    queue.pop(timeout: @timeout)
        lib/sequel/connection_pool/timed_queue.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/connection_pool/timed_queue.rb	2026-05-04 02:36:52.100733741 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/connection_pool/timed_queue.rb	2026-05-04 02:36:52.139733759 +0000
                @@ -45 +45 @@
                -          break unless (conn = @queue.pop(timeout: 0)) && !conns[conn]
                +          break unless (conn = available) && !conns[conn]
                @@ -62 +62,3 @@
                -    while conn = @queue.pop(timeout: 0)
                +    nconns = 0
                +    while conn = available
                +      nconns += 1
                @@ -65 +67 @@
                -    fill_queue
                +    fill_queue(nconns)
                @@ -97 +99 @@
                -        fill_queue
                +        fill_queue(1)
                @@ -159,2 +161,5 @@
                -  def fill_queue
                -    if @queue.num_waiting > 0
                +  #
                +  # nconns specifies the maximum number of connections to add, which should
                +  # be the number of connections that were disconnected.
                +  def fill_queue(nconns)
                +    if nconns > 0 && @queue.num_waiting > 0
                @@ -162 +167 @@
                -        while @queue.num_waiting > 0 && (conn = try_make_new)
                +        while nconns > 0 && @queue.num_waiting > 0 && (conn = try_make_new)
                @@ -210 +215 @@
                -        fill_queue
                +        fill_queue(to_disconnect.size)
                @@ -223 +228 @@
                -    if conn = @queue.pop(timeout: 0) || try_make_new || @queue.pop(timeout: @timeout)
                +    if conn = available || try_make_new || wait_until_available
                @@ -228,0 +234,13 @@
                +  end
                +
                +  # Return the next connection in the pool if there is one available. Returns nil
                +  # if no connection is currently available.
                +  def available
                +    @queue.pop(timeout: 0)
                +  end
                +
                +  # Return the next connection in the pool if there is one available. If not, wait
                +  # until the timeout for a connection to become available. If there is still no
                +  # available connection, return nil.
                +  def wait_until_available
                +    @queue.pop(timeout: @timeout)
        lib/sequel/database/schema_generator.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/database/schema_generator.rb	2026-05-04 02:36:52.102733742 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/database/schema_generator.rb	2026-05-04 02:36:52.140733759 +0000
                @@ -5,0 +6,27 @@
                +    module ColumnOptionMerger
                +      private
                +
                +      # Merge given options into the column's default options. For backwards compatibility,
                +      # the options take priority, but in cases where the option value overrides the argument
                +      # value, and the values are different, we warn as this is likely to be an error in the
                +      # code.
                +      def _merge_column_options(defaults, opts)
                +        defaults.merge!(opts) do |k, defv, v|
                +          unless defv == v
                +            # :nocov:
                +            if RUBY_VERSION >= "3.2"
                +            # :nocov:
                +              caller_loc = Thread.each_caller_location do |loc|
                +                break loc unless loc.path == __FILE__
                +              end
                +              caller_loc &&= "#{caller_loc.path}:#{caller_loc.lineno}: "
                +            end
                +            warn("#{caller_loc}#{k.inspect} option value (#{v.inspect}) overrides argument value (#{defv.inspect})")
                +          end
                +
                +          v
                +        end
                +      end
                +    end
                +    private_constant :ColumnOptionMerger
                +
                @@ -19,0 +47,2 @@
                +      include ColumnOptionMerger
                +
                @@ -176 +205 @@
                -        columns << {:name => name, :type => type}.merge!(opts)
                +        columns << _merge_column_options({:name => name, :type => type}, opts)
                @@ -182 +211 @@
                -      
                +
                @@ -249 +278 @@
                -        column(name, Integer, opts)
                +        column(name, opts.fetch(:type, Integer), opts)
                @@ -431,0 +461,2 @@
                +      include ColumnOptionMerger
                +
                @@ -457 +488 @@
                -        op = {:op => :add_column, :name => name, :type => type}.merge!(opts)
                +        op = _merge_column_options({:op => :add_column, :name => name, :type => type}, opts)
                @@ -522 +553 @@
                -        add_column(name, Integer, {:table=>table}.merge!(opts))
                +        add_column(name, opts.fetch(:type, Integer), {:table=>table}.merge!(opts))
        lib/sequel/database/schema_methods.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/database/schema_methods.rb	2026-05-04 02:36:52.102733742 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/database/schema_methods.rb	2026-05-04 02:36:52.140733759 +0000
                @@ -822 +822 @@
                -      run(create_table_as_sql(name, sql, options))
                +      run(create_table_as_sql(name, sql, options).freeze)
        lib/sequel/dataset/placeholder_literalizer.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/dataset/placeholder_literalizer.rb	2026-05-04 02:36:52.104733743 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/dataset/placeholder_literalizer.rb	2026-05-04 02:36:52.142733760 +0000
                @@ -119,0 +120,2 @@
                +          frags.each(&:freeze)
                +          frags.freeze
                @@ -127,0 +130 @@
                +          ds.opts[:sql].freeze
        lib/sequel/dataset/prepared_statements.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/dataset/prepared_statements.rb	2026-05-04 02:36:52.104733743 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/dataset/prepared_statements.rb	2026-05-04 02:36:52.142733760 +0000
                @@ -57 +57 @@
                -        sql = prepared_sql
                +        sql = prepared_sql.freeze
                @@ -226 +226 @@
                -          with_sql(prepared_sql)
                +          with_sql(prepared_sql.freeze)
                @@ -290 +290 @@
                -          sql = literal(Sequel::SQL::PlaceholderLiteralString.new(@opts[:prepared_sql_frags], @opts[:bind_arguments], false))
                +          sql = literal(Sequel::SQL::PlaceholderLiteralString.new(@opts[:prepared_sql_frags], @opts[:bind_arguments], false)).freeze
                @@ -322,0 +323,3 @@
                +        frags.freeze
                +        frags.each(&:freeze)
                +        prepared_sql.freeze
                @@ -412 +415 @@
                -        sql = ps.prepared_sql
                +        sql = ps.prepared_sql.freeze
        lib/sequel/dataset/query.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/dataset/query.rb	2026-05-04 02:36:52.104733743 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/dataset/query.rb	2026-05-04 02:36:52.142733760 +0000
                @@ -1304 +1304 @@
                -        sql = public_send(sql, *args)
                +        sql = public_send(sql, *args).freeze
                @@ -1477 +1477,4 @@
                -    
                +
                +    PAREN_WRAPPER = ["(".freeze, ")".freeze].freeze
                +    private_constant :PAREN_WRAPPER
                +
                @@ -1498 +1501 @@
                -        LiteralString.new("(#{expr})")
                +        SQL::PlaceholderLiteralString.new(PAREN_WRAPPER, [expr])
        lib/sequel/dataset/sql.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/dataset/sql.rb	2026-05-04 02:36:52.105733743 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/dataset/sql.rb	2026-05-04 02:36:52.143733761 +0000
                @@ -56 +56 @@
                -          sql << v
                +          literal_literal_string_append(sql, v)
                @@ -1424,0 +1425,5 @@
                +    end
                +
                +    # Append string to SQL string.
                +    def literal_literal_string_append(sql, v)
                +      sql << v
        lib/sequel/extensions/connection_expiration.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/extensions/connection_expiration.rb	2026-05-04 02:36:52.106733743 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/extensions/connection_expiration.rb	2026-05-04 02:36:52.144733761 +0000
                @@ -94 +94 @@
                -          disconnect_connection(conn)
                +          disconnect_acquired_connection(conn, *a)
        lib/sequel/extensions/connection_validator.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/extensions/connection_validator.rb	2026-05-04 02:36:52.106733743 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/extensions/connection_validator.rb	2026-05-04 02:36:52.145733762 +0000
                @@ -121 +121 @@
                -              disconnect_connection(conn)
                +              disconnect_acquired_connection(conn, *a)
        lib/sequel/extensions/dataset_run.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/extensions/dataset_run.rb	2026-05-04 02:36:52.107733744 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/extensions/dataset_run.rb	2026-05-04 02:36:52.145733762 +0000
                @@ -33 +33 @@
                -        db.run(sql, :server=>server)
                +        db.run(sql.freeze, :server=>server)
                @@ -35 +35 @@
                -        db.run(sql)
                +        db.run(sql.freeze)
        lib/sequel/extensions/date_arithmetic.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/extensions/date_arithmetic.rb	2026-05-04 02:36:52.107733744 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/extensions/date_arithmetic.rb	2026-05-04 02:36:52.146733762 +0000
                @@ -85,2 +85,2 @@
                -        MYSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s.upcase[0...-1]).freeze}).freeze
                -        MSSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s[0...-1]).freeze}).freeze
                +        MYSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s.upcase[0...-1].freeze).freeze}).freeze
                +        MSSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s[0...-1].freeze).freeze}).freeze
                @@ -88 +88 @@
                -        DERBY_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit("SQL_TSI_#{s.to_s.upcase[0...-1]}").freeze}).freeze
                +        DERBY_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit("SQL_TSI_#{s.to_s.upcase[0...-1]}".freeze).freeze}).freeze
                @@ -90 +90 @@
                -        DB2_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s).freeze}).freeze
                +        DB2_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s.freeze).freeze}).freeze
                @@ -110 +110 @@
                -                placeholder << "#{', ' unless placeholder.empty?}#{sql_unit} := "
                +                placeholder << "#{', ' unless placeholder.empty?}#{sql_unit} := ".freeze
                @@ -159 +159 @@
                -              expr = Sequel.lit(["{fn timestampadd(#{sql_unit}, ", ", timestamp(", "))}"], value, expr)
                +              expr = Sequel.lit(["{fn timestampadd(#{sql_unit}, ".freeze, ", timestamp(", "))}"], value, expr)
        lib/sequel/extensions/migration.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/extensions/migration.rb	2026-05-04 02:36:52.109733745 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/extensions/migration.rb	2026-05-04 02:36:52.148733763 +0000
                @@ -189,16 +189,13 @@
                -      begin
                -        instance_exec(&block)
                -      rescue
                -        just_raise = true
                -      end
                -      if just_raise
                -        Proc.new{raise Sequel::Error, "irreversible migration method used in #{block.source_location.first}, you may need to write your own down method"}
                -      else
                -        actions = @actions.reverse
                -        Proc.new do
                -          actions.each do |a|
                -            pr = a.last.is_a?(Proc) ? a.pop : nil
                -            # Allow calling private methods as the reversing methods are private
                -            send(*a, &pr)
                -          end
                -        end
                +      instance_exec(&block)
                +    rescue NoMethodError => e
                +      Proc.new{raise Sequel::Error, "irreversible migration method \"#{e.name}\" used in #{block.source_location.first}, you may need to write your own down method"}
                +    rescue => e
                +      Proc.new{raise Sequel::Error, "unable to reverse migration due to #{e.class} in #{block.source_location.first}, you may need to write your own down method"}
                +    else
                +      actions = @actions.reverse
                +      Proc.new do
                +       actions.each do |a|
                +        pr = a.last.is_a?(Proc) ? a.pop : nil
                +        # Allow calling private methods as the reversing methods are private
                +        send(*a, &pr)
                +       end
                @@ -273 +270 @@
                -      raise if args.first.is_a?(Array)
                +      super if args.first.is_a?(Array)
        lib/sequel/extensions/pg_enum.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/extensions/pg_enum.rb	2026-05-04 02:36:52.110733745 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/extensions/pg_enum.rb	2026-05-04 02:36:52.149733764 +0000
                @@ -130 +130 @@
                -        run(sql)
                +        run(sql.freeze)
        lib/sequel/model/associations.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/model/associations.rb	2026-05-04 02:36:52.117733749 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/model/associations.rb	2026-05-04 02:36:52.156733767 +0000
                @@ -114,0 +115,2 @@
                +          when :lateral_subquery
                +            apply_lateral_subquery_eager_graph_limit_strategy(ds)
                @@ -301 +303,6 @@
                -            objects = apply_eager_limit_strategy(ds, strategy, eager_limit).all
                +
                +            objects = if strategy == :lateral_subquery
                +              apply_lateral_subquery_eager_limit_strategy(ds, ids, eager_limit).all
                +            else
                +              apply_eager_limit_strategy(ds, strategy, eager_limit).all
                +            end
                @@ -323 +330 @@
                -              objects.concat(ds.with_sql(sql).to_a)
                +              objects.concat(ds.with_sql(sql.freeze).to_a)
                @@ -369 +376,6 @@
                -          ds = filter_by_associations_conditions_dataset.where(filter_by_associations_conditions_subquery_conditions(obj))
                +          ds = if filter_by_associations_limit_strategy == :lateral_subquery
                +            apply_lateral_subquery_filter_limit_strategy(associated_eager_dataset, obj)
                +          else
                +            filter_by_associations_conditions_dataset.where(filter_by_associations_conditions_subquery_conditions(obj))
                +          end
                +
                @@ -756,0 +769,55 @@
                +        def lateral_subquery_eager_limit_strategy_lateral_dataset(ds, limit_and_offset)
                +          ds.
                +            where(Array(filter_by_associations_conditions_key).zip(Array(filter_by_associations_conditions_associated_keys))).
                +            limit(*limit_and_offset).
                +            lateral
                +        end
                +
                +        def apply_lateral_subquery_eager_limit_strategy(ds, ids, limit_and_offset)
                +          table_name = self[:model].table_name
                +          associated_table_name = associated_class.table_name
                +
                +          associated_class.
                +            from(table_name).
                +            select_all(associated_table_name).
                +            join(lateral_subquery_eager_limit_strategy_lateral_dataset(ds, limit_and_offset).as(associated_table_name), true).
                +            order(*self[:order])
                +        end
                +
                +        # Return an expression to filter the filter by associations dataset to only
                +        # rows related to given objects.
                +        def _lateral_subquery_filter_limit_strategy_conditions(obj, key, value_method, value_column)
                +          value = case obj
                +          when Array
                +            if key.is_a?(Array)
                +              key_methods = Array(value_method)
                +              obj.map{|o| key_methods.map{|meth| o.send(meth)}}
                +            else
                +              obj.map{|o| o.send(value_method)}
                +            end
                +          when Sequel::Dataset
                +            obj.select(*Array(qualify(associated_class.table_name, value_column)))
                +          else
                +            if key.is_a?(Array)
                +              return Array(key).zip(Array(value_method).map{|meth| obj.send(meth)})
                +            else
                +              obj.send(value_method)
                +            end
                +          end
                +
                +          {key => value}
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_lateral_dataset(ds, obj)
                +          lateral_subquery_filter_limit_strategy_filter_lateral_dataset(ds.
                +            select(*qualify(associated_class.table_name, associated_class.primary_key)).
                +            limit(*limit_and_offset).
                +            lateral)
                +        end
                +
                +        def apply_lateral_subquery_filter_limit_strategy(ds, obj)
                +          lateral_subquery_filter_limit_strategy_filter_dataset(self[:model].
                +            select(*lateral_subquery_filter_limit_strategy_lateral_dataset_select).
                +            join(lateral_subquery_filter_limit_strategy_lateral_dataset(ds, obj).as(associated_class.table_name), filter_by_associations_conditions_subquery_conditions(obj)), obj)
                +        end
                +
                @@ -779 +846,6 @@
                -              apply_eager_limit_strategy(ds.where(predicate_key=>arg), eager_limit_strategy)
                +              strategy = eager_limit_strategy
                +              if strategy == :lateral_subquery
                +                apply_lateral_subquery_eager_limit_strategy(ds, arg, limit_and_offset)
                +              else
                +                apply_eager_limit_strategy(ds.where(predicate_key=>arg), strategy)
                +              end
                @@ -1021 +1093 @@
                -        # Support a correlated subquery limit strategy when using eager_graph.
                +        # Support a lateral_subquery and correlated_subquery limit strategy when using eager_graph.
                @@ -1120,0 +1193,48 @@
                +        # Use a LATERAL subquery to limit the dataset.  Note that this will not
                +        # work correctly if the associated dataset uses qualified identifers in the WHERE clause,
                +        # as they would reference the containing query instead of the subquery.
                +        #
                +        # This does not contain the conditions that are necessary to join to the
                +        # query, since the necessary qualifier is not passed as an argument.
                +        def apply_lateral_subquery_eager_graph_limit_strategy(ds)
                +          table_name = ds.first_source_alias
                +          qualifier = ds.opts[:eager_options][:implicit_qualifier]
                +          graph_conditions = self[:_graph_conditions]
                +
                +          unless Sequel.condition_specifier?(graph_conditions)
                +            raise Error, "lateral_subquery eager graph limit strategy only supported when graph conditions are a hash or array of pairs"
                +          end
                +
                +          ds.
                +            limit(*limit_and_offset).
                +            order(*self[:order]).
                +            where(graph_conditions.map{|k, v| [qualify(table_name, k), qualify(qualifier, v)]}).
                +            lateral
                +        end
                +
                +        # Avoid setting duplicate predicate condition when using the lateral subquery
                +        # eager limit strategy.
                +        def eager_loading_set_predicate_condition(ds, eo)
                +          eager_limit_strategy == :lateral_subquery ? ds : super
                +        end
                +
                +        def apply_lateral_subquery_eager_limit_strategy(ds, ids, limit_and_offset)
                +          super.where(qualify(self[:model].table_name, self[:primary_key]) => ids)
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_conditions(obj)
                +          _lateral_subquery_filter_limit_strategy_conditions(obj, filter_by_associations_conditions_key, self[:key_method], self[:key])
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_filter_lateral_dataset(ds)
                +          ds.where(Array(filter_by_associations_conditions_key).zip(Array(filter_by_associations_conditions_associated_keys)))
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_filter_dataset(ds, obj)
                +          ds.where(lateral_subquery_filter_limit_strategy_conditions(obj))
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_lateral_dataset_select
                +          qualified_primary_key
                +        end
                +
                @@ -1434,0 +1555,46 @@
                +        def lateral_subquery_filter_limit_strategy_conditions(obj)
                +          _lateral_subquery_filter_limit_strategy_conditions(obj, lateral_subquery_filter_limit_strategy_conditions_key, right_primary_key_method, right_primary_key)
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_conditions_key
                +          self[:right_key]
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_filter_lateral_dataset(ds)
                +          ds.where(Array(filter_by_associations_conditions_key).zip(Array(filter_by_associations_conditions_associated_keys)))
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_filter_dataset(ds, obj)
                +          ds.where(filter_by_associations_conditions_key => ds.db.from(self[:join_table]).
                +            select(*self[:left_key]).
                +            where(lateral_subquery_filter_limit_strategy_conditions(obj)))
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_lateral_dataset_select
                +          qualify(self[:model].table_name, self[:left_primary_key])
                +        end
                +
                +        def lateral_subquery_eager_limit_strategy_lateral_dataset(ds, limit_and_offset)
                +          ds = super
                +          select = ds.opts[:select].dup
                +          left_key_alias = self[:left_key_alias]
                +          select.pop while (s = select.last).is_a?(Sequel::SQL::AliasedExpression) && (left_key_alias.is_a?(Array) ? left_key_alias.include?(s.alias) : s.alias == left_key_alias)
                +          ds = ds.clone(:select=>select)
                +        end
                +
                +        def apply_lateral_subquery_eager_limit_strategy(ds, ids, limit_and_offset)
                +          table_name = self[:model].table_name
                +          super.
                +            select_all(associated_class.table_name).
                +            select_append(*qualify(table_name, self[:left_primary_keys]).zip(Array(self[:left_key_alias])).map{|id, aliaz| Sequel.as(id, aliaz)}).
                +            where(qualify(table_name, self[:left_primary_key]) => ids)
                +        end
                +
                +        def apply_lateral_subquery_eager_graph_limit_strategy(ds)
                +          ds.
                +            limit(*limit_and_offset).
                +            where(qualify(join_table_alias, self[:left_keys]).zip(qualify(ds.opts[:eager_options][:implicit_qualifier], self[:left_primary_key_columns]))).
                +            order(*self[:order]).
                +            lateral
                +        end
                +
                @@ -2415,0 +2582 @@
                +          graph_conditions = opts[:_graph_conditions] = use_only_conditions ? only_conditions : cks.zip(pkcs) + conditions
                @@ -2418 +2585,5 @@
                -            ds = ds.graph(opts.apply_eager_graph_limit_strategy(eo[:limit_strategy], eager_graph_dataset(opts, eo)), use_only_conditions ? only_conditions : cks.zip(pkcs) + conditions, eo.merge(:select=>select, :join_type=>eo[:join_type]||join_type, :qualify=>:deep), &graph_block)
                +            graph_limit_strategy = eo[:limit_strategy]
                +            egds = opts.apply_eager_graph_limit_strategy(graph_limit_strategy, eager_graph_dataset(opts, eo))
                +            graph_conditions_true = true if graph_limit_strategy == :lateral_subquery
                +
                +            ds = ds.graph(egds, graph_conditions_true || graph_conditions, eo.merge(:select=>select, :join_type=>eo[:join_type]||join_type, :qualify=>:deep), &graph_block)
                @@ -2500,0 +2672,3 @@
                +          if eager_options[:limit_strategy] == :lateral_subquery
                +            ds = ds.clone(:eager_options=>eager_options)
                +          end
        lib/sequel/plugins/dataset_associations.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/plugins/dataset_associations.rb	2026-05-04 02:36:52.121733750 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/plugins/dataset_associations.rb	2026-05-04 02:36:52.163733770 +0000
                @@ -85 +85 @@
                -          ds = r.associated_class.dataset
                +          klass = r.associated_class
                @@ -86,0 +87,19 @@
                +
                +          if r.send(:filter_by_associations_limit_strategy) == :lateral_subquery
                +            ds = r.send(:associated_eager_dataset)
                +
                +            case r[:type]
                +            when :one_to_one, :one_to_many
                +              sds = sds.select(*Array(r.qualified_primary_key))
                +            else
                +              sds = sds.select(*r[:left_primary_keys])
                +              ds = ds.select_all(klass.table_name)
                +              update_select = true
                +            end
                +
                +            ds = r.send(:apply_lateral_subquery_eager_limit_strategy, ds, sds, r.limit_and_offset)
                +            ds = ds.clone(:select=>ds.opts[:select][0,1]) if update_select
                +            return ds.clone(:eager=>nil, :eager_graph=>nil)
                +          end
                +
                +          ds = klass.dataset
        lib/sequel/plugins/dirty.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/plugins/dirty.rb	2026-05-04 02:36:52.121733750 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/plugins/dirty.rb	2026-05-04 02:36:52.166733772 +0000
                @@ -237,2 +237,5 @@
                -            check_missing_initial_value(column)
                -            iv[column] = get_column_value(column)
                +            if db_schema[column]
                +              check_missing_initial_value(column)
                +              iv[column] = get_column_value(column)
                +            end
                +
        lib/sequel/plugins/many_through_many.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/plugins/many_through_many.rb	2026-05-04 02:36:52.124733752 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/plugins/many_through_many.rb	2026-05-04 02:36:52.171733774 +0000
                @@ -148,0 +149,21 @@
                +        def lateral_subquery_filter_limit_strategy_conditions_key
                +          qualify(reverse_edges.first[:table], reverse_edges.first[:left])
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_filter_lateral_dataset(ds)
                +          ds.where(Array(qualify(edges.first[:table], edges.first[:right])).zip(Array(qualify(self[:model].table_name, edges.first[:left]))))
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_filter_dataset(ds, obj)
                +          first_edge, *remaining_edges = edges
                +          filter_ds = ds.db.from(first_edge[:table]).
                +            select(*qualify(first_edge[:table], first_edge[:right])).
                +            where(lateral_subquery_filter_limit_strategy_conditions(obj))
                +
                +          remaining_edges.each do |edge|
                +            filter_ds = filter_ds.join(edge[:table], Array(edge[:right]).zip(Array(edge[:left])))
                +          end
                +
                +          ds.where(qualify(self[:model].table_name, self[:left_primary_key]) => filter_ds)
                +        end
                +
        lib/sequel/plugins/mssql_optimistic_locking.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/plugins/mssql_optimistic_locking.rb	2026-05-04 02:36:52.124733752 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/plugins/mssql_optimistic_locking.rb	2026-05-04 02:36:52.171733774 +0000
                @@ -58 +58 @@
                -          rows = ds.clone(ds.send(:default_server_opts, :sql=>ds.output(nil, [Sequel[:inserted][lc]]).update_sql(columns))).all
                +          rows = ds.clone(ds.send(:default_server_opts, :sql=>ds.output(nil, [Sequel[:inserted][lc]]).update_sql(columns).freeze)).all
        lib/sequel/plugins/pg_xmin_optimistic_locking.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/plugins/pg_xmin_optimistic_locking.rb	2026-05-04 02:36:52.125733752 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/plugins/pg_xmin_optimistic_locking.rb	2026-05-04 02:36:52.173733775 +0000
                @@ -102 +102 @@
                -          rows = ds.clone(ds.send(:default_server_opts, :sql=>ds.returning(:xmin).update_sql(columns))).all
                +          rows = ds.clone(ds.send(:default_server_opts, :sql=>ds.returning(:xmin).update_sql(columns).freeze)).all
        lib/sequel/plugins/serialization.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/plugins/serialization.rb	2026-05-04 02:36:52.125733752 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/plugins/serialization.rb	2026-05-04 02:36:52.174733775 +0000
                @@ -32,0 +33,9 @@
                +    # Note that use of an unsafe serialization method can result in an attack vector
                +    # (potentially allowing remote code execution) if an attacker has the ability to
                +    # store data directly in the underlying column. This would affect the marshal
                +    # serialization format, and on older versions of Ruby, potentially the yaml and
                +    # json serialization formats as well. It can also affect custom formats. You
                +    # should ensure that attackers do not have access to store data directly in the
                +    # underlying column when using this plugin (especially when using an unsafe
                +    # serialization method).
                +    #
                @@ -100 +109 @@
                -          v = v.unpack('m')[0] unless v[0..1] == "\x04\x08"
                +          v = v.unpack('m')[0] unless v.start_with?("\x04\x08")
        lib/sequel/version.rb
                --- /tmp/d20260504-1904-vw3u71/sequel-5.101.0/lib/sequel/version.rb	2026-05-04 02:36:52.129733754 +0000
                +++ /tmp/d20260504-1904-vw3u71/sequel-5.104.0/lib/sequel/version.rb	2026-05-04 02:36:52.183733780 +0000
                @@ -9 +9 @@
                -  MINOR = 101
                +  MINOR = 104

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

gem compare --diff sequel 5.101.0 5.104.0

Compared versions: ["5.101.0", "5.104.0"]
  DIFFERENT files:
    5.101.0->5.104.0:
      * Added:
        lib/sequel/extensions/connection_checkout_event_callback.rb
                --- /tmp/20260504-1830-1m2xxg	2026-05-04 02:37:12.336769302 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/extensions/connection_checkout_event_callback.rb	2026-05-04 02:37:12.313769445 +0000
                @@ -0,0 +1,151 @@
                +# frozen-string-literal: true
                +#
                +# The connection_checkout_event_callback extension modifies a database's
                +# connection pool to allow for a checkout event callback. This callback is
                +# called with the following arguments:
                +#
                +# :immediately_available :: Connection immediately available and returned
                +# :not_immediately_available :: Connection not immediately available
                +# :new_connection :: New connection created and returned
                +# Float :: Number of seconds waiting to acquire a connection
                +#
                +# This is a low-level extension that allows for building telemetry
                +# information. It doesn't implement any telemetry reporting itself. The
                +# main reason for recording this information is to use it to determine the
                +# appropriate size for the connection pool. Having too large a connection
                +# pool can waste resources, while having too small a connection pool can
                +# result in substantial time to check out a connection. In general, you
                +# want to use as small a pool as possible while keeping the time to
                +# checkout a connection low.
                +#
                +# To use the connection checkout event callback, you must first load the
                +# extension:
                +#
                +#   DB.extension(:connection_checkout_event_callback)
                +#
                +# By default, an empty proc is used as the callback so that loading the
                +# support doesn't break anything. If you are using the extension, you
                +# should set the callback at some point during application startup:
                +#
                +#   DB.pool.on_checkout_event = proc do |event|
                +#     # ...
                +#   end
                +#
                +# When using the sharded connection pool, the callback is also
                +# passed a second argument, the requested server shard (generally a
                +# symbol), allowing for collection of per-shard telemetry:
                +#
                +#   DB.pool.on_checkout_event = proc do |event, server|
                +#     # ...
                +#   end
                +#
                +# Note that the callback may be called currently by multiple threads.
                +# You should use some form of concurrency control inside the callback,
                +# such as a mutex or queue.
                +#
                +# Below is a brief example of usage to determine the percentage of
                +# connection requests where a connection was immediately available:
                +#
                +#   mutex = Mutex.new
                +#   total = immediates = 0
                +#
                +#   DB.pool.on_checkout_event = proc do |event|
                +#     case event
                +#     when :immediately_available
                +#       mutex.synchronize do
                +#         total += 1
                +#         immediates += 1
                +#       end
                +#     when :not_immediately_available
                +#       mutex.synchronize do
                +#         total += 1
                +#       end
                +#     end
                +#   end
                +#
                +#   immediate_percentage = lambda do
                +#     mutex.synchronize do
                +#       100.0 * immediates / total
                +#     end
                +#   end
                +#   
                +# Note that this extension only works with the timed_queue
                +# and sharded_timed_queue connection pools (the default
                +# connection pools when using Ruby 3.2+).
                +#
                +# Related modules: Sequel::ConnectionCheckoutEventCallbacks::TimedQueue,
                +# Sequel::ConnectionCheckoutEventCallbacks::ShardedTimedQueue
                +
                +#
                +module Sequel
                +  module ConnectionCheckoutEventCallbacks
                +    module TimedQueue
                +      # The callback that is called with connection checkout events.
                +      attr_accessor :on_checkout_event
                +
                +      private
                +
                +      def available
                +        conn = super
                +        @on_checkout_event.call(conn ? :immediately_available : :not_immediately_available)
                +        conn
                +      end
                +
                +      def try_make_new
                +        conn = super
                +        @on_checkout_event.call(:new_connection) if conn
                +        conn
                +      end
                +
                +      def wait_until_available
                +        timer = Sequel.start_timer
                +        conn = super
                +        @on_checkout_event.call(Sequel.elapsed_seconds_since(timer))
                +        conn
                +      end
                +    end
                +
                +    module ShardedTimedQueue
                +      # The callback that is called with connection checkout events.
                +      attr_accessor :on_checkout_event
                +
                +      private
                +
                +      def available(queue, server)
                +        conn = super
                +        @on_checkout_event.call(conn ? :immediately_available : :not_immediately_available, server)
                +        conn
                +      end
                +
                +      def try_make_new(server)
                +        conn = super
                +        @on_checkout_event.call(:new_connection, server) if conn
                +        conn
                +      end
                +
                +      def wait_until_available(queue, server)
                +        timer = Sequel.start_timer
                +        conn = super
                +        @on_checkout_event.call(Sequel.elapsed_seconds_since(timer), server)
                +        conn
                +      end
                +    end
                +  end
                +
                +  default_callback = proc{}
                +
                +  Database.register_extension(:connection_checkout_event_callback) do |db|
                +    pool = db.pool
                +
                +    case pool.pool_type
                +    when :timed_queue
                +      db.pool.extend(ConnectionCheckoutEventCallbacks::TimedQueue)
                +    when :sharded_timed_queue
                +      db.pool.extend(ConnectionCheckoutEventCallbacks::ShardedTimedQueue)
                +    else
                +      raise Error, "the connection_checkout_event_callback extension is only supported when using a timed_queue connection pool"
                +    end
                +
                +    pool.on_checkout_event ||= default_callback
                +  end
                +end
        lib/sequel/extensions/lit_require_frozen.rb
                --- /tmp/20260504-1830-fo2wff	2026-05-04 02:37:12.410768845 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/extensions/lit_require_frozen.rb	2026-05-04 02:37:12.315769432 +0000
                @@ -0,0 +1,131 @@
                +# frozen-string-literal: true
                +#
                +# The lit_require_frozen extension disallows the use of unfrozen strings
                +# as literal strings in database and dataset methods. If you try to use an
                +# unfrozen string as a literal string for a dataset using this extension,
                +# an exception will be raised.
                +#
                +# While this works for all Ruby versions, it is designed for use on Ruby 3+
                +# where all files are using the frozen-string-literal magic comment. In this
                +# case, uninterpolated literal strings are frozen, but interpolated strings
                +# are not frozen. This allows you to catch potentially dangerous code:
                +#
                +#   # Probably safe, no exception raised
                +#   DB["SELECT * FROM t WHERE c > :v", v: user_provided_string)
                +#
                +#   # Potentially unsafe, raises Sequel::LitRequireFrozen::Error
                +#   DB["SELECT * FROM t WHERE c > '#{user_provided_string}'"]
                +#
                +# The assumption made is that a frozen string is unlikely to contain unsafe
                +# input, while an unfrozen string has potentially been interpolated and may
                +# contain unsafe input.
                +#
                +# This disallows the the following cases:
                +#
                +# * Sequel::LiteralString instances that are unfrozen and are not based on a
                +#   frozen string
                +# * Sequel::SQL::PlaceholderLiteralString instances when the placeholder string
                +#   is not frozen
                +# * Unfrozen strings passed to Database#<< or #[] or Dataset#with_sql
                +#
                +# To use this extension, load it into the database:
                +#
                +#   DB.extension :lit_require_frozen
                +#
                +# It can also be loaded into individual datasets:
                +#
                +#  ds = DB[:t].extension(:lit_require_frozen)
                +#
                +# Assuming you have good test coverage, it is recommended to only load
                +# this extension when testing.
                +#
                +# Related module: Sequel::LitRequireFrozen
                +
                +#
                +module Sequel
                +  class LiteralString
                +    # The string used when creating the literal string (first argument to
                +    # Sequel::LiteralString.new). This may be nil if no string was provided,
                +    # or if the litral string was created before this extension was required.
                +    attr_reader :source
                +
                +    def initialize(*a)
                +      @source = a.first
                +      super
                +    end
                +    # :nocov:
                +    ruby2_keywords :initialize if respond_to?(:ruby2_keywords, true)
                +    # :nocov:
                +  end
                +
                +  module LitRequireFrozen
                +    # Error class raised for using unfrozen literal string.
                +    class Error < Sequel::Error
                +    end
                +
                +    module DatabaseMethods
                +      def self.extended(db)
                +        db.extend_datasets(DatasetMethods)
                +      end
                +
                +      # Check given SQL is frozen before running it.
                +      def run(sql, opts=OPTS)
                +        @default_dataset.with_sql(sql)
                +        super
                +      end
                +    end
                +
                +    module DatasetMethods
                +      # Check given SQL is not an unfrozen string.
                +      def with_sql(sql, *args)
                +        _check_unfrozen_literal_string(sql)
                +        super
                +      end
                +
                +      # Check that placeholder string is frozen (or all entries
                +      # in placeholder array are frozen).
                +      def placeholder_literal_string_sql_append(sql, pls)
                +        str = pls.str
                +
                +        if str.is_a?(Array)
                +          str.each do |s|
                +            _check_unfrozen_literal_string(s)
                +          end
                +        else
                +          _check_unfrozen_literal_string(str)
                +        end
                +
                +        super
                +      end
                +
                +      private
                +
                +      # Base method that other methods used to check for whether a string should be allowed
                +      # as literal SQL. Allows non-strings as well as frozen strings.
                +      def _check_unfrozen_literal_string(str)
                +        return if !str.is_a?(String) || str.frozen?
                +
                +        if str.is_a?(LiteralString)
                +          _check_unfrozen_literal_string(str.source)
                +        else
                +          raise Error, "cannot treat unfrozen string as literal SQL: #{str.inspect}"
                +        end
                +      end
                +
                +      # Check literal strings appended to SQL.
                +      def literal_literal_string_append(sql, v)
                +        _check_unfrozen_literal_string(v)
                +        super
                +      end
                +
                +      # Check static SQL is not frozen.
                +      def static_sql(sql)
                +        _check_unfrozen_literal_string(sql)
                +        super
                +      end
                +    end
                +  end
                +
                +  Dataset.register_extension(:lit_require_frozen, LitRequireFrozen::DatasetMethods)
                +  Database.register_extension(:lit_require_frozen, LitRequireFrozen::DatabaseMethods)
                +end
      * Changed:
        MIT-LICENSE
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/MIT-LICENSE	2026-05-04 02:37:12.264769748 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/MIT-LICENSE	2026-05-04 02:37:12.300769525 +0000
                @@ -2 +2 @@
                -Copyright (c) 2008-2023 Jeremy Evans
                +Copyright (c) 2008-2026 Jeremy Evans and contributors
        lib/sequel/adapters/jdbc/derby.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/adapters/jdbc/derby.rb	2026-05-04 02:37:12.266769735 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/adapters/jdbc/derby.rb	2026-05-04 02:37:12.301769519 +0000
                @@ -2,0 +3,2 @@
                +# SEQUEL6: Remove
                +
        lib/sequel/adapters/jdbc/h2.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/adapters/jdbc/h2.rb	2026-05-04 02:37:12.266769735 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/adapters/jdbc/h2.rb	2026-05-04 02:37:12.301769519 +0000
                @@ -21 +21 @@
                -          run("COMMIT TRANSACTION #{transaction_id}", opts)
                +          run("COMMIT TRANSACTION #{transaction_id}".freeze, opts)
                @@ -39 +39 @@
                -          run("ROLLBACK TRANSACTION #{transaction_id}", opts)
                +          run("ROLLBACK TRANSACTION #{transaction_id}".freeze, opts)
        lib/sequel/adapters/postgres.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/adapters/postgres.rb	2026-05-04 02:37:12.269769717 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/adapters/postgres.rb	2026-05-04 02:37:12.303769507 +0000
                @@ -730 +730 @@
                -            LiteralString.new("#{prepared_arg_placeholder}#{i}")
                +            LiteralString.new("#{prepared_arg_placeholder}#{i}".freeze)
        lib/sequel/adapters/shared/mssql.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/adapters/shared/mssql.rb	2026-05-04 02:37:12.269769717 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/adapters/shared/mssql.rb	2026-05-04 02:37:12.304769500 +0000
                @@ -109 +109 @@
                -        sql = "DECLARE #{declarations.join(', ')}; EXECUTE @RC = #{name} #{values.join(', ')}; SELECT #{names.join(', ')}"
                +        sql = "DECLARE #{declarations.join(', ')}; EXECUTE @RC = #{name} #{values.join(', ')}; SELECT #{names.join(', ')}".freeze
                @@ -403 +403 @@
                -        run(ds.into(name).sql)
                +        run(ds.into(name).sql.freeze)
                @@ -881 +881 @@
                -          sql = multi_insert_sql(columns, values)[0]
                +          sql = multi_insert_sql(columns, values)[0].freeze
        lib/sequel/adapters/shared/mysql.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/adapters/shared/mysql.rb	2026-05-04 02:37:12.270769711 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/adapters/shared/mysql.rb	2026-05-04 02:37:12.304769500 +0000
                @@ -43 +43 @@
                -        run("XA COMMIT #{literal(transaction_id)}", opts)
                +        run("XA COMMIT #{literal(transaction_id)}".freeze, opts)
                @@ -106 +106 @@
                -        metadata_dataset.with_sql(sql).each do |r|
                +        metadata_dataset.with_sql(sql.freeze).each do |r|
                @@ -118 +118 @@
                -        run("XA ROLLBACK #{literal(transaction_id)}", opts)
                +        run("XA ROLLBACK #{literal(transaction_id)}".freeze, opts)
                @@ -783 +783,2 @@
                -        ds = db.send(:metadata_dataset).with_sql(((opts[:extended] && (db.mariadb? || db.server_version < 50700)) ? 'EXPLAIN EXTENDED ' : 'EXPLAIN ') + select_sql).naked
                +        sql = ((opts[:extended] && (db.mariadb? || db.server_version < 50700)) ? 'EXPLAIN EXTENDED ' : 'EXPLAIN ') + select_sql
                +        ds = db.send(:metadata_dataset).with_sql(sql.freeze).naked
        lib/sequel/adapters/shared/postgres.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/adapters/shared/postgres.rb	2026-05-04 02:37:12.270769711 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/adapters/shared/postgres.rb	2026-05-04 02:37:12.305769494 +0000
                @@ -342 +342 @@
                -        run("COMMIT PREPARED #{literal(transaction_id)}", opts)
                +        run("COMMIT PREPARED #{literal(transaction_id)}".freeze, opts)
                @@ -419 +419 @@
                -          run("ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(column)} DROP DEFAULT", server_hash)
                +          run("ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(column)} DROP DEFAULT".freeze, server_hash)
                @@ -456 +456 @@
                -        self << create_function_sql(name, definition, opts)
                +        self << create_function_sql(name, definition, opts).freeze
                @@ -467 +467 @@
                -        self << create_language_sql(name, opts)
                +        self << create_language_sql(name, opts).freeze
                @@ -476 +476 @@
                -        self << create_schema_sql(name, opts)
                +        self << create_schema_sql(name, opts).freeze
                @@ -512 +512 @@
                -        self << create_trigger_sql(table, name, function, opts)
                +        self << create_trigger_sql(table, name, function, opts).freeze
                @@ -545 +545 @@
                -        run "DO #{"LANGUAGE #{literal(language.to_s)} " if language}#{literal(code)}"
                +        run "DO #{"LANGUAGE #{literal(language.to_s)} " if language}#{literal(code)}".freeze
                @@ -555 +555 @@
                -        self << drop_function_sql(name, opts)
                +        self << drop_function_sql(name, opts).freeze
                @@ -564 +564 @@
                -        self << drop_language_sql(name, opts)
                +        self << drop_language_sql(name, opts).freeze
                @@ -573 +573 @@
                -        self << drop_schema_sql(name, opts)
                +        self << drop_schema_sql(name, opts).freeze
                @@ -584 +584 @@
                -        self << drop_trigger_sql(table, name, opts)
                +        self << drop_trigger_sql(table, name, opts).freeze
                @@ -748 +748 @@
                -        self << rename_schema_sql(name, new_name)
                +        self << rename_schema_sql(name, new_name).freeze
                @@ -759 +759 @@
                -        run "REFRESH MATERIALIZED VIEW#{' CONCURRENTLY' if opts[:concurrently]} #{quote_schema_table(name)}"
                +        run "REFRESH MATERIALIZED VIEW#{' CONCURRENTLY' if opts[:concurrently]} #{quote_schema_table(name)}".freeze
                @@ -772 +772 @@
                -          seq_ds = metadata_dataset.from(:pg_sequence).where(:seqrelid=>regclass_oid(LiteralString.new(seq)))
                +          seq_ds = metadata_dataset.from(:pg_sequence).where(:seqrelid=>regclass_oid(LiteralString.new(seq.freeze)))
                @@ -787 +787 @@
                -        run("ROLLBACK PREPARED #{literal(transaction_id)}", opts)
                +        run("ROLLBACK PREPARED #{literal(transaction_id)}".freeze, opts)
                @@ -1472 +1472 @@
                -        cols[-1] = Sequel.lit("PERIOD #{quote_identifier(cols[-1])}")
                +        cols[-1] = Sequel.lit("PERIOD #{quote_identifier(cols[-1])}".freeze)
        lib/sequel/adapters/shared/sqlite.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/adapters/shared/sqlite.rb	2026-05-04 02:37:12.271769704 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/adapters/shared/sqlite.rb	2026-05-04 02:37:12.305769494 +0000
                @@ -198 +198 @@
                -        metadata_dataset.with_sql("PRAGMA table_#{'x' if sqlite_version > 33100}info(?)", input_identifier_meth(opts[:dataset]).call(table_name))
                +        metadata_dataset.with_sql("PRAGMA table_#{'x' if sqlite_version > 33100}info(?)".freeze, input_identifier_meth(opts[:dataset]).call(table_name))
                @@ -410 +410 @@
                -          c[:default] = LiteralString.new(c[:default]) if c[:default]
                +          c[:default] = LiteralString.new(c[:default]).freeze if c[:default]
                @@ -687 +687 @@
                -        ds = db.send(:metadata_dataset).clone(:sql=>"EXPLAIN #{select_sql}")
                +        ds = db.send(:metadata_dataset).clone(:sql=>"EXPLAIN #{select_sql}".freeze)
        lib/sequel/adapters/sqlite.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/adapters/sqlite.rb	2026-05-04 02:37:12.271769704 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/adapters/sqlite.rb	2026-05-04 02:37:12.306769488 +0000
                @@ -391 +391 @@
                -          LiteralString.new("#{prepared_arg_placeholder}#{k.to_s.gsub('.', '__')}")
                +          LiteralString.new("#{prepared_arg_placeholder}#{k.to_s.gsub('.', '__')}".freeze)
        lib/sequel/adapters/tinytds.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/adapters/tinytds.rb	2026-05-04 02:37:12.271769704 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/adapters/tinytds.rb	2026-05-04 02:37:12.306769488 +0000
                @@ -208 +208 @@
                -          LiteralString.new("@#{k.to_s.gsub('.', '__')}")
                +          LiteralString.new("@#{k.to_s.gsub('.', '__')}".freeze)
        lib/sequel/connection_pool.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/connection_pool.rb	2026-05-04 02:37:12.272769698 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/connection_pool.rb	2026-05-04 02:37:12.307769482 +0000
                @@ -133,0 +134,6 @@
                +  # Only for use by extension that need to disconnect a connection inside acquire.
                +  # Takes the connection and any arguments accepted by acquire.
                +  def disconnect_acquired_connection(conn, *)
                +    disconnect_connection(conn)
                +  end
                +
        lib/sequel/connection_pool/sharded_timed_queue.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/connection_pool/sharded_timed_queue.rb	2026-05-04 02:37:12.273769692 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/connection_pool/sharded_timed_queue.rb	2026-05-04 02:37:12.307769482 +0000
                @@ -74 +74 @@
                -          break unless (conn = queue.pop(timeout: 0)) && !conns[conn]
                +          break unless (conn = available(queue, server)) && !conns[conn]
                @@ -94 +94 @@
                -  # and then the method will only disconnect connectsion from those specified shards.
                +  # and then the method will only disconnect connections from those specified shards.
                @@ -98 +98,3 @@
                -      while conn = queue.pop(timeout: 0)
                +      nconns = 0
                +      while conn = available(queue, server)
                +        nconns += 1
                @@ -101 +103 @@
                -      fill_queue(server)
                +      fill_queue(server, nconns)
                @@ -135 +137 @@
                -        fill_queue(server)
                +        fill_queue(server, 1)
                @@ -170 +172 @@
                -        while conn = queue.pop(timeout: 0)
                +        while conn = available(queue, server)
                @@ -227,0 +230,6 @@
                +  # Only for use by extension that need to disconnect a connection inside acquire.
                +  # Takes the connection and any arguments accepted by acquire.
                +  def disconnect_acquired_connection(conn, _, server)
                +    disconnect_pool_connection(conn, server)
                +  end
                +
                @@ -247 +255,4 @@
                -  def fill_queue(server)
                +  #
                +  # nconns specifies the maximum number of connections to add, which should
                +  # be the number of connections that were disconnected.
                +  def fill_queue(server, nconns)
                @@ -249 +260 @@
                -    if queue.num_waiting > 0
                +    if nconns > 0 && queue.num_waiting > 0
                @@ -251 +262,2 @@
                -        while queue.num_waiting > 0 && (conn = try_make_new(server))
                +        while nconns > 0 && queue.num_waiting > 0 && (conn = try_make_new(server))
                +          nconns -= 1
                @@ -300 +312 @@
                -        fill_queue(server)
                +        fill_queue(server, to_disconnect.size)
                @@ -314 +326 @@
                -    if conn = queue.pop(timeout: 0) || try_make_new(server) || queue.pop(timeout: @timeout)
                +    if conn = available(queue, server) || try_make_new(server) || wait_until_available(queue, server)
                @@ -319,0 +332,13 @@
                +  end
                +
                +  # Return the next connection in the pool if there is one available. Returns nil
                +  # if no connection is currently available.
                +  def available(queue, _server)
                +    queue.pop(timeout: 0)
                +  end
                +
                +  # Return the next connection in the pool if there is one available. If not, wait
                +  # until the timeout for a connection to become available. If there is still no
                +  # available connection, return nil.
                +  def wait_until_available(queue, _server)
                +    queue.pop(timeout: @timeout)
        lib/sequel/connection_pool/timed_queue.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/connection_pool/timed_queue.rb	2026-05-04 02:37:12.273769692 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/connection_pool/timed_queue.rb	2026-05-04 02:37:12.307769482 +0000
                @@ -45 +45 @@
                -          break unless (conn = @queue.pop(timeout: 0)) && !conns[conn]
                +          break unless (conn = available) && !conns[conn]
                @@ -62 +62,3 @@
                -    while conn = @queue.pop(timeout: 0)
                +    nconns = 0
                +    while conn = available
                +      nconns += 1
                @@ -65 +67 @@
                -    fill_queue
                +    fill_queue(nconns)
                @@ -97 +99 @@
                -        fill_queue
                +        fill_queue(1)
                @@ -159,2 +161,5 @@
                -  def fill_queue
                -    if @queue.num_waiting > 0
                +  #
                +  # nconns specifies the maximum number of connections to add, which should
                +  # be the number of connections that were disconnected.
                +  def fill_queue(nconns)
                +    if nconns > 0 && @queue.num_waiting > 0
                @@ -162 +167 @@
                -        while @queue.num_waiting > 0 && (conn = try_make_new)
                +        while nconns > 0 && @queue.num_waiting > 0 && (conn = try_make_new)
                @@ -210 +215 @@
                -        fill_queue
                +        fill_queue(to_disconnect.size)
                @@ -223 +228 @@
                -    if conn = @queue.pop(timeout: 0) || try_make_new || @queue.pop(timeout: @timeout)
                +    if conn = available || try_make_new || wait_until_available
                @@ -228,0 +234,13 @@
                +  end
                +
                +  # Return the next connection in the pool if there is one available. Returns nil
                +  # if no connection is currently available.
                +  def available
                +    @queue.pop(timeout: 0)
                +  end
                +
                +  # Return the next connection in the pool if there is one available. If not, wait
                +  # until the timeout for a connection to become available. If there is still no
                +  # available connection, return nil.
                +  def wait_until_available
                +    @queue.pop(timeout: @timeout)
        lib/sequel/database/schema_generator.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/database/schema_generator.rb	2026-05-04 02:37:12.274769686 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/database/schema_generator.rb	2026-05-04 02:37:12.309769470 +0000
                @@ -5,0 +6,27 @@
                +    module ColumnOptionMerger
                +      private
                +
                +      # Merge given options into the column's default options. For backwards compatibility,
                +      # the options take priority, but in cases where the option value overrides the argument
                +      # value, and the values are different, we warn as this is likely to be an error in the
                +      # code.
                +      def _merge_column_options(defaults, opts)
                +        defaults.merge!(opts) do |k, defv, v|
                +          unless defv == v
                +            # :nocov:
                +            if RUBY_VERSION >= "3.2"
                +            # :nocov:
                +              caller_loc = Thread.each_caller_location do |loc|
                +                break loc unless loc.path == __FILE__
                +              end
                +              caller_loc &&= "#{caller_loc.path}:#{caller_loc.lineno}: "
                +            end
                +            warn("#{caller_loc}#{k.inspect} option value (#{v.inspect}) overrides argument value (#{defv.inspect})")
                +          end
                +
                +          v
                +        end
                +      end
                +    end
                +    private_constant :ColumnOptionMerger
                +
                @@ -19,0 +47,2 @@
                +      include ColumnOptionMerger
                +
                @@ -176 +205 @@
                -        columns << {:name => name, :type => type}.merge!(opts)
                +        columns << _merge_column_options({:name => name, :type => type}, opts)
                @@ -182 +211 @@
                -      
                +
                @@ -249 +278 @@
                -        column(name, Integer, opts)
                +        column(name, opts.fetch(:type, Integer), opts)
                @@ -431,0 +461,2 @@
                +      include ColumnOptionMerger
                +
                @@ -457 +488 @@
                -        op = {:op => :add_column, :name => name, :type => type}.merge!(opts)
                +        op = _merge_column_options({:op => :add_column, :name => name, :type => type}, opts)
                @@ -522 +553 @@
                -        add_column(name, Integer, {:table=>table}.merge!(opts))
                +        add_column(name, opts.fetch(:type, Integer), {:table=>table}.merge!(opts))
        lib/sequel/database/schema_methods.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/database/schema_methods.rb	2026-05-04 02:37:12.274769686 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/database/schema_methods.rb	2026-05-04 02:37:12.309769470 +0000
                @@ -822 +822 @@
                -      run(create_table_as_sql(name, sql, options))
                +      run(create_table_as_sql(name, sql, options).freeze)
        lib/sequel/dataset/placeholder_literalizer.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/dataset/placeholder_literalizer.rb	2026-05-04 02:37:12.276769673 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/dataset/placeholder_literalizer.rb	2026-05-04 02:37:12.310769463 +0000
                @@ -119,0 +120,2 @@
                +          frags.each(&:freeze)
                +          frags.freeze
                @@ -127,0 +130 @@
                +          ds.opts[:sql].freeze
        lib/sequel/dataset/prepared_statements.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/dataset/prepared_statements.rb	2026-05-04 02:37:12.276769673 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/dataset/prepared_statements.rb	2026-05-04 02:37:12.311769457 +0000
                @@ -57 +57 @@
                -        sql = prepared_sql
                +        sql = prepared_sql.freeze
                @@ -226 +226 @@
                -          with_sql(prepared_sql)
                +          with_sql(prepared_sql.freeze)
                @@ -290 +290 @@
                -          sql = literal(Sequel::SQL::PlaceholderLiteralString.new(@opts[:prepared_sql_frags], @opts[:bind_arguments], false))
                +          sql = literal(Sequel::SQL::PlaceholderLiteralString.new(@opts[:prepared_sql_frags], @opts[:bind_arguments], false)).freeze
                @@ -322,0 +323,3 @@
                +        frags.freeze
                +        frags.each(&:freeze)
                +        prepared_sql.freeze
                @@ -412 +415 @@
                -        sql = ps.prepared_sql
                +        sql = ps.prepared_sql.freeze
        lib/sequel/dataset/query.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/dataset/query.rb	2026-05-04 02:37:12.276769673 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/dataset/query.rb	2026-05-04 02:37:12.311769457 +0000
                @@ -1304 +1304 @@
                -        sql = public_send(sql, *args)
                +        sql = public_send(sql, *args).freeze
                @@ -1477 +1477,4 @@
                -    
                +
                +    PAREN_WRAPPER = ["(".freeze, ")".freeze].freeze
                +    private_constant :PAREN_WRAPPER
                +
                @@ -1498 +1501 @@
                -        LiteralString.new("(#{expr})")
                +        SQL::PlaceholderLiteralString.new(PAREN_WRAPPER, [expr])
        lib/sequel/dataset/sql.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/dataset/sql.rb	2026-05-04 02:37:12.277769667 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/dataset/sql.rb	2026-05-04 02:37:12.311769457 +0000
                @@ -56 +56 @@
                -          sql << v
                +          literal_literal_string_append(sql, v)
                @@ -1424,0 +1425,5 @@
                +    end
                +
                +    # Append string to SQL string.
                +    def literal_literal_string_append(sql, v)
                +      sql << v
        lib/sequel/extensions/connection_expiration.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/extensions/connection_expiration.rb	2026-05-04 02:37:12.278769661 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/extensions/connection_expiration.rb	2026-05-04 02:37:12.313769445 +0000
                @@ -94 +94 @@
                -          disconnect_connection(conn)
                +          disconnect_acquired_connection(conn, *a)
        lib/sequel/extensions/connection_validator.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/extensions/connection_validator.rb	2026-05-04 02:37:12.278769661 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/extensions/connection_validator.rb	2026-05-04 02:37:12.313769445 +0000
                @@ -121 +121 @@
                -              disconnect_connection(conn)
                +              disconnect_acquired_connection(conn, *a)
        lib/sequel/extensions/dataset_run.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/extensions/dataset_run.rb	2026-05-04 02:37:12.278769661 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/extensions/dataset_run.rb	2026-05-04 02:37:12.314769439 +0000
                @@ -33 +33 @@
                -        db.run(sql, :server=>server)
                +        db.run(sql.freeze, :server=>server)
                @@ -35 +35 @@
                -        db.run(sql)
                +        db.run(sql.freeze)
        lib/sequel/extensions/date_arithmetic.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/extensions/date_arithmetic.rb	2026-05-04 02:37:12.279769655 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/extensions/date_arithmetic.rb	2026-05-04 02:37:12.314769439 +0000
                @@ -85,2 +85,2 @@
                -        MYSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s.upcase[0...-1]).freeze}).freeze
                -        MSSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s[0...-1]).freeze}).freeze
                +        MYSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s.upcase[0...-1].freeze).freeze}).freeze
                +        MSSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s[0...-1].freeze).freeze}).freeze
                @@ -88 +88 @@
                -        DERBY_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit("SQL_TSI_#{s.to_s.upcase[0...-1]}").freeze}).freeze
                +        DERBY_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit("SQL_TSI_#{s.to_s.upcase[0...-1]}".freeze).freeze}).freeze
                @@ -90 +90 @@
                -        DB2_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s).freeze}).freeze
                +        DB2_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s.freeze).freeze}).freeze
                @@ -110 +110 @@
                -                placeholder << "#{', ' unless placeholder.empty?}#{sql_unit} := "
                +                placeholder << "#{', ' unless placeholder.empty?}#{sql_unit} := ".freeze
                @@ -159 +159 @@
                -              expr = Sequel.lit(["{fn timestampadd(#{sql_unit}, ", ", timestamp(", "))}"], value, expr)
                +              expr = Sequel.lit(["{fn timestampadd(#{sql_unit}, ".freeze, ", timestamp(", "))}"], value, expr)
        lib/sequel/extensions/migration.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/extensions/migration.rb	2026-05-04 02:37:12.280769649 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/extensions/migration.rb	2026-05-04 02:37:12.316769426 +0000
                @@ -189,16 +189,13 @@
                -      begin
                -        instance_exec(&block)
                -      rescue
                -        just_raise = true
                -      end
                -      if just_raise
                -        Proc.new{raise Sequel::Error, "irreversible migration method used in #{block.source_location.first}, you may need to write your own down method"}
                -      else
                -        actions = @actions.reverse
                -        Proc.new do
                -          actions.each do |a|
                -            pr = a.last.is_a?(Proc) ? a.pop : nil
                -            # Allow calling private methods as the reversing methods are private
                -            send(*a, &pr)
                -          end
                -        end
                +      instance_exec(&block)
                +    rescue NoMethodError => e
                +      Proc.new{raise Sequel::Error, "irreversible migration method \"#{e.name}\" used in #{block.source_location.first}, you may need to write your own down method"}
                +    rescue => e
                +      Proc.new{raise Sequel::Error, "unable to reverse migration due to #{e.class} in #{block.source_location.first}, you may need to write your own down method"}
                +    else
                +      actions = @actions.reverse
                +      Proc.new do
                +       actions.each do |a|
                +        pr = a.last.is_a?(Proc) ? a.pop : nil
                +        # Allow calling private methods as the reversing methods are private
                +        send(*a, &pr)
                +       end
                @@ -273 +270 @@
                -      raise if args.first.is_a?(Array)
                +      super if args.first.is_a?(Array)
        lib/sequel/extensions/pg_enum.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/extensions/pg_enum.rb	2026-05-04 02:37:12.282769636 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/extensions/pg_enum.rb	2026-05-04 02:37:12.317769420 +0000
                @@ -130 +130 @@
                -        run(sql)
                +        run(sql.freeze)
        lib/sequel/model/associations.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/model/associations.rb	2026-05-04 02:37:12.287769605 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/model/associations.rb	2026-05-04 02:37:12.323769383 +0000
                @@ -114,0 +115,2 @@
                +          when :lateral_subquery
                +            apply_lateral_subquery_eager_graph_limit_strategy(ds)
                @@ -301 +303,6 @@
                -            objects = apply_eager_limit_strategy(ds, strategy, eager_limit).all
                +
                +            objects = if strategy == :lateral_subquery
                +              apply_lateral_subquery_eager_limit_strategy(ds, ids, eager_limit).all
                +            else
                +              apply_eager_limit_strategy(ds, strategy, eager_limit).all
                +            end
                @@ -323 +330 @@
                -              objects.concat(ds.with_sql(sql).to_a)
                +              objects.concat(ds.with_sql(sql.freeze).to_a)
                @@ -369 +376,6 @@
                -          ds = filter_by_associations_conditions_dataset.where(filter_by_associations_conditions_subquery_conditions(obj))
                +          ds = if filter_by_associations_limit_strategy == :lateral_subquery
                +            apply_lateral_subquery_filter_limit_strategy(associated_eager_dataset, obj)
                +          else
                +            filter_by_associations_conditions_dataset.where(filter_by_associations_conditions_subquery_conditions(obj))
                +          end
                +
                @@ -756,0 +769,55 @@
                +        def lateral_subquery_eager_limit_strategy_lateral_dataset(ds, limit_and_offset)
                +          ds.
                +            where(Array(filter_by_associations_conditions_key).zip(Array(filter_by_associations_conditions_associated_keys))).
                +            limit(*limit_and_offset).
                +            lateral
                +        end
                +
                +        def apply_lateral_subquery_eager_limit_strategy(ds, ids, limit_and_offset)
                +          table_name = self[:model].table_name
                +          associated_table_name = associated_class.table_name
                +
                +          associated_class.
                +            from(table_name).
                +            select_all(associated_table_name).
                +            join(lateral_subquery_eager_limit_strategy_lateral_dataset(ds, limit_and_offset).as(associated_table_name), true).
                +            order(*self[:order])
                +        end
                +
                +        # Return an expression to filter the filter by associations dataset to only
                +        # rows related to given objects.
                +        def _lateral_subquery_filter_limit_strategy_conditions(obj, key, value_method, value_column)
                +          value = case obj
                +          when Array
                +            if key.is_a?(Array)
                +              key_methods = Array(value_method)
                +              obj.map{|o| key_methods.map{|meth| o.send(meth)}}
                +            else
                +              obj.map{|o| o.send(value_method)}
                +            end
                +          when Sequel::Dataset
                +            obj.select(*Array(qualify(associated_class.table_name, value_column)))
                +          else
                +            if key.is_a?(Array)
                +              return Array(key).zip(Array(value_method).map{|meth| obj.send(meth)})
                +            else
                +              obj.send(value_method)
                +            end
                +          end
                +
                +          {key => value}
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_lateral_dataset(ds, obj)
                +          lateral_subquery_filter_limit_strategy_filter_lateral_dataset(ds.
                +            select(*qualify(associated_class.table_name, associated_class.primary_key)).
                +            limit(*limit_and_offset).
                +            lateral)
                +        end
                +
                +        def apply_lateral_subquery_filter_limit_strategy(ds, obj)
                +          lateral_subquery_filter_limit_strategy_filter_dataset(self[:model].
                +            select(*lateral_subquery_filter_limit_strategy_lateral_dataset_select).
                +            join(lateral_subquery_filter_limit_strategy_lateral_dataset(ds, obj).as(associated_class.table_name), filter_by_associations_conditions_subquery_conditions(obj)), obj)
                +        end
                +
                @@ -779 +846,6 @@
                -              apply_eager_limit_strategy(ds.where(predicate_key=>arg), eager_limit_strategy)
                +              strategy = eager_limit_strategy
                +              if strategy == :lateral_subquery
                +                apply_lateral_subquery_eager_limit_strategy(ds, arg, limit_and_offset)
                +              else
                +                apply_eager_limit_strategy(ds.where(predicate_key=>arg), strategy)
                +              end
                @@ -1021 +1093 @@
                -        # Support a correlated subquery limit strategy when using eager_graph.
                +        # Support a lateral_subquery and correlated_subquery limit strategy when using eager_graph.
                @@ -1120,0 +1193,48 @@
                +        # Use a LATERAL subquery to limit the dataset.  Note that this will not
                +        # work correctly if the associated dataset uses qualified identifers in the WHERE clause,
                +        # as they would reference the containing query instead of the subquery.
                +        #
                +        # This does not contain the conditions that are necessary to join to the
                +        # query, since the necessary qualifier is not passed as an argument.
                +        def apply_lateral_subquery_eager_graph_limit_strategy(ds)
                +          table_name = ds.first_source_alias
                +          qualifier = ds.opts[:eager_options][:implicit_qualifier]
                +          graph_conditions = self[:_graph_conditions]
                +
                +          unless Sequel.condition_specifier?(graph_conditions)
                +            raise Error, "lateral_subquery eager graph limit strategy only supported when graph conditions are a hash or array of pairs"
                +          end
                +
                +          ds.
                +            limit(*limit_and_offset).
                +            order(*self[:order]).
                +            where(graph_conditions.map{|k, v| [qualify(table_name, k), qualify(qualifier, v)]}).
                +            lateral
                +        end
                +
                +        # Avoid setting duplicate predicate condition when using the lateral subquery
                +        # eager limit strategy.
                +        def eager_loading_set_predicate_condition(ds, eo)
                +          eager_limit_strategy == :lateral_subquery ? ds : super
                +        end
                +
                +        def apply_lateral_subquery_eager_limit_strategy(ds, ids, limit_and_offset)
                +          super.where(qualify(self[:model].table_name, self[:primary_key]) => ids)
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_conditions(obj)
                +          _lateral_subquery_filter_limit_strategy_conditions(obj, filter_by_associations_conditions_key, self[:key_method], self[:key])
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_filter_lateral_dataset(ds)
                +          ds.where(Array(filter_by_associations_conditions_key).zip(Array(filter_by_associations_conditions_associated_keys)))
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_filter_dataset(ds, obj)
                +          ds.where(lateral_subquery_filter_limit_strategy_conditions(obj))
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_lateral_dataset_select
                +          qualified_primary_key
                +        end
                +
                @@ -1434,0 +1555,46 @@
                +        def lateral_subquery_filter_limit_strategy_conditions(obj)
                +          _lateral_subquery_filter_limit_strategy_conditions(obj, lateral_subquery_filter_limit_strategy_conditions_key, right_primary_key_method, right_primary_key)
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_conditions_key
                +          self[:right_key]
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_filter_lateral_dataset(ds)
                +          ds.where(Array(filter_by_associations_conditions_key).zip(Array(filter_by_associations_conditions_associated_keys)))
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_filter_dataset(ds, obj)
                +          ds.where(filter_by_associations_conditions_key => ds.db.from(self[:join_table]).
                +            select(*self[:left_key]).
                +            where(lateral_subquery_filter_limit_strategy_conditions(obj)))
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_lateral_dataset_select
                +          qualify(self[:model].table_name, self[:left_primary_key])
                +        end
                +
                +        def lateral_subquery_eager_limit_strategy_lateral_dataset(ds, limit_and_offset)
                +          ds = super
                +          select = ds.opts[:select].dup
                +          left_key_alias = self[:left_key_alias]
                +          select.pop while (s = select.last).is_a?(Sequel::SQL::AliasedExpression) && (left_key_alias.is_a?(Array) ? left_key_alias.include?(s.alias) : s.alias == left_key_alias)
                +          ds = ds.clone(:select=>select)
                +        end
                +
                +        def apply_lateral_subquery_eager_limit_strategy(ds, ids, limit_and_offset)
                +          table_name = self[:model].table_name
                +          super.
                +            select_all(associated_class.table_name).
                +            select_append(*qualify(table_name, self[:left_primary_keys]).zip(Array(self[:left_key_alias])).map{|id, aliaz| Sequel.as(id, aliaz)}).
                +            where(qualify(table_name, self[:left_primary_key]) => ids)
                +        end
                +
                +        def apply_lateral_subquery_eager_graph_limit_strategy(ds)
                +          ds.
                +            limit(*limit_and_offset).
                +            where(qualify(join_table_alias, self[:left_keys]).zip(qualify(ds.opts[:eager_options][:implicit_qualifier], self[:left_primary_key_columns]))).
                +            order(*self[:order]).
                +            lateral
                +        end
                +
                @@ -2415,0 +2582 @@
                +          graph_conditions = opts[:_graph_conditions] = use_only_conditions ? only_conditions : cks.zip(pkcs) + conditions
                @@ -2418 +2585,5 @@
                -            ds = ds.graph(opts.apply_eager_graph_limit_strategy(eo[:limit_strategy], eager_graph_dataset(opts, eo)), use_only_conditions ? only_conditions : cks.zip(pkcs) + conditions, eo.merge(:select=>select, :join_type=>eo[:join_type]||join_type, :qualify=>:deep), &graph_block)
                +            graph_limit_strategy = eo[:limit_strategy]
                +            egds = opts.apply_eager_graph_limit_strategy(graph_limit_strategy, eager_graph_dataset(opts, eo))
                +            graph_conditions_true = true if graph_limit_strategy == :lateral_subquery
                +
                +            ds = ds.graph(egds, graph_conditions_true || graph_conditions, eo.merge(:select=>select, :join_type=>eo[:join_type]||join_type, :qualify=>:deep), &graph_block)
                @@ -2500,0 +2672,3 @@
                +          if eager_options[:limit_strategy] == :lateral_subquery
                +            ds = ds.clone(:eager_options=>eager_options)
                +          end
        lib/sequel/plugins/dataset_associations.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/plugins/dataset_associations.rb	2026-05-04 02:37:12.291769581 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/plugins/dataset_associations.rb	2026-05-04 02:37:12.327769358 +0000
                @@ -85 +85 @@
                -          ds = r.associated_class.dataset
                +          klass = r.associated_class
                @@ -86,0 +87,19 @@
                +
                +          if r.send(:filter_by_associations_limit_strategy) == :lateral_subquery
                +            ds = r.send(:associated_eager_dataset)
                +
                +            case r[:type]
                +            when :one_to_one, :one_to_many
                +              sds = sds.select(*Array(r.qualified_primary_key))
                +            else
                +              sds = sds.select(*r[:left_primary_keys])
                +              ds = ds.select_all(klass.table_name)
                +              update_select = true
                +            end
                +
                +            ds = r.send(:apply_lateral_subquery_eager_limit_strategy, ds, sds, r.limit_and_offset)
                +            ds = ds.clone(:select=>ds.opts[:select][0,1]) if update_select
                +            return ds.clone(:eager=>nil, :eager_graph=>nil)
                +          end
                +
                +          ds = klass.dataset
        lib/sequel/plugins/dirty.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/plugins/dirty.rb	2026-05-04 02:37:12.292769574 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/plugins/dirty.rb	2026-05-04 02:37:12.328769352 +0000
                @@ -237,2 +237,5 @@
                -            check_missing_initial_value(column)
                -            iv[column] = get_column_value(column)
                +            if db_schema[column]
                +              check_missing_initial_value(column)
                +              iv[column] = get_column_value(column)
                +            end
                +
        lib/sequel/plugins/many_through_many.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/plugins/many_through_many.rb	2026-05-04 02:37:12.293769568 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/plugins/many_through_many.rb	2026-05-04 02:37:12.330769340 +0000
                @@ -148,0 +149,21 @@
                +        def lateral_subquery_filter_limit_strategy_conditions_key
                +          qualify(reverse_edges.first[:table], reverse_edges.first[:left])
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_filter_lateral_dataset(ds)
                +          ds.where(Array(qualify(edges.first[:table], edges.first[:right])).zip(Array(qualify(self[:model].table_name, edges.first[:left]))))
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_filter_dataset(ds, obj)
                +          first_edge, *remaining_edges = edges
                +          filter_ds = ds.db.from(first_edge[:table]).
                +            select(*qualify(first_edge[:table], first_edge[:right])).
                +            where(lateral_subquery_filter_limit_strategy_conditions(obj))
                +
                +          remaining_edges.each do |edge|
                +            filter_ds = filter_ds.join(edge[:table], Array(edge[:right]).zip(Array(edge[:left])))
                +          end
                +
                +          ds.where(qualify(self[:model].table_name, self[:left_primary_key]) => filter_ds)
                +        end
                +
        lib/sequel/plugins/mssql_optimistic_locking.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/plugins/mssql_optimistic_locking.rb	2026-05-04 02:37:12.294769562 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/plugins/mssql_optimistic_locking.rb	2026-05-04 02:37:12.330769340 +0000
                @@ -58 +58 @@
                -          rows = ds.clone(ds.send(:default_server_opts, :sql=>ds.output(nil, [Sequel[:inserted][lc]]).update_sql(columns))).all
                +          rows = ds.clone(ds.send(:default_server_opts, :sql=>ds.output(nil, [Sequel[:inserted][lc]]).update_sql(columns).freeze)).all
        lib/sequel/plugins/pg_xmin_optimistic_locking.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/plugins/pg_xmin_optimistic_locking.rb	2026-05-04 02:37:12.295769556 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/plugins/pg_xmin_optimistic_locking.rb	2026-05-04 02:37:12.331769333 +0000
                @@ -102 +102 @@
                -          rows = ds.clone(ds.send(:default_server_opts, :sql=>ds.returning(:xmin).update_sql(columns))).all
                +          rows = ds.clone(ds.send(:default_server_opts, :sql=>ds.returning(:xmin).update_sql(columns).freeze)).all
        lib/sequel/plugins/serialization.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/plugins/serialization.rb	2026-05-04 02:37:12.295769556 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/plugins/serialization.rb	2026-05-04 02:37:12.332769327 +0000
                @@ -32,0 +33,9 @@
                +    # Note that use of an unsafe serialization method can result in an attack vector
                +    # (potentially allowing remote code execution) if an attacker has the ability to
                +    # store data directly in the underlying column. This would affect the marshal
                +    # serialization format, and on older versions of Ruby, potentially the yaml and
                +    # json serialization formats as well. It can also affect custom formats. You
                +    # should ensure that attackers do not have access to store data directly in the
                +    # underlying column when using this plugin (especially when using an unsafe
                +    # serialization method).
                +    #
                @@ -100 +109 @@
                -          v = v.unpack('m')[0] unless v[0..1] == "\x04\x08"
                +          v = v.unpack('m')[0] unless v.start_with?("\x04\x08")
        lib/sequel/version.rb
                --- /tmp/d20260504-1830-kczwjk/sequel-5.101.0/lib/sequel/version.rb	2026-05-04 02:37:12.299769531 +0000
                +++ /tmp/d20260504-1830-kczwjk/sequel-5.104.0/lib/sequel/version.rb	2026-05-04 02:37:12.336769302 +0000
                @@ -9 +9 @@
                -  MINOR = 101
                +  MINOR = 104

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

gem compare --diff bigdecimal 4.0.1 4.1.2

Diff too large (211479 chars)

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

gem compare sequel 5.101.0 5.104.0

Compared versions: ["5.101.0", "5.104.0"]
  DIFFERENT version:
    5.101.0: 5.101.0
    5.104.0: 5.104.0
  DIFFERENT files:
    5.101.0->5.104.0:
      * Added:
            lib/sequel/extensions/connection_checkout_event_callback.rb +151/-0
            lib/sequel/extensions/lit_require_frozen.rb +131/-0
      * Changed:
            MIT-LICENSE +1/-1
            lib/sequel/adapters/jdbc/derby.rb +2/-0
            lib/sequel/adapters/jdbc/h2.rb +2/-2
            lib/sequel/adapters/postgres.rb +1/-1
            lib/sequel/adapters/shared/mssql.rb +3/-3
            lib/sequel/adapters/shared/mysql.rb +5/-4
            lib/sequel/adapters/shared/postgres.rb +16/-16
            lib/sequel/adapters/shared/sqlite.rb +3/-3
            lib/sequel/adapters/sqlite.rb +1/-1
            lib/sequel/adapters/tinytds.rb +1/-1
            lib/sequel/connection_pool.rb +6/-0
            lib/sequel/connection_pool/sharded_timed_queue.rb +36/-11
            lib/sequel/connection_pool/timed_queue.rb +27/-9
            lib/sequel/database/schema_generator.rb +36/-5
            lib/sequel/database/schema_methods.rb +1/-1
            lib/sequel/dataset/placeholder_literalizer.rb +3/-0
            lib/sequel/dataset/prepared_statements.rb +7/-4
            lib/sequel/dataset/query.rb +6/-3
            lib/sequel/dataset/sql.rb +6/-1
            lib/sequel/extensions/connection_expiration.rb +1/-1
            lib/sequel/extensions/connection_validator.rb +1/-1
            lib/sequel/extensions/dataset_run.rb +2/-2
            lib/sequel/extensions/date_arithmetic.rb +6/-6
            lib/sequel/extensions/migration.rb +14/-17
            lib/sequel/extensions/pg_enum.rb +1/-1
            lib/sequel/model/associations.rb +180/-6
            lib/sequel/plugins/dataset_associations.rb +20/-1
            lib/sequel/plugins/dirty.rb +5/-2
            lib/sequel/plugins/many_through_many.rb +21/-0
            lib/sequel/plugins/mssql_optimistic_locking.rb +1/-1
            lib/sequel/plugins/pg_xmin_optimistic_locking.rb +1/-1
            lib/sequel/plugins/serialization.rb +10/-1
            lib/sequel/version.rb +1/-1
  DIFFERENT extra_rdoc_files:
    5.101.0->5.104.0:
      * Changed:
            MIT-LICENSE +1/-1

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 4, 2026

gem compare --diff sequel 5.101.0 5.104.0

Compared versions: ["5.101.0", "5.104.0"]
  DIFFERENT files:
    5.101.0->5.104.0:
      * Added:
        lib/sequel/extensions/connection_checkout_event_callback.rb
                --- /tmp/20260504-1888-klbdc	2026-05-04 02:37:42.161776864 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/extensions/connection_checkout_event_callback.rb	2026-05-04 02:37:42.131777110 +0000
                @@ -0,0 +1,151 @@
                +# frozen-string-literal: true
                +#
                +# The connection_checkout_event_callback extension modifies a database's
                +# connection pool to allow for a checkout event callback. This callback is
                +# called with the following arguments:
                +#
                +# :immediately_available :: Connection immediately available and returned
                +# :not_immediately_available :: Connection not immediately available
                +# :new_connection :: New connection created and returned
                +# Float :: Number of seconds waiting to acquire a connection
                +#
                +# This is a low-level extension that allows for building telemetry
                +# information. It doesn't implement any telemetry reporting itself. The
                +# main reason for recording this information is to use it to determine the
                +# appropriate size for the connection pool. Having too large a connection
                +# pool can waste resources, while having too small a connection pool can
                +# result in substantial time to check out a connection. In general, you
                +# want to use as small a pool as possible while keeping the time to
                +# checkout a connection low.
                +#
                +# To use the connection checkout event callback, you must first load the
                +# extension:
                +#
                +#   DB.extension(:connection_checkout_event_callback)
                +#
                +# By default, an empty proc is used as the callback so that loading the
                +# support doesn't break anything. If you are using the extension, you
                +# should set the callback at some point during application startup:
                +#
                +#   DB.pool.on_checkout_event = proc do |event|
                +#     # ...
                +#   end
                +#
                +# When using the sharded connection pool, the callback is also
                +# passed a second argument, the requested server shard (generally a
                +# symbol), allowing for collection of per-shard telemetry:
                +#
                +#   DB.pool.on_checkout_event = proc do |event, server|
                +#     # ...
                +#   end
                +#
                +# Note that the callback may be called currently by multiple threads.
                +# You should use some form of concurrency control inside the callback,
                +# such as a mutex or queue.
                +#
                +# Below is a brief example of usage to determine the percentage of
                +# connection requests where a connection was immediately available:
                +#
                +#   mutex = Mutex.new
                +#   total = immediates = 0
                +#
                +#   DB.pool.on_checkout_event = proc do |event|
                +#     case event
                +#     when :immediately_available
                +#       mutex.synchronize do
                +#         total += 1
                +#         immediates += 1
                +#       end
                +#     when :not_immediately_available
                +#       mutex.synchronize do
                +#         total += 1
                +#       end
                +#     end
                +#   end
                +#
                +#   immediate_percentage = lambda do
                +#     mutex.synchronize do
                +#       100.0 * immediates / total
                +#     end
                +#   end
                +#   
                +# Note that this extension only works with the timed_queue
                +# and sharded_timed_queue connection pools (the default
                +# connection pools when using Ruby 3.2+).
                +#
                +# Related modules: Sequel::ConnectionCheckoutEventCallbacks::TimedQueue,
                +# Sequel::ConnectionCheckoutEventCallbacks::ShardedTimedQueue
                +
                +#
                +module Sequel
                +  module ConnectionCheckoutEventCallbacks
                +    module TimedQueue
                +      # The callback that is called with connection checkout events.
                +      attr_accessor :on_checkout_event
                +
                +      private
                +
                +      def available
                +        conn = super
                +        @on_checkout_event.call(conn ? :immediately_available : :not_immediately_available)
                +        conn
                +      end
                +
                +      def try_make_new
                +        conn = super
                +        @on_checkout_event.call(:new_connection) if conn
                +        conn
                +      end
                +
                +      def wait_until_available
                +        timer = Sequel.start_timer
                +        conn = super
                +        @on_checkout_event.call(Sequel.elapsed_seconds_since(timer))
                +        conn
                +      end
                +    end
                +
                +    module ShardedTimedQueue
                +      # The callback that is called with connection checkout events.
                +      attr_accessor :on_checkout_event
                +
                +      private
                +
                +      def available(queue, server)
                +        conn = super
                +        @on_checkout_event.call(conn ? :immediately_available : :not_immediately_available, server)
                +        conn
                +      end
                +
                +      def try_make_new(server)
                +        conn = super
                +        @on_checkout_event.call(:new_connection, server) if conn
                +        conn
                +      end
                +
                +      def wait_until_available(queue, server)
                +        timer = Sequel.start_timer
                +        conn = super
                +        @on_checkout_event.call(Sequel.elapsed_seconds_since(timer), server)
                +        conn
                +      end
                +    end
                +  end
                +
                +  default_callback = proc{}
                +
                +  Database.register_extension(:connection_checkout_event_callback) do |db|
                +    pool = db.pool
                +
                +    case pool.pool_type
                +    when :timed_queue
                +      db.pool.extend(ConnectionCheckoutEventCallbacks::TimedQueue)
                +    when :sharded_timed_queue
                +      db.pool.extend(ConnectionCheckoutEventCallbacks::ShardedTimedQueue)
                +    else
                +      raise Error, "the connection_checkout_event_callback extension is only supported when using a timed_queue connection pool"
                +    end
                +
                +    pool.on_checkout_event ||= default_callback
                +  end
                +end
        lib/sequel/extensions/lit_require_frozen.rb
                --- /tmp/20260504-1888-ppkw38	2026-05-04 02:37:42.164776839 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/extensions/lit_require_frozen.rb	2026-05-04 02:37:42.134777086 +0000
                @@ -0,0 +1,131 @@
                +# frozen-string-literal: true
                +#
                +# The lit_require_frozen extension disallows the use of unfrozen strings
                +# as literal strings in database and dataset methods. If you try to use an
                +# unfrozen string as a literal string for a dataset using this extension,
                +# an exception will be raised.
                +#
                +# While this works for all Ruby versions, it is designed for use on Ruby 3+
                +# where all files are using the frozen-string-literal magic comment. In this
                +# case, uninterpolated literal strings are frozen, but interpolated strings
                +# are not frozen. This allows you to catch potentially dangerous code:
                +#
                +#   # Probably safe, no exception raised
                +#   DB["SELECT * FROM t WHERE c > :v", v: user_provided_string)
                +#
                +#   # Potentially unsafe, raises Sequel::LitRequireFrozen::Error
                +#   DB["SELECT * FROM t WHERE c > '#{user_provided_string}'"]
                +#
                +# The assumption made is that a frozen string is unlikely to contain unsafe
                +# input, while an unfrozen string has potentially been interpolated and may
                +# contain unsafe input.
                +#
                +# This disallows the the following cases:
                +#
                +# * Sequel::LiteralString instances that are unfrozen and are not based on a
                +#   frozen string
                +# * Sequel::SQL::PlaceholderLiteralString instances when the placeholder string
                +#   is not frozen
                +# * Unfrozen strings passed to Database#<< or #[] or Dataset#with_sql
                +#
                +# To use this extension, load it into the database:
                +#
                +#   DB.extension :lit_require_frozen
                +#
                +# It can also be loaded into individual datasets:
                +#
                +#  ds = DB[:t].extension(:lit_require_frozen)
                +#
                +# Assuming you have good test coverage, it is recommended to only load
                +# this extension when testing.
                +#
                +# Related module: Sequel::LitRequireFrozen
                +
                +#
                +module Sequel
                +  class LiteralString
                +    # The string used when creating the literal string (first argument to
                +    # Sequel::LiteralString.new). This may be nil if no string was provided,
                +    # or if the litral string was created before this extension was required.
                +    attr_reader :source
                +
                +    def initialize(*a)
                +      @source = a.first
                +      super
                +    end
                +    # :nocov:
                +    ruby2_keywords :initialize if respond_to?(:ruby2_keywords, true)
                +    # :nocov:
                +  end
                +
                +  module LitRequireFrozen
                +    # Error class raised for using unfrozen literal string.
                +    class Error < Sequel::Error
                +    end
                +
                +    module DatabaseMethods
                +      def self.extended(db)
                +        db.extend_datasets(DatasetMethods)
                +      end
                +
                +      # Check given SQL is frozen before running it.
                +      def run(sql, opts=OPTS)
                +        @default_dataset.with_sql(sql)
                +        super
                +      end
                +    end
                +
                +    module DatasetMethods
                +      # Check given SQL is not an unfrozen string.
                +      def with_sql(sql, *args)
                +        _check_unfrozen_literal_string(sql)
                +        super
                +      end
                +
                +      # Check that placeholder string is frozen (or all entries
                +      # in placeholder array are frozen).
                +      def placeholder_literal_string_sql_append(sql, pls)
                +        str = pls.str
                +
                +        if str.is_a?(Array)
                +          str.each do |s|
                +            _check_unfrozen_literal_string(s)
                +          end
                +        else
                +          _check_unfrozen_literal_string(str)
                +        end
                +
                +        super
                +      end
                +
                +      private
                +
                +      # Base method that other methods used to check for whether a string should be allowed
                +      # as literal SQL. Allows non-strings as well as frozen strings.
                +      def _check_unfrozen_literal_string(str)
                +        return if !str.is_a?(String) || str.frozen?
                +
                +        if str.is_a?(LiteralString)
                +          _check_unfrozen_literal_string(str.source)
                +        else
                +          raise Error, "cannot treat unfrozen string as literal SQL: #{str.inspect}"
                +        end
                +      end
                +
                +      # Check literal strings appended to SQL.
                +      def literal_literal_string_append(sql, v)
                +        _check_unfrozen_literal_string(v)
                +        super
                +      end
                +
                +      # Check static SQL is not frozen.
                +      def static_sql(sql)
                +        _check_unfrozen_literal_string(sql)
                +        super
                +      end
                +    end
                +  end
                +
                +  Dataset.register_extension(:lit_require_frozen, LitRequireFrozen::DatasetMethods)
                +  Database.register_extension(:lit_require_frozen, LitRequireFrozen::DatabaseMethods)
                +end
      * Changed:
        MIT-LICENSE
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/MIT-LICENSE	2026-05-04 02:37:42.066777644 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/MIT-LICENSE	2026-05-04 02:37:42.115777242 +0000
                @@ -2 +2 @@
                -Copyright (c) 2008-2023 Jeremy Evans
                +Copyright (c) 2008-2026 Jeremy Evans and contributors
        lib/sequel/adapters/jdbc/derby.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/adapters/jdbc/derby.rb	2026-05-04 02:37:42.071777603 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/adapters/jdbc/derby.rb	2026-05-04 02:37:42.117777225 +0000
                @@ -2,0 +3,2 @@
                +# SEQUEL6: Remove
                +
        lib/sequel/adapters/jdbc/h2.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/adapters/jdbc/h2.rb	2026-05-04 02:37:42.071777603 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/adapters/jdbc/h2.rb	2026-05-04 02:37:42.117777225 +0000
                @@ -21 +21 @@
                -          run("COMMIT TRANSACTION #{transaction_id}", opts)
                +          run("COMMIT TRANSACTION #{transaction_id}".freeze, opts)
                @@ -39 +39 @@
                -          run("ROLLBACK TRANSACTION #{transaction_id}", opts)
                +          run("ROLLBACK TRANSACTION #{transaction_id}".freeze, opts)
        lib/sequel/adapters/postgres.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/adapters/postgres.rb	2026-05-04 02:37:42.074777578 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/adapters/postgres.rb	2026-05-04 02:37:42.119777209 +0000
                @@ -730 +730 @@
                -            LiteralString.new("#{prepared_arg_placeholder}#{i}")
                +            LiteralString.new("#{prepared_arg_placeholder}#{i}".freeze)
        lib/sequel/adapters/shared/mssql.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/adapters/shared/mssql.rb	2026-05-04 02:37:42.075777570 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/adapters/shared/mssql.rb	2026-05-04 02:37:42.120777201 +0000
                @@ -109 +109 @@
                -        sql = "DECLARE #{declarations.join(', ')}; EXECUTE @RC = #{name} #{values.join(', ')}; SELECT #{names.join(', ')}"
                +        sql = "DECLARE #{declarations.join(', ')}; EXECUTE @RC = #{name} #{values.join(', ')}; SELECT #{names.join(', ')}".freeze
                @@ -403 +403 @@
                -        run(ds.into(name).sql)
                +        run(ds.into(name).sql.freeze)
                @@ -881 +881 @@
                -          sql = multi_insert_sql(columns, values)[0]
                +          sql = multi_insert_sql(columns, values)[0].freeze
        lib/sequel/adapters/shared/mysql.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/adapters/shared/mysql.rb	2026-05-04 02:37:42.075777570 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/adapters/shared/mysql.rb	2026-05-04 02:37:42.121777193 +0000
                @@ -43 +43 @@
                -        run("XA COMMIT #{literal(transaction_id)}", opts)
                +        run("XA COMMIT #{literal(transaction_id)}".freeze, opts)
                @@ -106 +106 @@
                -        metadata_dataset.with_sql(sql).each do |r|
                +        metadata_dataset.with_sql(sql.freeze).each do |r|
                @@ -118 +118 @@
                -        run("XA ROLLBACK #{literal(transaction_id)}", opts)
                +        run("XA ROLLBACK #{literal(transaction_id)}".freeze, opts)
                @@ -783 +783,2 @@
                -        ds = db.send(:metadata_dataset).with_sql(((opts[:extended] && (db.mariadb? || db.server_version < 50700)) ? 'EXPLAIN EXTENDED ' : 'EXPLAIN ') + select_sql).naked
                +        sql = ((opts[:extended] && (db.mariadb? || db.server_version < 50700)) ? 'EXPLAIN EXTENDED ' : 'EXPLAIN ') + select_sql
                +        ds = db.send(:metadata_dataset).with_sql(sql.freeze).naked
        lib/sequel/adapters/shared/postgres.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/adapters/shared/postgres.rb	2026-05-04 02:37:42.076777562 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/adapters/shared/postgres.rb	2026-05-04 02:37:42.122777184 +0000
                @@ -342 +342 @@
                -        run("COMMIT PREPARED #{literal(transaction_id)}", opts)
                +        run("COMMIT PREPARED #{literal(transaction_id)}".freeze, opts)
                @@ -419 +419 @@
                -          run("ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(column)} DROP DEFAULT", server_hash)
                +          run("ALTER TABLE #{quote_schema_table(table)} ALTER COLUMN #{quote_identifier(column)} DROP DEFAULT".freeze, server_hash)
                @@ -456 +456 @@
                -        self << create_function_sql(name, definition, opts)
                +        self << create_function_sql(name, definition, opts).freeze
                @@ -467 +467 @@
                -        self << create_language_sql(name, opts)
                +        self << create_language_sql(name, opts).freeze
                @@ -476 +476 @@
                -        self << create_schema_sql(name, opts)
                +        self << create_schema_sql(name, opts).freeze
                @@ -512 +512 @@
                -        self << create_trigger_sql(table, name, function, opts)
                +        self << create_trigger_sql(table, name, function, opts).freeze
                @@ -545 +545 @@
                -        run "DO #{"LANGUAGE #{literal(language.to_s)} " if language}#{literal(code)}"
                +        run "DO #{"LANGUAGE #{literal(language.to_s)} " if language}#{literal(code)}".freeze
                @@ -555 +555 @@
                -        self << drop_function_sql(name, opts)
                +        self << drop_function_sql(name, opts).freeze
                @@ -564 +564 @@
                -        self << drop_language_sql(name, opts)
                +        self << drop_language_sql(name, opts).freeze
                @@ -573 +573 @@
                -        self << drop_schema_sql(name, opts)
                +        self << drop_schema_sql(name, opts).freeze
                @@ -584 +584 @@
                -        self << drop_trigger_sql(table, name, opts)
                +        self << drop_trigger_sql(table, name, opts).freeze
                @@ -748 +748 @@
                -        self << rename_schema_sql(name, new_name)
                +        self << rename_schema_sql(name, new_name).freeze
                @@ -759 +759 @@
                -        run "REFRESH MATERIALIZED VIEW#{' CONCURRENTLY' if opts[:concurrently]} #{quote_schema_table(name)}"
                +        run "REFRESH MATERIALIZED VIEW#{' CONCURRENTLY' if opts[:concurrently]} #{quote_schema_table(name)}".freeze
                @@ -772 +772 @@
                -          seq_ds = metadata_dataset.from(:pg_sequence).where(:seqrelid=>regclass_oid(LiteralString.new(seq)))
                +          seq_ds = metadata_dataset.from(:pg_sequence).where(:seqrelid=>regclass_oid(LiteralString.new(seq.freeze)))
                @@ -787 +787 @@
                -        run("ROLLBACK PREPARED #{literal(transaction_id)}", opts)
                +        run("ROLLBACK PREPARED #{literal(transaction_id)}".freeze, opts)
                @@ -1472 +1472 @@
                -        cols[-1] = Sequel.lit("PERIOD #{quote_identifier(cols[-1])}")
                +        cols[-1] = Sequel.lit("PERIOD #{quote_identifier(cols[-1])}".freeze)
        lib/sequel/adapters/shared/sqlite.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/adapters/shared/sqlite.rb	2026-05-04 02:37:42.077777554 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/adapters/shared/sqlite.rb	2026-05-04 02:37:42.122777184 +0000
                @@ -198 +198 @@
                -        metadata_dataset.with_sql("PRAGMA table_#{'x' if sqlite_version > 33100}info(?)", input_identifier_meth(opts[:dataset]).call(table_name))
                +        metadata_dataset.with_sql("PRAGMA table_#{'x' if sqlite_version > 33100}info(?)".freeze, input_identifier_meth(opts[:dataset]).call(table_name))
                @@ -410 +410 @@
                -          c[:default] = LiteralString.new(c[:default]) if c[:default]
                +          c[:default] = LiteralString.new(c[:default]).freeze if c[:default]
                @@ -687 +687 @@
                -        ds = db.send(:metadata_dataset).clone(:sql=>"EXPLAIN #{select_sql}")
                +        ds = db.send(:metadata_dataset).clone(:sql=>"EXPLAIN #{select_sql}".freeze)
        lib/sequel/adapters/sqlite.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/adapters/sqlite.rb	2026-05-04 02:37:42.078777546 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/adapters/sqlite.rb	2026-05-04 02:37:42.122777184 +0000
                @@ -391 +391 @@
                -          LiteralString.new("#{prepared_arg_placeholder}#{k.to_s.gsub('.', '__')}")
                +          LiteralString.new("#{prepared_arg_placeholder}#{k.to_s.gsub('.', '__')}".freeze)
        lib/sequel/adapters/tinytds.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/adapters/tinytds.rb	2026-05-04 02:37:42.078777546 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/adapters/tinytds.rb	2026-05-04 02:37:42.122777184 +0000
                @@ -208 +208 @@
                -          LiteralString.new("@#{k.to_s.gsub('.', '__')}")
                +          LiteralString.new("@#{k.to_s.gsub('.', '__')}".freeze)
        lib/sequel/connection_pool.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/connection_pool.rb	2026-05-04 02:37:42.079777537 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/connection_pool.rb	2026-05-04 02:37:42.124777168 +0000
                @@ -133,0 +134,6 @@
                +  # Only for use by extension that need to disconnect a connection inside acquire.
                +  # Takes the connection and any arguments accepted by acquire.
                +  def disconnect_acquired_connection(conn, *)
                +    disconnect_connection(conn)
                +  end
                +
        lib/sequel/connection_pool/sharded_timed_queue.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/connection_pool/sharded_timed_queue.rb	2026-05-04 02:37:42.080777529 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/connection_pool/sharded_timed_queue.rb	2026-05-04 02:37:42.124777168 +0000
                @@ -74 +74 @@
                -          break unless (conn = queue.pop(timeout: 0)) && !conns[conn]
                +          break unless (conn = available(queue, server)) && !conns[conn]
                @@ -94 +94 @@
                -  # and then the method will only disconnect connectsion from those specified shards.
                +  # and then the method will only disconnect connections from those specified shards.
                @@ -98 +98,3 @@
                -      while conn = queue.pop(timeout: 0)
                +      nconns = 0
                +      while conn = available(queue, server)
                +        nconns += 1
                @@ -101 +103 @@
                -      fill_queue(server)
                +      fill_queue(server, nconns)
                @@ -135 +137 @@
                -        fill_queue(server)
                +        fill_queue(server, 1)
                @@ -170 +172 @@
                -        while conn = queue.pop(timeout: 0)
                +        while conn = available(queue, server)
                @@ -227,0 +230,6 @@
                +  # Only for use by extension that need to disconnect a connection inside acquire.
                +  # Takes the connection and any arguments accepted by acquire.
                +  def disconnect_acquired_connection(conn, _, server)
                +    disconnect_pool_connection(conn, server)
                +  end
                +
                @@ -247 +255,4 @@
                -  def fill_queue(server)
                +  #
                +  # nconns specifies the maximum number of connections to add, which should
                +  # be the number of connections that were disconnected.
                +  def fill_queue(server, nconns)
                @@ -249 +260 @@
                -    if queue.num_waiting > 0
                +    if nconns > 0 && queue.num_waiting > 0
                @@ -251 +262,2 @@
                -        while queue.num_waiting > 0 && (conn = try_make_new(server))
                +        while nconns > 0 && queue.num_waiting > 0 && (conn = try_make_new(server))
                +          nconns -= 1
                @@ -300 +312 @@
                -        fill_queue(server)
                +        fill_queue(server, to_disconnect.size)
                @@ -314 +326 @@
                -    if conn = queue.pop(timeout: 0) || try_make_new(server) || queue.pop(timeout: @timeout)
                +    if conn = available(queue, server) || try_make_new(server) || wait_until_available(queue, server)
                @@ -319,0 +332,13 @@
                +  end
                +
                +  # Return the next connection in the pool if there is one available. Returns nil
                +  # if no connection is currently available.
                +  def available(queue, _server)
                +    queue.pop(timeout: 0)
                +  end
                +
                +  # Return the next connection in the pool if there is one available. If not, wait
                +  # until the timeout for a connection to become available. If there is still no
                +  # available connection, return nil.
                +  def wait_until_available(queue, _server)
                +    queue.pop(timeout: @timeout)
        lib/sequel/connection_pool/timed_queue.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/connection_pool/timed_queue.rb	2026-05-04 02:37:42.080777529 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/connection_pool/timed_queue.rb	2026-05-04 02:37:42.124777168 +0000
                @@ -45 +45 @@
                -          break unless (conn = @queue.pop(timeout: 0)) && !conns[conn]
                +          break unless (conn = available) && !conns[conn]
                @@ -62 +62,3 @@
                -    while conn = @queue.pop(timeout: 0)
                +    nconns = 0
                +    while conn = available
                +      nconns += 1
                @@ -65 +67 @@
                -    fill_queue
                +    fill_queue(nconns)
                @@ -97 +99 @@
                -        fill_queue
                +        fill_queue(1)
                @@ -159,2 +161,5 @@
                -  def fill_queue
                -    if @queue.num_waiting > 0
                +  #
                +  # nconns specifies the maximum number of connections to add, which should
                +  # be the number of connections that were disconnected.
                +  def fill_queue(nconns)
                +    if nconns > 0 && @queue.num_waiting > 0
                @@ -162 +167 @@
                -        while @queue.num_waiting > 0 && (conn = try_make_new)
                +        while nconns > 0 && @queue.num_waiting > 0 && (conn = try_make_new)
                @@ -210 +215 @@
                -        fill_queue
                +        fill_queue(to_disconnect.size)
                @@ -223 +228 @@
                -    if conn = @queue.pop(timeout: 0) || try_make_new || @queue.pop(timeout: @timeout)
                +    if conn = available || try_make_new || wait_until_available
                @@ -228,0 +234,13 @@
                +  end
                +
                +  # Return the next connection in the pool if there is one available. Returns nil
                +  # if no connection is currently available.
                +  def available
                +    @queue.pop(timeout: 0)
                +  end
                +
                +  # Return the next connection in the pool if there is one available. If not, wait
                +  # until the timeout for a connection to become available. If there is still no
                +  # available connection, return nil.
                +  def wait_until_available
                +    @queue.pop(timeout: @timeout)
        lib/sequel/database/schema_generator.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/database/schema_generator.rb	2026-05-04 02:37:42.082777513 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/database/schema_generator.rb	2026-05-04 02:37:42.126777151 +0000
                @@ -5,0 +6,27 @@
                +    module ColumnOptionMerger
                +      private
                +
                +      # Merge given options into the column's default options. For backwards compatibility,
                +      # the options take priority, but in cases where the option value overrides the argument
                +      # value, and the values are different, we warn as this is likely to be an error in the
                +      # code.
                +      def _merge_column_options(defaults, opts)
                +        defaults.merge!(opts) do |k, defv, v|
                +          unless defv == v
                +            # :nocov:
                +            if RUBY_VERSION >= "3.2"
                +            # :nocov:
                +              caller_loc = Thread.each_caller_location do |loc|
                +                break loc unless loc.path == __FILE__
                +              end
                +              caller_loc &&= "#{caller_loc.path}:#{caller_loc.lineno}: "
                +            end
                +            warn("#{caller_loc}#{k.inspect} option value (#{v.inspect}) overrides argument value (#{defv.inspect})")
                +          end
                +
                +          v
                +        end
                +      end
                +    end
                +    private_constant :ColumnOptionMerger
                +
                @@ -19,0 +47,2 @@
                +      include ColumnOptionMerger
                +
                @@ -176 +205 @@
                -        columns << {:name => name, :type => type}.merge!(opts)
                +        columns << _merge_column_options({:name => name, :type => type}, opts)
                @@ -182 +211 @@
                -      
                +
                @@ -249 +278 @@
                -        column(name, Integer, opts)
                +        column(name, opts.fetch(:type, Integer), opts)
                @@ -431,0 +461,2 @@
                +      include ColumnOptionMerger
                +
                @@ -457 +488 @@
                -        op = {:op => :add_column, :name => name, :type => type}.merge!(opts)
                +        op = _merge_column_options({:op => :add_column, :name => name, :type => type}, opts)
                @@ -522 +553 @@
                -        add_column(name, Integer, {:table=>table}.merge!(opts))
                +        add_column(name, opts.fetch(:type, Integer), {:table=>table}.merge!(opts))
        lib/sequel/database/schema_methods.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/database/schema_methods.rb	2026-05-04 02:37:42.082777513 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/database/schema_methods.rb	2026-05-04 02:37:42.126777151 +0000
                @@ -822 +822 @@
                -      run(create_table_as_sql(name, sql, options))
                +      run(create_table_as_sql(name, sql, options).freeze)
        lib/sequel/dataset/placeholder_literalizer.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/dataset/placeholder_literalizer.rb	2026-05-04 02:37:42.084777496 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/dataset/placeholder_literalizer.rb	2026-05-04 02:37:42.128777135 +0000
                @@ -119,0 +120,2 @@
                +          frags.each(&:freeze)
                +          frags.freeze
                @@ -127,0 +130 @@
                +          ds.opts[:sql].freeze
        lib/sequel/dataset/prepared_statements.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/dataset/prepared_statements.rb	2026-05-04 02:37:42.084777496 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/dataset/prepared_statements.rb	2026-05-04 02:37:42.128777135 +0000
                @@ -57 +57 @@
                -        sql = prepared_sql
                +        sql = prepared_sql.freeze
                @@ -226 +226 @@
                -          with_sql(prepared_sql)
                +          with_sql(prepared_sql.freeze)
                @@ -290 +290 @@
                -          sql = literal(Sequel::SQL::PlaceholderLiteralString.new(@opts[:prepared_sql_frags], @opts[:bind_arguments], false))
                +          sql = literal(Sequel::SQL::PlaceholderLiteralString.new(@opts[:prepared_sql_frags], @opts[:bind_arguments], false)).freeze
                @@ -322,0 +323,3 @@
                +        frags.freeze
                +        frags.each(&:freeze)
                +        prepared_sql.freeze
                @@ -412 +415 @@
                -        sql = ps.prepared_sql
                +        sql = ps.prepared_sql.freeze
        lib/sequel/dataset/query.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/dataset/query.rb	2026-05-04 02:37:42.085777488 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/dataset/query.rb	2026-05-04 02:37:42.129777127 +0000
                @@ -1304 +1304 @@
                -        sql = public_send(sql, *args)
                +        sql = public_send(sql, *args).freeze
                @@ -1477 +1477,4 @@
                -    
                +
                +    PAREN_WRAPPER = ["(".freeze, ")".freeze].freeze
                +    private_constant :PAREN_WRAPPER
                +
                @@ -1498 +1501 @@
                -        LiteralString.new("(#{expr})")
                +        SQL::PlaceholderLiteralString.new(PAREN_WRAPPER, [expr])
        lib/sequel/dataset/sql.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/dataset/sql.rb	2026-05-04 02:37:42.085777488 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/dataset/sql.rb	2026-05-04 02:37:42.129777127 +0000
                @@ -56 +56 @@
                -          sql << v
                +          literal_literal_string_append(sql, v)
                @@ -1424,0 +1425,5 @@
                +    end
                +
                +    # Append string to SQL string.
                +    def literal_literal_string_append(sql, v)
                +      sql << v
        lib/sequel/extensions/connection_expiration.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/extensions/connection_expiration.rb	2026-05-04 02:37:42.086777480 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/extensions/connection_expiration.rb	2026-05-04 02:37:42.131777110 +0000
                @@ -94 +94 @@
                -          disconnect_connection(conn)
                +          disconnect_acquired_connection(conn, *a)
        lib/sequel/extensions/connection_validator.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/extensions/connection_validator.rb	2026-05-04 02:37:42.087777472 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/extensions/connection_validator.rb	2026-05-04 02:37:42.131777110 +0000
                @@ -121 +121 @@
                -              disconnect_connection(conn)
                +              disconnect_acquired_connection(conn, *a)
        lib/sequel/extensions/dataset_run.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/extensions/dataset_run.rb	2026-05-04 02:37:42.087777472 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/extensions/dataset_run.rb	2026-05-04 02:37:42.132777102 +0000
                @@ -33 +33 @@
                -        db.run(sql, :server=>server)
                +        db.run(sql.freeze, :server=>server)
                @@ -35 +35 @@
                -        db.run(sql)
                +        db.run(sql.freeze)
        lib/sequel/extensions/date_arithmetic.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/extensions/date_arithmetic.rb	2026-05-04 02:37:42.087777472 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/extensions/date_arithmetic.rb	2026-05-04 02:37:42.132777102 +0000
                @@ -85,2 +85,2 @@
                -        MYSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s.upcase[0...-1]).freeze}).freeze
                -        MSSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s[0...-1]).freeze}).freeze
                +        MYSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s.upcase[0...-1].freeze).freeze}).freeze
                +        MSSQL_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s[0...-1].freeze).freeze}).freeze
                @@ -88 +88 @@
                -        DERBY_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit("SQL_TSI_#{s.to_s.upcase[0...-1]}").freeze}).freeze
                +        DERBY_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit("SQL_TSI_#{s.to_s.upcase[0...-1]}".freeze).freeze}).freeze
                @@ -90 +90 @@
                -        DB2_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s).freeze}).freeze
                +        DB2_DURATION_UNITS = DURATION_UNITS.zip(DURATION_UNITS.map{|s| Sequel.lit(s.to_s.freeze).freeze}).freeze
                @@ -110 +110 @@
                -                placeholder << "#{', ' unless placeholder.empty?}#{sql_unit} := "
                +                placeholder << "#{', ' unless placeholder.empty?}#{sql_unit} := ".freeze
                @@ -159 +159 @@
                -              expr = Sequel.lit(["{fn timestampadd(#{sql_unit}, ", ", timestamp(", "))}"], value, expr)
                +              expr = Sequel.lit(["{fn timestampadd(#{sql_unit}, ".freeze, ", timestamp(", "))}"], value, expr)
        lib/sequel/extensions/migration.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/extensions/migration.rb	2026-05-04 02:37:42.090777447 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/extensions/migration.rb	2026-05-04 02:37:42.134777086 +0000
                @@ -189,16 +189,13 @@
                -      begin
                -        instance_exec(&block)
                -      rescue
                -        just_raise = true
                -      end
                -      if just_raise
                -        Proc.new{raise Sequel::Error, "irreversible migration method used in #{block.source_location.first}, you may need to write your own down method"}
                -      else
                -        actions = @actions.reverse
                -        Proc.new do
                -          actions.each do |a|
                -            pr = a.last.is_a?(Proc) ? a.pop : nil
                -            # Allow calling private methods as the reversing methods are private
                -            send(*a, &pr)
                -          end
                -        end
                +      instance_exec(&block)
                +    rescue NoMethodError => e
                +      Proc.new{raise Sequel::Error, "irreversible migration method \"#{e.name}\" used in #{block.source_location.first}, you may need to write your own down method"}
                +    rescue => e
                +      Proc.new{raise Sequel::Error, "unable to reverse migration due to #{e.class} in #{block.source_location.first}, you may need to write your own down method"}
                +    else
                +      actions = @actions.reverse
                +      Proc.new do
                +       actions.each do |a|
                +        pr = a.last.is_a?(Proc) ? a.pop : nil
                +        # Allow calling private methods as the reversing methods are private
                +        send(*a, &pr)
                +       end
                @@ -273 +270 @@
                -      raise if args.first.is_a?(Array)
                +      super if args.first.is_a?(Array)
        lib/sequel/extensions/pg_enum.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/extensions/pg_enum.rb	2026-05-04 02:37:42.091777439 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/extensions/pg_enum.rb	2026-05-04 02:37:42.136777069 +0000
                @@ -130 +130 @@
                -        run(sql)
                +        run(sql.freeze)
        lib/sequel/model/associations.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/model/associations.rb	2026-05-04 02:37:42.099777373 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/model/associations.rb	2026-05-04 02:37:42.144777004 +0000
                @@ -114,0 +115,2 @@
                +          when :lateral_subquery
                +            apply_lateral_subquery_eager_graph_limit_strategy(ds)
                @@ -301 +303,6 @@
                -            objects = apply_eager_limit_strategy(ds, strategy, eager_limit).all
                +
                +            objects = if strategy == :lateral_subquery
                +              apply_lateral_subquery_eager_limit_strategy(ds, ids, eager_limit).all
                +            else
                +              apply_eager_limit_strategy(ds, strategy, eager_limit).all
                +            end
                @@ -323 +330 @@
                -              objects.concat(ds.with_sql(sql).to_a)
                +              objects.concat(ds.with_sql(sql.freeze).to_a)
                @@ -369 +376,6 @@
                -          ds = filter_by_associations_conditions_dataset.where(filter_by_associations_conditions_subquery_conditions(obj))
                +          ds = if filter_by_associations_limit_strategy == :lateral_subquery
                +            apply_lateral_subquery_filter_limit_strategy(associated_eager_dataset, obj)
                +          else
                +            filter_by_associations_conditions_dataset.where(filter_by_associations_conditions_subquery_conditions(obj))
                +          end
                +
                @@ -756,0 +769,55 @@
                +        def lateral_subquery_eager_limit_strategy_lateral_dataset(ds, limit_and_offset)
                +          ds.
                +            where(Array(filter_by_associations_conditions_key).zip(Array(filter_by_associations_conditions_associated_keys))).
                +            limit(*limit_and_offset).
                +            lateral
                +        end
                +
                +        def apply_lateral_subquery_eager_limit_strategy(ds, ids, limit_and_offset)
                +          table_name = self[:model].table_name
                +          associated_table_name = associated_class.table_name
                +
                +          associated_class.
                +            from(table_name).
                +            select_all(associated_table_name).
                +            join(lateral_subquery_eager_limit_strategy_lateral_dataset(ds, limit_and_offset).as(associated_table_name), true).
                +            order(*self[:order])
                +        end
                +
                +        # Return an expression to filter the filter by associations dataset to only
                +        # rows related to given objects.
                +        def _lateral_subquery_filter_limit_strategy_conditions(obj, key, value_method, value_column)
                +          value = case obj
                +          when Array
                +            if key.is_a?(Array)
                +              key_methods = Array(value_method)
                +              obj.map{|o| key_methods.map{|meth| o.send(meth)}}
                +            else
                +              obj.map{|o| o.send(value_method)}
                +            end
                +          when Sequel::Dataset
                +            obj.select(*Array(qualify(associated_class.table_name, value_column)))
                +          else
                +            if key.is_a?(Array)
                +              return Array(key).zip(Array(value_method).map{|meth| obj.send(meth)})
                +            else
                +              obj.send(value_method)
                +            end
                +          end
                +
                +          {key => value}
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_lateral_dataset(ds, obj)
                +          lateral_subquery_filter_limit_strategy_filter_lateral_dataset(ds.
                +            select(*qualify(associated_class.table_name, associated_class.primary_key)).
                +            limit(*limit_and_offset).
                +            lateral)
                +        end
                +
                +        def apply_lateral_subquery_filter_limit_strategy(ds, obj)
                +          lateral_subquery_filter_limit_strategy_filter_dataset(self[:model].
                +            select(*lateral_subquery_filter_limit_strategy_lateral_dataset_select).
                +            join(lateral_subquery_filter_limit_strategy_lateral_dataset(ds, obj).as(associated_class.table_name), filter_by_associations_conditions_subquery_conditions(obj)), obj)
                +        end
                +
                @@ -779 +846,6 @@
                -              apply_eager_limit_strategy(ds.where(predicate_key=>arg), eager_limit_strategy)
                +              strategy = eager_limit_strategy
                +              if strategy == :lateral_subquery
                +                apply_lateral_subquery_eager_limit_strategy(ds, arg, limit_and_offset)
                +              else
                +                apply_eager_limit_strategy(ds.where(predicate_key=>arg), strategy)
                +              end
                @@ -1021 +1093 @@
                -        # Support a correlated subquery limit strategy when using eager_graph.
                +        # Support a lateral_subquery and correlated_subquery limit strategy when using eager_graph.
                @@ -1120,0 +1193,48 @@
                +        # Use a LATERAL subquery to limit the dataset.  Note that this will not
                +        # work correctly if the associated dataset uses qualified identifers in the WHERE clause,
                +        # as they would reference the containing query instead of the subquery.
                +        #
                +        # This does not contain the conditions that are necessary to join to the
                +        # query, since the necessary qualifier is not passed as an argument.
                +        def apply_lateral_subquery_eager_graph_limit_strategy(ds)
                +          table_name = ds.first_source_alias
                +          qualifier = ds.opts[:eager_options][:implicit_qualifier]
                +          graph_conditions = self[:_graph_conditions]
                +
                +          unless Sequel.condition_specifier?(graph_conditions)
                +            raise Error, "lateral_subquery eager graph limit strategy only supported when graph conditions are a hash or array of pairs"
                +          end
                +
                +          ds.
                +            limit(*limit_and_offset).
                +            order(*self[:order]).
                +            where(graph_conditions.map{|k, v| [qualify(table_name, k), qualify(qualifier, v)]}).
                +            lateral
                +        end
                +
                +        # Avoid setting duplicate predicate condition when using the lateral subquery
                +        # eager limit strategy.
                +        def eager_loading_set_predicate_condition(ds, eo)
                +          eager_limit_strategy == :lateral_subquery ? ds : super
                +        end
                +
                +        def apply_lateral_subquery_eager_limit_strategy(ds, ids, limit_and_offset)
                +          super.where(qualify(self[:model].table_name, self[:primary_key]) => ids)
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_conditions(obj)
                +          _lateral_subquery_filter_limit_strategy_conditions(obj, filter_by_associations_conditions_key, self[:key_method], self[:key])
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_filter_lateral_dataset(ds)
                +          ds.where(Array(filter_by_associations_conditions_key).zip(Array(filter_by_associations_conditions_associated_keys)))
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_filter_dataset(ds, obj)
                +          ds.where(lateral_subquery_filter_limit_strategy_conditions(obj))
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_lateral_dataset_select
                +          qualified_primary_key
                +        end
                +
                @@ -1434,0 +1555,46 @@
                +        def lateral_subquery_filter_limit_strategy_conditions(obj)
                +          _lateral_subquery_filter_limit_strategy_conditions(obj, lateral_subquery_filter_limit_strategy_conditions_key, right_primary_key_method, right_primary_key)
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_conditions_key
                +          self[:right_key]
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_filter_lateral_dataset(ds)
                +          ds.where(Array(filter_by_associations_conditions_key).zip(Array(filter_by_associations_conditions_associated_keys)))
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_filter_dataset(ds, obj)
                +          ds.where(filter_by_associations_conditions_key => ds.db.from(self[:join_table]).
                +            select(*self[:left_key]).
                +            where(lateral_subquery_filter_limit_strategy_conditions(obj)))
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_lateral_dataset_select
                +          qualify(self[:model].table_name, self[:left_primary_key])
                +        end
                +
                +        def lateral_subquery_eager_limit_strategy_lateral_dataset(ds, limit_and_offset)
                +          ds = super
                +          select = ds.opts[:select].dup
                +          left_key_alias = self[:left_key_alias]
                +          select.pop while (s = select.last).is_a?(Sequel::SQL::AliasedExpression) && (left_key_alias.is_a?(Array) ? left_key_alias.include?(s.alias) : s.alias == left_key_alias)
                +          ds = ds.clone(:select=>select)
                +        end
                +
                +        def apply_lateral_subquery_eager_limit_strategy(ds, ids, limit_and_offset)
                +          table_name = self[:model].table_name
                +          super.
                +            select_all(associated_class.table_name).
                +            select_append(*qualify(table_name, self[:left_primary_keys]).zip(Array(self[:left_key_alias])).map{|id, aliaz| Sequel.as(id, aliaz)}).
                +            where(qualify(table_name, self[:left_primary_key]) => ids)
                +        end
                +
                +        def apply_lateral_subquery_eager_graph_limit_strategy(ds)
                +          ds.
                +            limit(*limit_and_offset).
                +            where(qualify(join_table_alias, self[:left_keys]).zip(qualify(ds.opts[:eager_options][:implicit_qualifier], self[:left_primary_key_columns]))).
                +            order(*self[:order]).
                +            lateral
                +        end
                +
                @@ -2415,0 +2582 @@
                +          graph_conditions = opts[:_graph_conditions] = use_only_conditions ? only_conditions : cks.zip(pkcs) + conditions
                @@ -2418 +2585,5 @@
                -            ds = ds.graph(opts.apply_eager_graph_limit_strategy(eo[:limit_strategy], eager_graph_dataset(opts, eo)), use_only_conditions ? only_conditions : cks.zip(pkcs) + conditions, eo.merge(:select=>select, :join_type=>eo[:join_type]||join_type, :qualify=>:deep), &graph_block)
                +            graph_limit_strategy = eo[:limit_strategy]
                +            egds = opts.apply_eager_graph_limit_strategy(graph_limit_strategy, eager_graph_dataset(opts, eo))
                +            graph_conditions_true = true if graph_limit_strategy == :lateral_subquery
                +
                +            ds = ds.graph(egds, graph_conditions_true || graph_conditions, eo.merge(:select=>select, :join_type=>eo[:join_type]||join_type, :qualify=>:deep), &graph_block)
                @@ -2500,0 +2672,3 @@
                +          if eager_options[:limit_strategy] == :lateral_subquery
                +            ds = ds.clone(:eager_options=>eager_options)
                +          end
        lib/sequel/plugins/dataset_associations.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/plugins/dataset_associations.rb	2026-05-04 02:37:42.104777332 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/plugins/dataset_associations.rb	2026-05-04 02:37:42.149776963 +0000
                @@ -85 +85 @@
                -          ds = r.associated_class.dataset
                +          klass = r.associated_class
                @@ -86,0 +87,19 @@
                +
                +          if r.send(:filter_by_associations_limit_strategy) == :lateral_subquery
                +            ds = r.send(:associated_eager_dataset)
                +
                +            case r[:type]
                +            when :one_to_one, :one_to_many
                +              sds = sds.select(*Array(r.qualified_primary_key))
                +            else
                +              sds = sds.select(*r[:left_primary_keys])
                +              ds = ds.select_all(klass.table_name)
                +              update_select = true
                +            end
                +
                +            ds = r.send(:apply_lateral_subquery_eager_limit_strategy, ds, sds, r.limit_and_offset)
                +            ds = ds.clone(:select=>ds.opts[:select][0,1]) if update_select
                +            return ds.clone(:eager=>nil, :eager_graph=>nil)
                +          end
                +
                +          ds = klass.dataset
        lib/sequel/plugins/dirty.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/plugins/dirty.rb	2026-05-04 02:37:42.105777324 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/plugins/dirty.rb	2026-05-04 02:37:42.150776954 +0000
                @@ -237,2 +237,5 @@
                -            check_missing_initial_value(column)
                -            iv[column] = get_column_value(column)
                +            if db_schema[column]
                +              check_missing_initial_value(column)
                +              iv[column] = get_column_value(column)
                +            end
                +
        lib/sequel/plugins/many_through_many.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/plugins/many_through_many.rb	2026-05-04 02:37:42.107777308 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/plugins/many_through_many.rb	2026-05-04 02:37:42.153776930 +0000
                @@ -148,0 +149,21 @@
                +        def lateral_subquery_filter_limit_strategy_conditions_key
                +          qualify(reverse_edges.first[:table], reverse_edges.first[:left])
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_filter_lateral_dataset(ds)
                +          ds.where(Array(qualify(edges.first[:table], edges.first[:right])).zip(Array(qualify(self[:model].table_name, edges.first[:left]))))
                +        end
                +
                +        def lateral_subquery_filter_limit_strategy_filter_dataset(ds, obj)
                +          first_edge, *remaining_edges = edges
                +          filter_ds = ds.db.from(first_edge[:table]).
                +            select(*qualify(first_edge[:table], first_edge[:right])).
                +            where(lateral_subquery_filter_limit_strategy_conditions(obj))
                +
                +          remaining_edges.each do |edge|
                +            filter_ds = filter_ds.join(edge[:table], Array(edge[:right]).zip(Array(edge[:left])))
                +          end
                +
                +          ds.where(qualify(self[:model].table_name, self[:left_primary_key]) => filter_ds)
                +        end
                +
        lib/sequel/plugins/mssql_optimistic_locking.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/plugins/mssql_optimistic_locking.rb	2026-05-04 02:37:42.107777308 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/plugins/mssql_optimistic_locking.rb	2026-05-04 02:37:42.153776930 +0000
                @@ -58 +58 @@
                -          rows = ds.clone(ds.send(:default_server_opts, :sql=>ds.output(nil, [Sequel[:inserted][lc]]).update_sql(columns))).all
                +          rows = ds.clone(ds.send(:default_server_opts, :sql=>ds.output(nil, [Sequel[:inserted][lc]]).update_sql(columns).freeze)).all
        lib/sequel/plugins/pg_xmin_optimistic_locking.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/plugins/pg_xmin_optimistic_locking.rb	2026-05-04 02:37:42.109777291 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/plugins/pg_xmin_optimistic_locking.rb	2026-05-04 02:37:42.154776922 +0000
                @@ -102 +102 @@
                -          rows = ds.clone(ds.send(:default_server_opts, :sql=>ds.returning(:xmin).update_sql(columns))).all
                +          rows = ds.clone(ds.send(:default_server_opts, :sql=>ds.returning(:xmin).update_sql(columns).freeze)).all
        lib/sequel/plugins/serialization.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/plugins/serialization.rb	2026-05-04 02:37:42.110777283 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/plugins/serialization.rb	2026-05-04 02:37:42.155776913 +0000
                @@ -32,0 +33,9 @@
                +    # Note that use of an unsafe serialization method can result in an attack vector
                +    # (potentially allowing remote code execution) if an attacker has the ability to
                +    # store data directly in the underlying column. This would affect the marshal
                +    # serialization format, and on older versions of Ruby, potentially the yaml and
                +    # json serialization formats as well. It can also affect custom formats. You
                +    # should ensure that attackers do not have access to store data directly in the
                +    # underlying column when using this plugin (especially when using an unsafe
                +    # serialization method).
                +    #
                @@ -100 +109 @@
                -          v = v.unpack('m')[0] unless v[0..1] == "\x04\x08"
                +          v = v.unpack('m')[0] unless v.start_with?("\x04\x08")
        lib/sequel/version.rb
                --- /tmp/d20260504-1888-jhtntp/sequel-5.101.0/lib/sequel/version.rb	2026-05-04 02:37:42.114777250 +0000
                +++ /tmp/d20260504-1888-jhtntp/sequel-5.104.0/lib/sequel/version.rb	2026-05-04 02:37:42.161776864 +0000
                @@ -9 +9 @@
                -  MINOR = 101
                +  MINOR = 104

@dependabot @github
Copy link
Copy Markdown
Contributor Author

dependabot Bot commented on behalf of github May 7, 2026

Looks like sequel is up-to-date now, so this is no longer needed.

@dependabot dependabot Bot closed this May 7, 2026
@dependabot dependabot Bot deleted the dependabot/bundler/sequel-5.104.0 branch May 7, 2026 14:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies ruby Pull requests that update Ruby code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants