sequel 5.32.0 → 5.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG +84 -0
- data/README.rdoc +1 -1
- data/doc/association_basics.rdoc +7 -2
- data/doc/dataset_filtering.rdoc +2 -2
- data/doc/model_plugins.rdoc +1 -1
- data/doc/release_notes/5.33.0.txt +24 -0
- data/doc/release_notes/5.34.0.txt +40 -0
- data/doc/release_notes/5.35.0.txt +56 -0
- data/doc/release_notes/5.36.0.txt +60 -0
- data/doc/release_notes/5.37.0.txt +30 -0
- data/doc/transactions.rdoc +0 -8
- data/doc/validations.rdoc +1 -1
- data/lib/sequel/adapters/odbc.rb +4 -6
- data/lib/sequel/adapters/oracle.rb +2 -1
- data/lib/sequel/adapters/shared/mssql.rb +14 -4
- data/lib/sequel/adapters/shared/oracle.rb +12 -6
- data/lib/sequel/adapters/shared/postgres.rb +39 -1
- data/lib/sequel/adapters/shared/sqlite.rb +13 -3
- data/lib/sequel/adapters/tinytds.rb +1 -0
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -0
- data/lib/sequel/connection_pool/sharded_single.rb +4 -1
- data/lib/sequel/connection_pool/sharded_threaded.rb +10 -10
- data/lib/sequel/connection_pool/single.rb +1 -1
- data/lib/sequel/connection_pool/threaded.rb +1 -1
- data/lib/sequel/core.rb +5 -6
- data/lib/sequel/database/connecting.rb +1 -1
- data/lib/sequel/database/misc.rb +16 -10
- data/lib/sequel/database/query.rb +2 -0
- data/lib/sequel/database/schema_generator.rb +0 -1
- data/lib/sequel/database/schema_methods.rb +15 -16
- data/lib/sequel/database/transactions.rb +8 -5
- data/lib/sequel/dataset/actions.rb +10 -6
- data/lib/sequel/dataset/placeholder_literalizer.rb +3 -7
- data/lib/sequel/dataset/query.rb +5 -4
- data/lib/sequel/deprecated.rb +3 -1
- data/lib/sequel/exceptions.rb +2 -0
- data/lib/sequel/extensions/_pretty_table.rb +1 -2
- data/lib/sequel/extensions/columns_introspection.rb +1 -2
- data/lib/sequel/extensions/core_refinements.rb +2 -0
- data/lib/sequel/extensions/duplicate_columns_handler.rb +2 -0
- data/lib/sequel/extensions/migration.rb +8 -2
- data/lib/sequel/extensions/pg_array_ops.rb +4 -0
- data/lib/sequel/extensions/pg_enum.rb +2 -0
- data/lib/sequel/extensions/pg_extended_date_support.rb +1 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +2 -0
- data/lib/sequel/extensions/pg_inet.rb +2 -0
- data/lib/sequel/extensions/pg_json_ops.rb +46 -2
- data/lib/sequel/extensions/pg_range.rb +3 -7
- data/lib/sequel/extensions/pg_range_ops.rb +2 -0
- data/lib/sequel/extensions/pg_row.rb +0 -1
- data/lib/sequel/extensions/pg_row_ops.rb +24 -0
- data/lib/sequel/extensions/query.rb +1 -0
- data/lib/sequel/extensions/run_transaction_hooks.rb +1 -1
- data/lib/sequel/extensions/s.rb +2 -0
- data/lib/sequel/extensions/schema_dumper.rb +3 -3
- data/lib/sequel/extensions/symbol_aref_refinement.rb +2 -0
- data/lib/sequel/extensions/symbol_as_refinement.rb +2 -0
- data/lib/sequel/extensions/to_dot.rb +9 -3
- data/lib/sequel/model.rb +3 -1
- data/lib/sequel/model/associations.rb +54 -25
- data/lib/sequel/model/base.rb +13 -5
- data/lib/sequel/model/plugins.rb +3 -3
- data/lib/sequel/plugins/association_pks.rb +60 -18
- data/lib/sequel/plugins/association_proxies.rb +1 -0
- data/lib/sequel/plugins/blacklist_security.rb +1 -2
- data/lib/sequel/plugins/class_table_inheritance.rb +3 -3
- data/lib/sequel/plugins/csv_serializer.rb +2 -0
- data/lib/sequel/plugins/dirty.rb +45 -0
- data/lib/sequel/plugins/forbid_lazy_load.rb +2 -0
- data/lib/sequel/plugins/instance_specific_default.rb +113 -0
- data/lib/sequel/plugins/lazy_attributes.rb +1 -1
- data/lib/sequel/plugins/pg_array_associations.rb +2 -3
- data/lib/sequel/plugins/prepared_statements.rb +5 -11
- data/lib/sequel/plugins/prepared_statements_safe.rb +1 -3
- data/lib/sequel/plugins/rcte_tree.rb +10 -16
- data/lib/sequel/plugins/string_stripper.rb +1 -1
- data/lib/sequel/plugins/validation_class_methods.rb +5 -1
- data/lib/sequel/version.rb +1 -1
- metadata +13 -2
| @@ -37,7 +37,7 @@ module Sequel | |
| 37 37 | 
             
                    {:type =>schema[:type] == :boolean ? TrueClass : Integer}
         | 
| 38 38 | 
             
                  when /\Abigint(?:\((?:\d+)\))?(?: unsigned)?\z/
         | 
| 39 39 | 
             
                    {:type=>:Bignum}
         | 
| 40 | 
            -
                  when /\A(?:real|float | 
| 40 | 
            +
                  when /\A(?:real|float|double(?: precision)?|double\(\d+,\d+\))(?: unsigned)?\z/
         | 
| 41 41 | 
             
                    {:type=>Float}
         | 
| 42 42 | 
             
                  when 'boolean', 'bit', 'bool'
         | 
| 43 43 | 
             
                    {:type=>TrueClass}
         | 
| @@ -57,7 +57,7 @@ module Sequel | |
| 57 57 | 
             
                    {:type=>String, :size=>($1.to_i if $1)}
         | 
| 58 58 | 
             
                  when /\A(?:small)?money\z/
         | 
| 59 59 | 
             
                    {:type=>BigDecimal, :size=>[19,2]}
         | 
| 60 | 
            -
                  when /\A(?:decimal|numeric|number)(?:\((\d+)(?:,\s*(\d+))?\))?\z/
         | 
| 60 | 
            +
                  when /\A(?:decimal|numeric|number)(?:\((\d+)(?:,\s*(\d+))?\))?(?: unsigned)?\z/
         | 
| 61 61 | 
             
                    s = [($1.to_i if $1), ($2.to_i if $2)].compact
         | 
| 62 62 | 
             
                    {:type=>BigDecimal, :size=>(s.empty? ? nil : s)}
         | 
| 63 63 | 
             
                  when /\A(?:bytea|(?:tiny|medium|long)?blob|(?:var)?binary)(?:\((\d+)\))?\z/
         | 
| @@ -218,7 +218,7 @@ END_MIG | |
| 218 218 | 
             
                      gen.foreign_key(name, table, col_opts)
         | 
| 219 219 | 
             
                    else
         | 
| 220 220 | 
             
                      gen.column(name, type, col_opts)
         | 
| 221 | 
            -
                      if [Integer, :Bignum, Float].include?(type) && schema[:db_type] =~ / unsigned\z/io
         | 
| 221 | 
            +
                      if [Integer, :Bignum, Float, BigDecimal].include?(type) && schema[:db_type] =~ / unsigned\z/io
         | 
| 222 222 | 
             
                        gen.check(Sequel::SQL::Identifier.new(name) >= 0)
         | 
| 223 223 | 
             
                      end
         | 
| 224 224 | 
             
                    end
         | 
| @@ -53,7 +53,13 @@ module Sequel | |
| 53 53 | 
             
                # is given, it is used directly as the node or transition.  Otherwise
         | 
| 54 54 | 
             
                # a node is created for the current object.
         | 
| 55 55 | 
             
                def dot(label, j=nil)
         | 
| 56 | 
            -
                   | 
| 56 | 
            +
                  label = case label
         | 
| 57 | 
            +
                  when nil
         | 
| 58 | 
            +
                    "<nil>"
         | 
| 59 | 
            +
                  else
         | 
| 60 | 
            +
                    label.to_s
         | 
| 61 | 
            +
                  end
         | 
| 62 | 
            +
                  @dot << "#{j||@i} [label=#{label.inspect}];"
         | 
| 57 63 | 
             
                end
         | 
| 58 64 |  | 
| 59 65 | 
             
                # Recursive method that parses all of Sequel's internal datastructures,
         | 
| @@ -61,7 +67,7 @@ module Sequel | |
| 61 67 | 
             
                # structure.
         | 
| 62 68 | 
             
                def v(e, l)
         | 
| 63 69 | 
             
                  @i += 1
         | 
| 64 | 
            -
                  dot(l, "#{@stack.last} -> #{@i}") | 
| 70 | 
            +
                  dot(l, "#{@stack.last} -> #{@i}")
         | 
| 65 71 | 
             
                  @stack.push(@i)
         | 
| 66 72 | 
             
                  case e
         | 
| 67 73 | 
             
                  when LiteralString
         | 
| @@ -144,7 +150,7 @@ module Sequel | |
| 144 150 | 
             
                    dot "Dataset"
         | 
| 145 151 | 
             
                    TO_DOT_OPTIONS.each do |k|
         | 
| 146 152 | 
             
                      if val = e.opts[k]
         | 
| 147 | 
            -
                        v(val, k | 
| 153 | 
            +
                        v(val, k) 
         | 
| 148 154 | 
             
                      end
         | 
| 149 155 | 
             
                    end
         | 
| 150 156 | 
             
                  else
         | 
    
        data/lib/sequel/model.rb
    CHANGED
    
    | @@ -69,7 +69,9 @@ module Sequel | |
| 69 69 | 
             
                require_relative "model/base"
         | 
| 70 70 | 
             
                require_relative "model/exceptions"
         | 
| 71 71 | 
             
                require_relative "model/errors"
         | 
| 72 | 
            +
                # :nocov:
         | 
| 72 73 | 
             
                if !defined?(::SEQUEL_NO_ASSOCIATIONS) && !ENV.has_key?('SEQUEL_NO_ASSOCIATIONS')
         | 
| 74 | 
            +
                # :nocov:
         | 
| 73 75 | 
             
                  require_relative 'model/associations'
         | 
| 74 76 | 
             
                  plugin Model::Associations
         | 
| 75 77 | 
             
                end
         | 
| @@ -77,7 +79,7 @@ module Sequel | |
| 77 79 | 
             
                def_Model(::Sequel)
         | 
| 78 80 |  | 
| 79 81 | 
             
                # The setter methods (methods ending with =) that are never allowed
         | 
| 80 | 
            -
                # to be called automatically via +set+/+update+/+new+/etc | 
| 82 | 
            +
                # to be called automatically via +set+/+update+/+new+/etc.
         | 
| 81 83 | 
             
                RESTRICTED_SETTER_METHODS = instance_methods.map(&:to_s).select{|l| l.end_with?('=')}.freeze
         | 
| 82 84 | 
             
              end
         | 
| 83 85 | 
             
            end
         | 
| @@ -164,11 +164,11 @@ module Sequel | |
| 164 164 | 
             
                    # range to return the object(s) at the correct offset/limit.
         | 
| 165 165 | 
             
                    def apply_ruby_eager_limit_strategy(rows, limit_and_offset = limit_and_offset())
         | 
| 166 166 | 
             
                      name = self[:name]
         | 
| 167 | 
            +
                      return unless range = slice_range(limit_and_offset)
         | 
| 167 168 | 
             
                      if returns_array?
         | 
| 168 | 
            -
                        range = slice_range(limit_and_offset)
         | 
| 169 169 | 
             
                        rows.each{|o| o.associations[name] = o.associations[name][range] || []}
         | 
| 170 | 
            -
                       | 
| 171 | 
            -
                        offset =  | 
| 170 | 
            +
                      else
         | 
| 171 | 
            +
                        offset = range.begin
         | 
| 172 172 | 
             
                        rows.each{|o| o.associations[name] = o.associations[name][offset]}
         | 
| 173 173 | 
             
                      end
         | 
| 174 174 | 
             
                    end
         | 
| @@ -356,7 +356,7 @@ module Sequel | |
| 356 356 | 
             
                    def finalize
         | 
| 357 357 | 
             
                      return unless cache = self[:cache]
         | 
| 358 358 |  | 
| 359 | 
            -
                       | 
| 359 | 
            +
                      finalizer = proc do |meth, key|
         | 
| 360 360 | 
             
                        next if has_key?(key)
         | 
| 361 361 |  | 
| 362 362 | 
             
                        # Allow calling private methods to make sure caching is done appropriately
         | 
| @@ -364,6 +364,13 @@ module Sequel | |
| 364 364 | 
             
                        self[key] = cache.delete(key) if cache.has_key?(key)
         | 
| 365 365 | 
             
                      end
         | 
| 366 366 |  | 
| 367 | 
            +
                      finalize_settings.each(&finalizer)
         | 
| 368 | 
            +
             | 
| 369 | 
            +
                      unless self[:instance_specific]
         | 
| 370 | 
            +
                        finalizer.call(:associated_eager_dataset, :associated_eager_dataset)
         | 
| 371 | 
            +
                        finalizer.call(:filter_by_associations_conditions_dataset, :filter_by_associations_conditions_dataset)
         | 
| 372 | 
            +
                      end
         | 
| 373 | 
            +
             | 
| 367 374 | 
             
                      nil
         | 
| 368 375 | 
             
                    end
         | 
| 369 376 |  | 
| @@ -371,9 +378,7 @@ module Sequel | |
| 371 378 | 
             
                    FINALIZE_SETTINGS = {
         | 
| 372 379 | 
             
                      :associated_class=>:class,
         | 
| 373 380 | 
             
                      :associated_dataset=>:_dataset,
         | 
| 374 | 
            -
                      :associated_eager_dataset=>:associated_eager_dataset,
         | 
| 375 381 | 
             
                      :eager_limit_strategy=>:_eager_limit_strategy,
         | 
| 376 | 
            -
                      :filter_by_associations_conditions_dataset=>:filter_by_associations_conditions_dataset,
         | 
| 377 382 | 
             
                      :placeholder_loader=>:placeholder_loader,
         | 
| 378 383 | 
             
                      :predicate_key=>:predicate_key,
         | 
| 379 384 | 
             
                      :predicate_keys=>:predicate_keys,
         | 
| @@ -432,7 +437,11 @@ module Sequel | |
| 432 437 | 
             
                      if use_placeholder_loader?
         | 
| 433 438 | 
             
                        cached_fetch(:placeholder_loader) do
         | 
| 434 439 | 
             
                          Sequel::Dataset::PlaceholderLiteralizer.loader(associated_dataset) do |pl, ds|
         | 
| 435 | 
            -
                            ds.where(Sequel.&(*predicate_keys.map{|k| SQL::BooleanExpression.new(:'=', k, pl.arg)}))
         | 
| 440 | 
            +
                            ds = ds.where(Sequel.&(*predicate_keys.map{|k| SQL::BooleanExpression.new(:'=', k, pl.arg)}))
         | 
| 441 | 
            +
                            if self[:block]
         | 
| 442 | 
            +
                              ds = self[:block].call(ds)
         | 
| 443 | 
            +
                            end
         | 
| 444 | 
            +
                            ds
         | 
| 436 445 | 
             
                          end
         | 
| 437 446 | 
             
                        end
         | 
| 438 447 | 
             
                      end
         | 
| @@ -796,7 +805,7 @@ module Sequel | |
| 796 805 |  | 
| 797 806 | 
             
                    # Whether the placeholder loader can be used to load the association.
         | 
| 798 807 | 
             
                    def use_placeholder_loader?
         | 
| 799 | 
            -
                       | 
| 808 | 
            +
                      self[:use_placeholder_loader]
         | 
| 800 809 | 
             
                    end
         | 
| 801 810 | 
             
                  end
         | 
| 802 811 |  | 
| @@ -1244,7 +1253,9 @@ module Sequel | |
| 1244 1253 | 
             
                        else
         | 
| 1245 1254 | 
             
                          assoc_record.values.delete(left_key_alias)
         | 
| 1246 1255 | 
             
                        end
         | 
| 1247 | 
            -
             | 
| 1256 | 
            +
             | 
| 1257 | 
            +
                        objects = h[hash_key]
         | 
| 1258 | 
            +
             | 
| 1248 1259 | 
             
                        if assign_singular
         | 
| 1249 1260 | 
             
                          objects.each do |object| 
         | 
| 1250 1261 | 
             
                            object.associations[name] ||= assoc_record
         | 
| @@ -1791,11 +1802,12 @@ module Sequel | |
| 1791 1802 | 
             
                      opts.merge!(:type => type, :name => name, :cache=>({} if cache_associations), :model => self)
         | 
| 1792 1803 |  | 
| 1793 1804 | 
             
                      opts[:block] = block if block
         | 
| 1794 | 
            -
                       | 
| 1805 | 
            +
                      opts[:instance_specific] = true if orig_opts[:dataset]
         | 
| 1806 | 
            +
                      if !opts.has_key?(:instance_specific) && (block || orig_opts[:block])
         | 
| 1795 1807 | 
             
                        # It's possible the association is instance specific, in that it depends on
         | 
| 1796 1808 | 
             
                        # values other than the foreign key value.  This needs to be checked for
         | 
| 1797 1809 | 
             
                        # in certain places to disable optimizations.
         | 
| 1798 | 
            -
                        opts[:instance_specific] =  | 
| 1810 | 
            +
                        opts[:instance_specific] = _association_instance_specific_default(name)
         | 
| 1799 1811 | 
             
                      end
         | 
| 1800 1812 | 
             
                      opts = assoc_class.new.merge!(opts)
         | 
| 1801 1813 |  | 
| @@ -1803,6 +1815,7 @@ module Sequel | |
| 1803 1815 | 
             
                        raise(Error, "cannot clone an association to an association of different type (association #{name} with type #{type} cloning #{opts[:clone]} with type #{cloned_assoc[:type]})")
         | 
| 1804 1816 | 
             
                      end
         | 
| 1805 1817 |  | 
| 1818 | 
            +
                      opts[:use_placeholder_loader] = !opts[:instance_specific] && !opts[:eager_graph]
         | 
| 1806 1819 | 
             
                      opts[:eager_block] = opts[:block] unless opts.include?(:eager_block)
         | 
| 1807 1820 | 
             
                      opts[:graph_join_type] ||= :left_outer
         | 
| 1808 1821 | 
             
                      opts[:order_eager_graph] = true unless opts.include?(:order_eager_graph)
         | 
| @@ -1899,6 +1912,12 @@ module Sequel | |
| 1899 1912 | 
             
                    Plugins.def_dataset_methods(self, [:eager, :eager_graph, :eager_graph_with_options, :association_join, :association_full_join, :association_inner_join, :association_left_join, :association_right_join])
         | 
| 1900 1913 |  | 
| 1901 1914 | 
             
                    private
         | 
| 1915 | 
            +
             | 
| 1916 | 
            +
                    # The default value for the instance_specific option, if the association
         | 
| 1917 | 
            +
                    # could be instance specific and the :instance_specific option is not specified.
         | 
| 1918 | 
            +
                    def _association_instance_specific_default(_)
         | 
| 1919 | 
            +
                      true
         | 
| 1920 | 
            +
                    end
         | 
| 1902 1921 |  | 
| 1903 1922 | 
             
                    # The module to use for the association's methods.  Defaults to
         | 
| 1904 1923 | 
             
                    # the overridable_methods_module.
         | 
| @@ -1948,10 +1967,8 @@ module Sequel | |
| 1948 1967 | 
             
                      if opts[:block]
         | 
| 1949 1968 | 
             
                        opts[:block_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_block", 1, &opts[:block])
         | 
| 1950 1969 | 
             
                      end
         | 
| 1951 | 
            -
                       | 
| 1952 | 
            -
             | 
| 1953 | 
            -
                        opts[:dataset_opt_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_dataset_opt", opts[:dataset_opt_arity], &opts[:dataset])
         | 
| 1954 | 
            -
                      end
         | 
| 1970 | 
            +
                      opts[:dataset_opt_arity] = opts[:dataset].arity == 0 ? 0 : 1
         | 
| 1971 | 
            +
                      opts[:dataset_opt_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_dataset_opt", opts[:dataset_opt_arity], &opts[:dataset])
         | 
| 1955 1972 | 
             
                      def_association_method(opts)
         | 
| 1956 1973 |  | 
| 1957 1974 | 
             
                      return if opts[:read_only]
         | 
| @@ -2122,9 +2139,7 @@ module Sequel | |
| 2122 2139 |  | 
| 2123 2140 | 
             
                        eager_load_results(opts, eo) do |assoc_record|
         | 
| 2124 2141 | 
             
                          hash_key = uses_cks ? pk_meths.map{|k| assoc_record.get_column_value(k)} : assoc_record.get_column_value(opts.primary_key_method)
         | 
| 2125 | 
            -
                           | 
| 2126 | 
            -
                            objects.each{|object| object.associations[name] = assoc_record}
         | 
| 2127 | 
            -
                          end
         | 
| 2142 | 
            +
                          h[hash_key].each{|object| object.associations[name] = assoc_record}
         | 
| 2128 2143 | 
             
                        end
         | 
| 2129 2144 | 
             
                      end
         | 
| 2130 2145 |  | 
| @@ -2171,7 +2186,7 @@ module Sequel | |
| 2171 2186 | 
             
                        eager_load_results(opts, eo) do |assoc_record|
         | 
| 2172 2187 | 
             
                          assoc_record.values.delete(delete_rn) if delete_rn
         | 
| 2173 2188 | 
             
                          hash_key = uses_cks ? km.map{|k| assoc_record.get_column_value(k)} : assoc_record.get_column_value(km)
         | 
| 2174 | 
            -
                           | 
| 2189 | 
            +
                          objects = h[hash_key]
         | 
| 2175 2190 | 
             
                          if assign_singular
         | 
| 2176 2191 | 
             
                            objects.each do |object| 
         | 
| 2177 2192 | 
             
                              unless object.associations[name]
         | 
| @@ -2966,8 +2981,8 @@ module Sequel | |
| 2966 2981 | 
             
                    # dataset.  If that association also has dependent associations, instead of a callable object,
         | 
| 2967 2982 | 
             
                    # use a hash with the callable object being the key, and the dependent association(s) as the value.
         | 
| 2968 2983 | 
             
                    # 
         | 
| 2969 | 
            -
                    # You can specify an alias  | 
| 2970 | 
            -
                    # an a Symbol for the  | 
| 2984 | 
            +
                    # You can specify an custom alias and/or join type on a per-association basis by providing an
         | 
| 2985 | 
            +
                    # Sequel::SQL::AliasedExpression object instead of an a Symbol for the association name.
         | 
| 2971 2986 | 
             
                    #
         | 
| 2972 2987 | 
             
                    # Examples:
         | 
| 2973 2988 | 
             
                    #
         | 
| @@ -2983,6 +2998,14 @@ module Sequel | |
| 2983 2998 | 
             
                    #   # FROM albums
         | 
| 2984 2999 | 
             
                    #   # LEFT OUTER JOIN artists AS a ON (a.id = albums.artist_id)
         | 
| 2985 3000 | 
             
                    #
         | 
| 3001 | 
            +
                    #   # For each album, eager_graph load the artist, using a specified alias
         | 
| 3002 | 
            +
                    #   # and custom join type
         | 
| 3003 | 
            +
                    #
         | 
| 3004 | 
            +
                    #   Album.eager_graph(Sequel[:artist].as(:a, join_type: :inner)).all
         | 
| 3005 | 
            +
                    #   # SELECT ...
         | 
| 3006 | 
            +
                    #   # FROM albums
         | 
| 3007 | 
            +
                    #   # INNER JOIN artists AS a ON (a.id = albums.artist_id)
         | 
| 3008 | 
            +
                    #
         | 
| 2986 3009 | 
             
                    #   # For each album, eager_graph load the artist and genre
         | 
| 2987 3010 | 
             
                    #   Album.eager_graph(:artist, :genre).all
         | 
| 2988 3011 | 
             
                    #   Album.eager_graph(:artist).eager_graph(:genre).all
         | 
| @@ -3056,6 +3079,8 @@ module Sequel | |
| 3056 3079 | 
             
                    #                    significantly slower in some cases (perhaps even the majority of cases), so you should
         | 
| 3057 3080 | 
             
                    #                    only use this if you have benchmarked that it is faster for your use cases.
         | 
| 3058 3081 | 
             
                    def eager_graph_with_options(associations, opts=OPTS)
         | 
| 3082 | 
            +
                      return self if associations.empty?
         | 
| 3083 | 
            +
             | 
| 3059 3084 | 
             
                      opts = opts.dup unless opts.frozen?
         | 
| 3060 3085 | 
             
                      associations = [associations] unless associations.is_a?(Array)
         | 
| 3061 3086 | 
             
                      ds = if eg = @opts[:eager_graph]
         | 
| @@ -3125,11 +3150,16 @@ module Sequel | |
| 3125 3150 | 
             
                    # ta :: table_alias used for the parent association
         | 
| 3126 3151 | 
             
                    # requirements :: an array, used as a stack for requirements
         | 
| 3127 3152 | 
             
                    # r :: association reflection for the current association, or an SQL::AliasedExpression
         | 
| 3128 | 
            -
                    #      with the reflection as the expression  | 
| 3153 | 
            +
                    #      with the reflection as the expression, the alias base as the alias (or nil to
         | 
| 3154 | 
            +
                    #      use the default alias), and an optional hash with a :join_type entry as the columns
         | 
| 3155 | 
            +
                    #      to use a custom join type.
         | 
| 3129 3156 | 
             
                    # *associations :: any associations dependent on this one
         | 
| 3130 3157 | 
             
                    def eager_graph_association(ds, model, ta, requirements, r, *associations)
         | 
| 3131 3158 | 
             
                      if r.is_a?(SQL::AliasedExpression)
         | 
| 3132 3159 | 
             
                        alias_base = r.alias
         | 
| 3160 | 
            +
                        if r.columns.is_a?(Hash)
         | 
| 3161 | 
            +
                          join_type = r.columns[:join_type]
         | 
| 3162 | 
            +
                        end
         | 
| 3133 3163 | 
             
                        r = r.expression
         | 
| 3134 3164 | 
             
                      else
         | 
| 3135 3165 | 
             
                        alias_base = r[:graph_alias_base]
         | 
| @@ -3152,7 +3182,7 @@ module Sequel | |
| 3152 3182 | 
             
                        raise Error, "Cannot eager_graph association when :conditions specified and not a hash or an array of pairs.  Specify :graph_conditions, :graph_only_conditions, or :graph_block for the association.  Model: #{r[:model]}, association: #{r[:name]}"
         | 
| 3153 3183 | 
             
                      end
         | 
| 3154 3184 |  | 
| 3155 | 
            -
                      ds = loader.call(:self=>ds, :table_alias=>assoc_table_alias, :implicit_qualifier=>(ta == ds.opts[:eager_graph][:master]) ? first_source : qualifier_from_alias_symbol(ta, first_source), :callback=>callback, :join_type=>local_opts[:join_type], :join_only=>local_opts[:join_only], :limit_strategy=>limit_strategy, :from_self_alias=>ds.opts[:eager_graph][:master])
         | 
| 3185 | 
            +
                      ds = loader.call(:self=>ds, :table_alias=>assoc_table_alias, :implicit_qualifier=>(ta == ds.opts[:eager_graph][:master]) ? first_source : qualifier_from_alias_symbol(ta, first_source), :callback=>callback, :join_type=>join_type || local_opts[:join_type], :join_only=>local_opts[:join_only], :limit_strategy=>limit_strategy, :from_self_alias=>ds.opts[:eager_graph][:master])
         | 
| 3156 3186 | 
             
                      if r[:order_eager_graph] && (order = r.fetch(:graph_order, r[:order]))
         | 
| 3157 3187 | 
             
                        ds = ds.order_append(*qualified_expression(order, assoc_table_alias))
         | 
| 3158 3188 | 
             
                      end
         | 
| @@ -3177,7 +3207,6 @@ module Sequel | |
| 3177 3207 | 
             
                    # requirements :: an array, used as a stack for requirements
         | 
| 3178 3208 | 
             
                    # *associations :: the associations to add to the graph
         | 
| 3179 3209 | 
             
                    def eager_graph_associations(ds, model, ta, requirements, *associations)
         | 
| 3180 | 
            -
                      return ds if associations.empty?
         | 
| 3181 3210 | 
             
                      associations.flatten.each do |association|
         | 
| 3182 3211 | 
             
                        ds = case association
         | 
| 3183 3212 | 
             
                        when Symbol, SQL::AliasedExpression
         | 
| @@ -3307,7 +3336,7 @@ module Sequel | |
| 3307 3336 | 
             
                          end
         | 
| 3308 3337 | 
             
                        end
         | 
| 3309 3338 |  | 
| 3310 | 
            -
                        SQL::AliasedExpression.new(check_association(model, expr), association.alias)
         | 
| 3339 | 
            +
                        SQL::AliasedExpression.new(check_association(model, expr), association.alias || expr, association.columns)
         | 
| 3311 3340 | 
             
                      else
         | 
| 3312 3341 | 
             
                        check_association(model, association)
         | 
| 3313 3342 | 
             
                      end
         | 
    
        data/lib/sequel/model/base.rb
    CHANGED
    
    | @@ -491,6 +491,11 @@ module Sequel | |
| 491 491 | 
             
                  # the module using a the camelized plugin name under Sequel::Plugins.
         | 
| 492 492 | 
             
                  def plugin(plugin, *args, &block)
         | 
| 493 493 | 
             
                    m = plugin.is_a?(Module) ? plugin : plugin_module(plugin)
         | 
| 494 | 
            +
             | 
| 495 | 
            +
                    if !m.respond_to?(:apply) && !m.respond_to?(:configure) && (!args.empty? || block)
         | 
| 496 | 
            +
                      Deprecation.deprecate("Plugin #{plugin} accepts no arguments or block, and passing arguments/block to it", "Remove arguments and block when loading the plugin")
         | 
| 497 | 
            +
                    end
         | 
| 498 | 
            +
             | 
| 494 499 | 
             
                    unless @plugins.include?(m)
         | 
| 495 500 | 
             
                      @plugins << m
         | 
| 496 501 | 
             
                      m.apply(self, *args, &block) if m.respond_to?(:apply)
         | 
| @@ -500,8 +505,10 @@ module Sequel | |
| 500 505 | 
             
                        dataset_extend(m::DatasetMethods, :create_class_methods=>false)
         | 
| 501 506 | 
             
                      end
         | 
| 502 507 | 
             
                    end
         | 
| 508 | 
            +
             | 
| 503 509 | 
             
                    m.configure(self, *args, &block) if m.respond_to?(:configure)
         | 
| 504 510 | 
             
                  end
         | 
| 511 | 
            +
                  ruby2_keywords(:plugin) if respond_to?(:ruby2_keywords, true)
         | 
| 505 512 |  | 
| 506 513 | 
             
                  # Returns primary key attribute hash.  If using a composite primary key
         | 
| 507 514 | 
             
                  # value such be an array with values for each primary key in the correct
         | 
| @@ -593,7 +600,7 @@ module Sequel | |
| 593 600 | 
             
                      @columns = superclass.instance_variable_get(:@columns)
         | 
| 594 601 | 
             
                      @db_schema = superclass.instance_variable_get(:@db_schema)
         | 
| 595 602 | 
             
                    else
         | 
| 596 | 
            -
                      @dataset = @dataset.with_extend(*@dataset_method_modules.reverse) | 
| 603 | 
            +
                      @dataset = @dataset.with_extend(*@dataset_method_modules.reverse)
         | 
| 597 604 | 
             
                      @db_schema = get_db_schema
         | 
| 598 605 | 
             
                    end
         | 
| 599 606 |  | 
| @@ -632,8 +639,7 @@ module Sequel | |
| 632 639 |  | 
| 633 640 | 
             
                  # Cache of setter methods to allow by default, in order to speed up mass assignment.
         | 
| 634 641 | 
             
                  def setter_methods
         | 
| 635 | 
            -
                     | 
| 636 | 
            -
                    @setter_methods = get_setter_methods
         | 
| 642 | 
            +
                    @setter_methods || (@setter_methods = get_setter_methods)
         | 
| 637 643 | 
             
                  end
         | 
| 638 644 |  | 
| 639 645 | 
             
                  # Returns name of primary table for the dataset. If the table for the dataset
         | 
| @@ -751,6 +757,7 @@ module Sequel | |
| 751 757 | 
             
                    else
         | 
| 752 758 | 
             
                      define_singleton_method(meth){|*args, &block| dataset.public_send(meth, *args, &block)}
         | 
| 753 759 | 
             
                    end
         | 
| 760 | 
            +
                    singleton_class.send(:ruby2_keywords, meth) if respond_to?(:ruby2_keywords, true)
         | 
| 754 761 | 
             
                  end
         | 
| 755 762 |  | 
| 756 763 | 
             
                  # Get the schema from the database, fall back on checking the columns
         | 
| @@ -820,7 +827,6 @@ module Sequel | |
| 820 827 | 
             
                    super
         | 
| 821 828 | 
             
                    ivs = subclass.instance_variables
         | 
| 822 829 | 
             
                    inherited_instance_variables.each do |iv, dup|
         | 
| 823 | 
            -
                      next if ivs.include?(iv)
         | 
| 824 830 | 
             
                      if (sup_class_value = instance_variable_get(iv)) && dup
         | 
| 825 831 | 
             
                        sup_class_value = case dup
         | 
| 826 832 | 
             
                        when :dup
         | 
| @@ -1116,7 +1122,7 @@ module Sequel | |
| 1116 1122 | 
             
                    when nil
         | 
| 1117 1123 | 
             
                      return false
         | 
| 1118 1124 | 
             
                    when Array
         | 
| 1119 | 
            -
                      return false if  | 
| 1125 | 
            +
                      return false if pkv.any?(&:nil?)
         | 
| 1120 1126 | 
             
                    end
         | 
| 1121 1127 |  | 
| 1122 1128 | 
             
                    (obj.class == model) && (obj.pk == pkv)
         | 
| @@ -2232,7 +2238,9 @@ module Sequel | |
| 2232 2238 | 
             
                plugin self
         | 
| 2233 2239 |  | 
| 2234 2240 | 
             
                singleton_class.send(:undef_method, :dup, :clone, :initialize_copy)
         | 
| 2241 | 
            +
                # :nocov:
         | 
| 2235 2242 | 
             
                if RUBY_VERSION >= '1.9.3'
         | 
| 2243 | 
            +
                # :nocov:
         | 
| 2236 2244 | 
             
                  singleton_class.send(:undef_method, :initialize_clone, :initialize_dup)
         | 
| 2237 2245 | 
             
                end
         | 
| 2238 2246 | 
             
              end
         | 
    
        data/lib/sequel/model/plugins.rb
    CHANGED
    
    | @@ -31,6 +31,7 @@ module Sequel | |
| 31 31 | 
             
                def self.def_dataset_methods(mod, meths)
         | 
| 32 32 | 
             
                  Array(meths).each do |meth|
         | 
| 33 33 | 
             
                    mod.class_eval("def #{meth}(*args, &block); dataset.#{meth}(*args, &block) end", __FILE__, __LINE__)
         | 
| 34 | 
            +
                    mod.send(:ruby2_keywords, meth) if respond_to?(:ruby2_keywords, true)
         | 
| 34 35 | 
             
                  end
         | 
| 35 36 | 
             
                end
         | 
| 36 37 |  | 
| @@ -149,9 +150,8 @@ module Sequel | |
| 149 150 | 
             
                  required_args = arity
         | 
| 150 151 | 
             
                  arity -= 1 if keyword == :required
         | 
| 151 152 |  | 
| 152 | 
            -
                   | 
| 153 | 
            -
             | 
| 154 | 
            -
                  end
         | 
| 153 | 
            +
                  # callable currently is always a non-lambda Proc
         | 
| 154 | 
            +
                  optional_args -= arity
         | 
| 155 155 |  | 
| 156 156 | 
             
                  [required_args, optional_args, rest, keyword]
         | 
| 157 157 | 
             
                end
         | 
| @@ -2,13 +2,17 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module Sequel
         | 
| 4 4 | 
             
              module Plugins
         | 
| 5 | 
            -
                # The association_pks plugin adds association_pks and | 
| 6 | 
            -
                # instance methods to the model class for each | 
| 7 | 
            -
                #  | 
| 8 | 
            -
                #  | 
| 5 | 
            +
                # The association_pks plugin adds association_pks, association_pks=, and
         | 
| 6 | 
            +
                # association_pks_dataset instance methods to the model class for each
         | 
| 7 | 
            +
                # one_to_many and many_to_many association added.  These methods allow for
         | 
| 8 | 
            +
                # easily returning the primary keys of the associated objects, and easily
         | 
| 9 | 
            +
                # modifying which objects are associated:
         | 
| 9 10 | 
             
                #
         | 
| 10 11 | 
             
                #   Artist.one_to_many :albums
         | 
| 11 12 | 
             
                #   artist = Artist[1]
         | 
| 13 | 
            +
                #   artist.album_pks_dataset
         | 
| 14 | 
            +
                #   # SELECT id FROM albums WHERE (albums.artist_id = 1)
         | 
| 15 | 
            +
                #
         | 
| 12 16 | 
             
                #   artist.album_pks # [1, 2, 3]
         | 
| 13 17 | 
             
                #   artist.album_pks = [2, 4]
         | 
| 14 18 | 
             
                #   artist.album_pks # [2, 4]
         | 
| @@ -22,11 +26,18 @@ module Sequel | |
| 22 26 | 
             
                # This plugin makes modifications directly to the underlying tables,
         | 
| 23 27 | 
             
                # it does not create or return any model objects, and therefore does
         | 
| 24 28 | 
             
                # not call any callbacks.  If you have any association callbacks,
         | 
| 25 | 
            -
                # you probably should not use the setter methods.
         | 
| 29 | 
            +
                # you probably should not use the setter methods this plugin adds.
         | 
| 26 30 | 
             
                #
         | 
| 27 31 | 
             
                # By default, changes to the association will not happen until the object
         | 
| 28 | 
            -
                # is saved.  However, using the delay_pks: false option, you can have | 
| 29 | 
            -
                # changes made immediately when the association_pks setter method is called.
         | 
| 32 | 
            +
                # is saved.  However, using the delay_pks: false association option, you can have
         | 
| 33 | 
            +
                # the changes made immediately when the association_pks setter method is called.
         | 
| 34 | 
            +
                #
         | 
| 35 | 
            +
                # By default, repeated calls to the association_pks getter method will not be
         | 
| 36 | 
            +
                # cached, unless the setter method has been used and the delay_pks: false
         | 
| 37 | 
            +
                # association option is not used.  You can set caching of repeated calls to the
         | 
| 38 | 
            +
                # association_pks getter method using the :cache_pks association option.  You can
         | 
| 39 | 
            +
                # pass the :refresh option when calling the getter method to ignore any existing
         | 
| 40 | 
            +
                # cached values, similar to how the :refresh option works with associations.
         | 
| 30 41 | 
             
                #
         | 
| 31 42 | 
             
                # By default, if you pass a nil value to the setter, an exception will be raised.
         | 
| 32 43 | 
             
                # You can change this behavior by using the :association_pks_nil association option.
         | 
| @@ -60,9 +71,11 @@ module Sequel | |
| 60 71 |  | 
| 61 72 | 
             
                    # Define a association_pks method using the block for the association reflection 
         | 
| 62 73 | 
             
                    def def_association_pks_methods(opts)
         | 
| 74 | 
            +
                      association_module_def(opts[:pks_dataset_method], &opts[:pks_dataset])
         | 
| 75 | 
            +
             | 
| 63 76 | 
             
                      opts[:pks_getter_method] = :"#{singularize(opts[:name])}_pks_getter"
         | 
| 64 77 | 
             
                      association_module_def(opts[:pks_getter_method], &opts[:pks_getter])
         | 
| 65 | 
            -
                      association_module_def(:"#{singularize(opts[:name])}_pks", opts){_association_pks_getter(opts)}
         | 
| 78 | 
            +
                      association_module_def(:"#{singularize(opts[:name])}_pks", opts){|dynamic_opts=OPTS| _association_pks_getter(opts, dynamic_opts)}
         | 
| 66 79 |  | 
| 67 80 | 
             
                      if opts[:pks_setter]
         | 
| 68 81 | 
             
                        opts[:pks_setter_method] = :"#{singularize(opts[:name])}_pks_setter"
         | 
| @@ -84,7 +97,9 @@ module Sequel | |
| 84 97 | 
             
                      clpk = lpk.is_a?(Array)
         | 
| 85 98 | 
             
                      crk = rk.is_a?(Array)
         | 
| 86 99 |  | 
| 87 | 
            -
                      opts[: | 
| 100 | 
            +
                      dataset_method = opts[:pks_dataset_method] = :"#{singularize(opts[:name])}_pks_dataset"
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                      opts[:pks_dataset] = if join_associated_table = opts[:association_pks_use_associated_table]
         | 
| 88 103 | 
             
                        tname = opts[:join_table]
         | 
| 89 104 | 
             
                        lambda do
         | 
| 90 105 | 
             
                          cond = if clpk
         | 
| @@ -95,16 +110,26 @@ module Sequel | |
| 95 110 | 
             
                          rpk = opts.associated_class.primary_key
         | 
| 96 111 | 
             
                          opts.associated_dataset.
         | 
| 97 112 | 
             
                            naked.where(cond).
         | 
| 98 | 
            -
                             | 
| 113 | 
            +
                            select(*Sequel.public_send(rpk.is_a?(Array) ? :deep_qualify : :qualify, opts.associated_class.table_name, rpk))
         | 
| 99 114 | 
             
                        end
         | 
| 100 115 | 
             
                      elsif clpk
         | 
| 101 116 | 
             
                        lambda do
         | 
| 102 117 | 
             
                          cond = lk.zip(lpk).map{|k, pk| [k, get_column_value(pk)]}
         | 
| 103 | 
            -
                          _join_table_dataset(opts).where(cond). | 
| 118 | 
            +
                          _join_table_dataset(opts).where(cond).select(*rk)
         | 
| 119 | 
            +
                        end
         | 
| 120 | 
            +
                      else
         | 
| 121 | 
            +
                        lambda do
         | 
| 122 | 
            +
                          _join_table_dataset(opts).where(lk=>get_column_value(lpk)).select(*rk)
         | 
| 123 | 
            +
                        end
         | 
| 124 | 
            +
                      end
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                      opts[:pks_getter] = if join_associated_table = opts[:association_pks_use_associated_table]
         | 
| 127 | 
            +
                        lambda do
         | 
| 128 | 
            +
                          public_send(dataset_method).map(opts.associated_class.primary_key)
         | 
| 104 129 | 
             
                        end
         | 
| 105 130 | 
             
                      else
         | 
| 106 131 | 
             
                        lambda do
         | 
| 107 | 
            -
                           | 
| 132 | 
            +
                          public_send(dataset_method).map(rk)
         | 
| 108 133 | 
             
                        end
         | 
| 109 134 | 
             
                      end
         | 
| 110 135 |  | 
| @@ -145,8 +170,14 @@ module Sequel | |
| 145 170 |  | 
| 146 171 | 
             
                      key = opts[:key]
         | 
| 147 172 |  | 
| 173 | 
            +
                      dataset_method = opts[:pks_dataset_method] = :"#{singularize(opts[:name])}_pks_dataset"
         | 
| 174 | 
            +
             | 
| 175 | 
            +
                      opts[:pks_dataset] = lambda do
         | 
| 176 | 
            +
                        public_send(opts[:dataset_method]).select(*opts.associated_class.primary_key)
         | 
| 177 | 
            +
                      end
         | 
| 178 | 
            +
             | 
| 148 179 | 
             
                      opts[:pks_getter] = lambda do
         | 
| 149 | 
            -
                        public_send( | 
| 180 | 
            +
                        public_send(dataset_method).map(opts.associated_class.primary_key)
         | 
| 150 181 | 
             
                      end
         | 
| 151 182 |  | 
| 152 183 | 
             
                      unless opts[:read_only]
         | 
| @@ -207,12 +238,22 @@ module Sequel | |
| 207 238 | 
             
                    # Return the primary keys of the associated objects.
         | 
| 208 239 | 
             
                    # If the receiver is a new object, return any saved
         | 
| 209 240 | 
             
                    # pks, or an empty array if no pks have been saved.
         | 
| 210 | 
            -
                    def _association_pks_getter(opts)
         | 
| 241 | 
            +
                    def _association_pks_getter(opts, dynamic_opts=OPTS)
         | 
| 242 | 
            +
                      do_cache = opts[:cache_pks]
         | 
| 211 243 | 
             
                      delay = opts.fetch(:delay_pks, true)
         | 
| 212 | 
            -
                       | 
| 244 | 
            +
                      cache_or_delay = do_cache || delay
         | 
| 245 | 
            +
             | 
| 246 | 
            +
                      if dynamic_opts[:refresh] && @_association_pks
         | 
| 247 | 
            +
                        @_association_pks.delete(opts[:name])
         | 
| 248 | 
            +
                      end
         | 
| 249 | 
            +
             | 
| 250 | 
            +
                      if new? && cache_or_delay
         | 
| 213 251 | 
             
                        (@_association_pks ||= {})[opts[:name]] ||= []
         | 
| 214 | 
            -
                      elsif  | 
| 252 | 
            +
                      elsif cache_or_delay && @_association_pks && (objs = @_association_pks[opts[:name]])
         | 
| 215 253 | 
             
                        objs
         | 
| 254 | 
            +
                      elsif do_cache
         | 
| 255 | 
            +
                       # pks_getter_method is private
         | 
| 256 | 
            +
                        (@_association_pks ||= {})[opts[:name]] = send(opts[:pks_getter_method])
         | 
| 216 257 | 
             
                      else
         | 
| 217 258 | 
             
                       # pks_getter_method is private
         | 
| 218 259 | 
             
                        send(opts[:pks_getter_method])
         | 
| @@ -254,9 +295,10 @@ module Sequel | |
| 254 295 |  | 
| 255 296 | 
             
                      if primary_key.is_a?(Array)
         | 
| 256 297 | 
             
                        if (cols = sch.values_at(*klass.primary_key)).all? && (convs = cols.map{|c| c[:type] == :integer}).all?
         | 
| 298 | 
            +
                          db = model.db
         | 
| 257 299 | 
             
                          pks.map do |cpk|
         | 
| 258 | 
            -
                            cpk. | 
| 259 | 
            -
                               | 
| 300 | 
            +
                            cpk.map do |pk|
         | 
| 301 | 
            +
                              db.typecast_value(:integer, pk)
         | 
| 260 302 | 
             
                            end
         | 
| 261 303 | 
             
                          end
         | 
| 262 304 | 
             
                        else
         |