sequel 2.10.0 → 2.11.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.
- data/CHANGELOG +51 -1
- data/README.rdoc +2 -2
- data/Rakefile +2 -2
- data/doc/advanced_associations.rdoc +6 -18
- data/doc/release_notes/1.0.txt +38 -0
- data/doc/release_notes/1.1.txt +143 -0
- data/doc/release_notes/1.3.txt +101 -0
- data/doc/release_notes/1.4.0.txt +53 -0
- data/doc/release_notes/1.5.0.txt +155 -0
- data/doc/release_notes/2.0.0.txt +298 -0
- data/doc/release_notes/2.1.0.txt +271 -0
- data/doc/release_notes/2.10.0.txt +328 -0
- data/doc/release_notes/2.11.0.txt +215 -0
- data/doc/release_notes/2.2.0.txt +253 -0
- data/doc/release_notes/2.3.0.txt +88 -0
- data/doc/release_notes/2.4.0.txt +106 -0
- data/doc/release_notes/2.5.0.txt +137 -0
- data/doc/release_notes/2.6.0.txt +157 -0
- data/doc/release_notes/2.7.0.txt +166 -0
- data/doc/release_notes/2.8.0.txt +171 -0
- data/doc/release_notes/2.9.0.txt +97 -0
- data/lib/sequel_core/adapters/ado.rb +3 -0
- data/lib/sequel_core/adapters/db2.rb +0 -11
- data/lib/sequel_core/adapters/dbi.rb +0 -11
- data/lib/sequel_core/adapters/do.rb +0 -12
- data/lib/sequel_core/adapters/firebird.rb +21 -16
- data/lib/sequel_core/adapters/informix.rb +1 -11
- data/lib/sequel_core/adapters/jdbc.rb +1 -13
- data/lib/sequel_core/adapters/jdbc/h2.rb +3 -11
- data/lib/sequel_core/adapters/jdbc/mysql.rb +0 -17
- data/lib/sequel_core/adapters/jdbc/postgresql.rb +3 -15
- data/lib/sequel_core/adapters/mysql.rb +31 -27
- data/lib/sequel_core/adapters/odbc.rb +34 -28
- data/lib/sequel_core/adapters/openbase.rb +0 -11
- data/lib/sequel_core/adapters/oracle.rb +11 -9
- data/lib/sequel_core/adapters/postgres.rb +14 -17
- data/lib/sequel_core/adapters/shared/mssql.rb +6 -15
- data/lib/sequel_core/adapters/shared/mysql.rb +29 -14
- data/lib/sequel_core/adapters/shared/oracle.rb +4 -0
- data/lib/sequel_core/adapters/shared/postgres.rb +30 -35
- data/lib/sequel_core/adapters/shared/progress.rb +4 -0
- data/lib/sequel_core/adapters/shared/sqlite.rb +73 -13
- data/lib/sequel_core/adapters/sqlite.rb +8 -18
- data/lib/sequel_core/adapters/utils/date_format.rb +21 -0
- data/lib/sequel_core/{dataset → adapters/utils}/stored_procedures.rb +0 -0
- data/lib/sequel_core/{dataset → adapters/utils}/unsupported.rb +0 -0
- data/lib/sequel_core/core_ext.rb +1 -1
- data/lib/sequel_core/core_sql.rb +9 -4
- data/lib/sequel_core/database.rb +63 -62
- data/lib/sequel_core/dataset.rb +9 -4
- data/lib/sequel_core/dataset/convenience.rb +10 -9
- data/lib/sequel_core/dataset/prepared_statements.rb +1 -1
- data/lib/sequel_core/dataset/sql.rb +130 -36
- data/lib/sequel_core/schema/sql.rb +2 -2
- data/lib/sequel_core/sql.rb +44 -51
- data/lib/sequel_core/version.rb +1 -1
- data/lib/sequel_model/associations.rb +25 -17
- data/lib/sequel_model/base.rb +35 -7
- data/lib/sequel_model/caching.rb +1 -6
- data/lib/sequel_model/record.rb +23 -5
- data/lib/sequel_model/validations.rb +20 -5
- data/spec/adapters/firebird_spec.rb +6 -1
- data/spec/adapters/mysql_spec.rb +12 -0
- data/spec/adapters/postgres_spec.rb +2 -2
- data/spec/adapters/sqlite_spec.rb +81 -2
- data/spec/integration/dataset_test.rb +2 -2
- data/spec/integration/type_test.rb +12 -2
- data/spec/sequel_core/core_sql_spec.rb +46 -12
- data/spec/sequel_core/database_spec.rb +24 -12
- data/spec/sequel_core/dataset_spec.rb +82 -32
- data/spec/sequel_core/schema_spec.rb +16 -0
- data/spec/sequel_model/associations_spec.rb +89 -0
- data/spec/sequel_model/base_spec.rb +66 -0
- data/spec/sequel_model/eager_loading_spec.rb +32 -0
- data/spec/sequel_model/record_spec.rb +9 -9
- data/spec/sequel_model/spec_helper.rb +3 -0
- data/spec/sequel_model/validations_spec.rb +63 -3
- metadata +41 -4
| @@ -52,6 +52,9 @@ module Sequel | |
| 52 52 | 
             
                    db.translator.add_translator("real", &prok)
         | 
| 53 53 | 
             
                    db.translator.add_translator("double precision", &prok)
         | 
| 54 54 |  | 
| 55 | 
            +
                    # Handle blob values with Sequel::SQL::Blob
         | 
| 56 | 
            +
                    db.translator.add_translator("blob"){|t,v| ::Sequel::SQL::Blob.new(v)}
         | 
| 57 | 
            +
                    
         | 
| 55 58 | 
             
                    db
         | 
| 56 59 | 
             
                  end
         | 
| 57 60 |  | 
| @@ -156,7 +159,7 @@ module Sequel | |
| 156 159 | 
             
                    # SQLite uses a : before the name of the argument for named
         | 
| 157 160 | 
             
                    # arguments.
         | 
| 158 161 | 
             
                    def prepared_arg(k)
         | 
| 159 | 
            -
                      "#{prepared_arg_placeholder}#{k}" | 
| 162 | 
            +
                      LiteralString.new("#{prepared_arg_placeholder}#{k}")
         | 
| 160 163 | 
             
                    end
         | 
| 161 164 | 
             
                  end
         | 
| 162 165 |  | 
| @@ -211,23 +214,6 @@ module Sequel | |
| 211 214 | 
             
                    end
         | 
| 212 215 | 
             
                  end
         | 
| 213 216 |  | 
| 214 | 
            -
                  # Use the ISO format for dates and timestamps, and quote strings
         | 
| 215 | 
            -
                  # using the ::SQLite3::Database.quote method.
         | 
| 216 | 
            -
                  def literal(v)
         | 
| 217 | 
            -
                    case v
         | 
| 218 | 
            -
                    when LiteralString
         | 
| 219 | 
            -
                      v
         | 
| 220 | 
            -
                    when String
         | 
| 221 | 
            -
                      "'#{::SQLite3::Database.quote(v)}'"
         | 
| 222 | 
            -
                    when Time
         | 
| 223 | 
            -
                      literal(v.iso8601)
         | 
| 224 | 
            -
                    when Date, DateTime
         | 
| 225 | 
            -
                      literal(v.to_s)
         | 
| 226 | 
            -
                    else
         | 
| 227 | 
            -
                      super
         | 
| 228 | 
            -
                    end
         | 
| 229 | 
            -
                  end
         | 
| 230 | 
            -
                  
         | 
| 231 217 | 
             
                  # Prepare the given type of query with the given name and store
         | 
| 232 218 | 
             
                  # it in the database.  Note that a new native prepared statement is
         | 
| 233 219 | 
             
                  # created on each call to this prepared statement.
         | 
| @@ -240,6 +226,10 @@ module Sequel | |
| 240 226 |  | 
| 241 227 | 
             
                  private
         | 
| 242 228 |  | 
| 229 | 
            +
                  def literal_string(v)
         | 
| 230 | 
            +
                    "'#{::SQLite3::Database.quote(v)}'"
         | 
| 231 | 
            +
                  end
         | 
| 232 | 
            +
             | 
| 243 233 | 
             
                  # SQLite uses a : before the name of the argument as a placeholder.
         | 
| 244 234 | 
             
                  def prepared_arg_placeholder
         | 
| 245 235 | 
             
                    PREPARED_ARG_PLACEHOLDER
         | 
| @@ -0,0 +1,21 @@ | |
| 1 | 
            +
            # Module containing overrides for Sequel's standard date/time literalization
         | 
| 2 | 
            +
            # to use the SQL standrd.  The SQL standard is used by fewer databases than
         | 
| 3 | 
            +
            # the defacto standard (which is just a normal string).
         | 
| 4 | 
            +
            module Sequel::Dataset::SQLStandardDateFormat
         | 
| 5 | 
            +
              private
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              # Use SQL standard syntax for Date
         | 
| 8 | 
            +
              def literal_date(v)
         | 
| 9 | 
            +
                v.strftime("DATE '%Y-%m-%d'") 
         | 
| 10 | 
            +
              end
         | 
| 11 | 
            +
                
         | 
| 12 | 
            +
              # Use SQL standard syntax for DateTime
         | 
| 13 | 
            +
              def literal_datetime(v)
         | 
| 14 | 
            +
                v.strftime("TIMESTAMP '%Y-%m-%d %H:%M:%S'")
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              # Use SQL standard syntax for Time
         | 
| 18 | 
            +
              def literal_time(v)
         | 
| 19 | 
            +
                v.strftime("TIMESTAMP '%Y-%m-%d %H:%M:%S'")
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
            end
         | 
| 
            File without changes
         | 
| 
            File without changes
         | 
    
        data/lib/sequel_core/core_ext.rb
    CHANGED
    
    | @@ -56,7 +56,7 @@ class Module | |
| 56 56 | 
             
              # same name, caching the result in an instance variable.  Define
         | 
| 57 57 | 
             
              # standard attr_writer method for modifying that instance variable
         | 
| 58 58 | 
             
              def class_attr_overridable(*meths)
         | 
| 59 | 
            -
                meths.each{|meth| class_eval("def #{meth}; @#{meth} | 
| 59 | 
            +
                meths.each{|meth| class_eval("def #{meth}; !defined?(@#{meth}) ? (@#{meth} = self.class.#{meth}) : @#{meth} end")}
         | 
| 60 60 | 
             
                attr_writer(*meths) 
         | 
| 61 61 | 
             
                public(*meths) 
         | 
| 62 62 | 
             
                public(*meths.collect{|m|"#{m}="}) 
         | 
    
        data/lib/sequel_core/core_sql.rb
    CHANGED
    
    | @@ -121,7 +121,7 @@ class String | |
| 121 121 | 
             
              include Sequel::SQL::AliasMethods
         | 
| 122 122 | 
             
              include Sequel::SQL::CastMethods
         | 
| 123 123 |  | 
| 124 | 
            -
              # Converts a string into  | 
| 124 | 
            +
              # Converts a string into a Sequel::LiteralString, in order to override string
         | 
| 125 125 | 
             
              # literalization, e.g.:
         | 
| 126 126 | 
             
              #
         | 
| 127 127 | 
             
              #   DB[:items].filter(:abc => 'def').sql #=>
         | 
| @@ -130,8 +130,12 @@ class String | |
| 130 130 | 
             
              #   DB[:items].filter(:abc => 'def'.lit).sql #=>
         | 
| 131 131 | 
             
              #     "SELECT * FROM items WHERE (abc = def)"
         | 
| 132 132 | 
             
              #
         | 
| 133 | 
            -
               | 
| 134 | 
            -
             | 
| 133 | 
            +
              # You can also provide arguments, to create a Sequel::SQL::PlaceholderLiteralString:
         | 
| 134 | 
            +
              #
         | 
| 135 | 
            +
              #    DB[:items].select{|o| o.count('DISTINCT ?'.lit(:a))}.sql #=>
         | 
| 136 | 
            +
              #      "SELECT count(DISTINCT a) FROM items"
         | 
| 137 | 
            +
              def lit(*args)
         | 
| 138 | 
            +
                args.empty? ? Sequel::LiteralString.new(self) : Sequel::SQL::PlaceholderLiteralString.new(self, args)
         | 
| 135 139 | 
             
              end
         | 
| 136 140 | 
             
              alias_method :expr, :lit
         | 
| 137 141 |  | 
| @@ -149,9 +153,10 @@ class String | |
| 149 153 |  | 
| 150 154 | 
             
              # Returns a Blob that holds the same data as this string. Blobs provide proper
         | 
| 151 155 | 
             
              # escaping of binary data.
         | 
| 152 | 
            -
              def  | 
| 156 | 
            +
              def to_sequel_blob
         | 
| 153 157 | 
             
                ::Sequel::SQL::Blob.new self
         | 
| 154 158 | 
             
              end
         | 
| 159 | 
            +
              alias to_blob to_sequel_blob
         | 
| 155 160 | 
             
            end
         | 
| 156 161 |  | 
| 157 162 | 
             
            class Symbol
         | 
    
        data/lib/sequel_core/database.rb
    CHANGED
    
    | @@ -75,6 +75,9 @@ module Sequel | |
| 75 75 | 
             
                  @default_schema = opts.include?(:default_schema) ? opts[:default_schema] : default_schema_default
         | 
| 76 76 | 
             
                  @prepared_statements = {}
         | 
| 77 77 | 
             
                  @transactions = []
         | 
| 78 | 
            +
                  @identifier_input_method = nil
         | 
| 79 | 
            +
                  @identifier_output_method = nil
         | 
| 80 | 
            +
                  @quote_identifiers = nil
         | 
| 78 81 | 
             
                  if opts.include?(:upcase_identifiers)
         | 
| 79 82 | 
             
                    @identifier_input_method = opts[:upcase_identifiers] ? :upcase : ""
         | 
| 80 83 | 
             
                  end
         | 
| @@ -492,73 +495,71 @@ module Sequel | |
| 492 495 | 
             
                # is invalid.
         | 
| 493 496 | 
             
                def typecast_value(column_type, value)
         | 
| 494 497 | 
             
                  return nil if value.nil?
         | 
| 495 | 
            -
                   | 
| 496 | 
            -
             | 
| 497 | 
            -
                     | 
| 498 | 
            +
                  begin
         | 
| 499 | 
            +
                    case column_type
         | 
| 500 | 
            +
                    when :integer
         | 
| 498 501 | 
             
                      Integer(value)
         | 
| 499 | 
            -
                     | 
| 500 | 
            -
                       | 
| 501 | 
            -
                     | 
| 502 | 
            -
                  when :string
         | 
| 503 | 
            -
                    value.to_s
         | 
| 504 | 
            -
                  when :float
         | 
| 505 | 
            -
                    begin
         | 
| 502 | 
            +
                    when :string
         | 
| 503 | 
            +
                      value.to_s
         | 
| 504 | 
            +
                    when :float
         | 
| 506 505 | 
             
                      Float(value)
         | 
| 507 | 
            -
                     | 
| 508 | 
            -
                       | 
| 509 | 
            -
             | 
| 510 | 
            -
             | 
| 511 | 
            -
             | 
| 512 | 
            -
             | 
| 513 | 
            -
                       | 
| 514 | 
            -
             | 
| 515 | 
            -
                       | 
| 516 | 
            -
             | 
| 517 | 
            -
                       | 
| 518 | 
            -
                     | 
| 519 | 
            -
                       | 
| 520 | 
            -
             | 
| 521 | 
            -
             | 
| 522 | 
            -
             | 
| 523 | 
            -
             | 
| 524 | 
            -
                       | 
| 525 | 
            -
                     | 
| 526 | 
            -
                      value | 
| 527 | 
            -
             | 
| 528 | 
            -
             | 
| 529 | 
            -
             | 
| 530 | 
            -
             | 
| 531 | 
            -
                       | 
| 532 | 
            -
             | 
| 533 | 
            -
                       | 
| 534 | 
            -
             | 
| 535 | 
            -
                       | 
| 536 | 
            -
                     | 
| 537 | 
            -
                       | 
| 538 | 
            -
             | 
| 539 | 
            -
             | 
| 540 | 
            -
             | 
| 541 | 
            -
             | 
| 542 | 
            -
                       | 
| 543 | 
            -
             | 
| 544 | 
            -
                       | 
| 506 | 
            +
                    when :decimal
         | 
| 507 | 
            +
                      case value
         | 
| 508 | 
            +
                      when BigDecimal
         | 
| 509 | 
            +
                        value
         | 
| 510 | 
            +
                      when String, Float
         | 
| 511 | 
            +
                        value.to_d
         | 
| 512 | 
            +
                      when Integer
         | 
| 513 | 
            +
                        value.to_s.to_d
         | 
| 514 | 
            +
                      else
         | 
| 515 | 
            +
                        raise Sequel::Error::InvalidValue, "invalid value for BigDecimal: #{value.inspect}"
         | 
| 516 | 
            +
                      end
         | 
| 517 | 
            +
                    when :boolean
         | 
| 518 | 
            +
                      case value
         | 
| 519 | 
            +
                      when false, 0, "0", /\Af(alse)?\z/i
         | 
| 520 | 
            +
                        false
         | 
| 521 | 
            +
                      else
         | 
| 522 | 
            +
                        value.blank? ? nil : true
         | 
| 523 | 
            +
                      end
         | 
| 524 | 
            +
                    when :date
         | 
| 525 | 
            +
                      case value
         | 
| 526 | 
            +
                      when Date
         | 
| 527 | 
            +
                        value
         | 
| 528 | 
            +
                      when DateTime, Time
         | 
| 529 | 
            +
                        Date.new(value.year, value.month, value.day)
         | 
| 530 | 
            +
                      when String
         | 
| 531 | 
            +
                        value.to_date
         | 
| 532 | 
            +
                      else
         | 
| 533 | 
            +
                        raise Sequel::Error::InvalidValue, "invalid value for Date: #{value.inspect}"
         | 
| 534 | 
            +
                      end
         | 
| 535 | 
            +
                    when :time
         | 
| 536 | 
            +
                      case value
         | 
| 537 | 
            +
                      when Time
         | 
| 538 | 
            +
                        value
         | 
| 539 | 
            +
                      when String
         | 
| 540 | 
            +
                        value.to_time
         | 
| 541 | 
            +
                      else
         | 
| 542 | 
            +
                        raise Sequel::Error::InvalidValue, "invalid value for Time: #{value.inspect}"
         | 
| 543 | 
            +
                      end
         | 
| 544 | 
            +
                    when :datetime
         | 
| 545 | 
            +
                      raise(Sequel::Error::InvalidValue, "invalid value for Datetime: #{value.inspect}") unless value.is_one_of?(DateTime, Date, Time, String)
         | 
| 546 | 
            +
                      if Sequel.datetime_class === value
         | 
| 547 | 
            +
                        # Already the correct class, no need to convert
         | 
| 548 | 
            +
                        value
         | 
| 549 | 
            +
                      else
         | 
| 550 | 
            +
                        # First convert it to standard ISO 8601 time, then
         | 
| 551 | 
            +
                        # parse that string using the time class.
         | 
| 552 | 
            +
                        (Time === value ? value.iso8601 : value.to_s).to_sequel_time
         | 
| 553 | 
            +
                      end
         | 
| 554 | 
            +
                    when :blob
         | 
| 555 | 
            +
                      ::Sequel::SQL::Blob.new(value)
         | 
| 545 556 | 
             
                    else
         | 
| 546 | 
            -
                      raise Sequel::Error::InvalidValue, "invalid value for Time: #{value.inspect}"
         | 
| 547 | 
            -
                    end
         | 
| 548 | 
            -
                  when :datetime
         | 
| 549 | 
            -
                    raise(Sequel::Error::InvalidValue, "invalid value for Datetime: #{value.inspect}") unless value.is_one_of?(DateTime, Date, Time, String)
         | 
| 550 | 
            -
                    if Sequel.datetime_class === value
         | 
| 551 | 
            -
                      # Already the correct class, no need to convert
         | 
| 552 557 | 
             
                      value
         | 
| 553 | 
            -
                    else
         | 
| 554 | 
            -
                      # First convert it to standard ISO 8601 time, then
         | 
| 555 | 
            -
                      # parse that string using the time class.
         | 
| 556 | 
            -
                      (Time === value ? value.iso8601 : value.to_s).to_sequel_time
         | 
| 557 558 | 
             
                    end
         | 
| 558 | 
            -
                   | 
| 559 | 
            -
                     | 
| 560 | 
            -
             | 
| 561 | 
            -
                     | 
| 559 | 
            +
                  rescue ArgumentError => exp
         | 
| 560 | 
            +
                    e = Sequel::Error::InvalidValue.new("#{exp.class} #{exp.message}")
         | 
| 561 | 
            +
                    e.set_backtrace(exp.backtrace)
         | 
| 562 | 
            +
                    raise e
         | 
| 562 563 | 
             
                  end
         | 
| 563 564 | 
             
                end
         | 
| 564 565 |  | 
    
        data/lib/sequel_core/dataset.rb
    CHANGED
    
    | @@ -1,4 +1,4 @@ | |
| 1 | 
            -
            %w'callback convenience pagination prepared_statements query schema sql | 
| 1 | 
            +
            %w'callback convenience pagination prepared_statements query schema sql'.each do |f|
         | 
| 2 2 | 
             
              require "sequel_core/dataset/#{f}"
         | 
| 3 3 | 
             
            end
         | 
| 4 4 |  | 
| @@ -54,7 +54,7 @@ module Sequel | |
| 54 54 | 
             
                left_outer_join limit naked or order order_by order_more paginate query reject
         | 
| 55 55 | 
             
                reverse reverse_order right_outer_join select select_all select_more
         | 
| 56 56 | 
             
                set_defaults set_graph_aliases set_model set_overrides sort sort_by
         | 
| 57 | 
            -
                unfiltered union unordered where'.collect{|x| x.to_sym}
         | 
| 57 | 
            +
                unfiltered union unordered where with_sql'.collect{|x| x.to_sym}
         | 
| 58 58 |  | 
| 59 59 | 
             
                NOTIMPL_MSG = "This method must be overridden in Sequel adapters".freeze
         | 
| 60 60 | 
             
                STOCK_TRANSFORMS = {
         | 
| @@ -467,6 +467,11 @@ module Sequel | |
| 467 467 | 
             
                  end
         | 
| 468 468 | 
             
                end
         | 
| 469 469 |  | 
| 470 | 
            +
                # Set the server to use to :default unless it is already set in the passed opts
         | 
| 471 | 
            +
                def default_server_opts(opts)
         | 
| 472 | 
            +
                  {:server=>@opts[:server] || :default}.merge(opts)
         | 
| 473 | 
            +
                end
         | 
| 474 | 
            +
             | 
| 470 475 | 
             
                # Execute the given SQL on the database using execute.
         | 
| 471 476 | 
             
                def execute(sql, opts={}, &block)
         | 
| 472 477 | 
             
                  @db.execute(sql, {:server=>@opts[:server] || :read_only}.merge(opts), &block)
         | 
| @@ -474,12 +479,12 @@ module Sequel | |
| 474 479 |  | 
| 475 480 | 
             
                # Execute the given SQL on the database using execute_dui.
         | 
| 476 481 | 
             
                def execute_dui(sql, opts={}, &block)
         | 
| 477 | 
            -
                  @db.execute_dui(sql,  | 
| 482 | 
            +
                  @db.execute_dui(sql, default_server_opts(opts), &block)
         | 
| 478 483 | 
             
                end
         | 
| 479 484 |  | 
| 480 485 | 
             
                # Execute the given SQL on the database using execute_insert.
         | 
| 481 486 | 
             
                def execute_insert(sql, opts={}, &block)
         | 
| 482 | 
            -
                  @db.execute_insert(sql,  | 
| 487 | 
            +
                  @db.execute_insert(sql, default_server_opts(opts), &block)
         | 
| 483 488 | 
             
                end
         | 
| 484 489 |  | 
| 485 490 | 
             
                # Modify the identifier returned from the database based on the
         | 
| @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            module Sequel
         | 
| 2 2 | 
             
              class Dataset
         | 
| 3 3 | 
             
                COMMA_SEPARATOR = ', '.freeze
         | 
| 4 | 
            -
                COUNT_OF_ALL_AS_COUNT = SQL::Function.new(:count, '*'. | 
| 4 | 
            +
                COUNT_OF_ALL_AS_COUNT = SQL::Function.new(:count, LiteralString.new('*'.freeze)).as(:count)
         | 
| 5 5 |  | 
| 6 6 | 
             
                # Returns the first record matching the conditions.
         | 
| 7 7 | 
             
                def [](*conditions)
         | 
| @@ -16,7 +16,7 @@ module Sequel | |
| 16 16 |  | 
| 17 17 | 
             
                # Returns the average value for the given column.
         | 
| 18 18 | 
             
                def avg(column)
         | 
| 19 | 
            -
                  get | 
| 19 | 
            +
                  get{|o| o.avg(column)}
         | 
| 20 20 | 
             
                end
         | 
| 21 21 |  | 
| 22 22 | 
             
                # Returns true if no records exists in the dataset
         | 
| @@ -61,8 +61,9 @@ module Sequel | |
| 61 61 | 
             
                end
         | 
| 62 62 |  | 
| 63 63 | 
             
                # Return the column value for the first matching record in the dataset.
         | 
| 64 | 
            -
                def get(column)
         | 
| 65 | 
            -
                   | 
| 64 | 
            +
                def get(column=nil, &block)
         | 
| 65 | 
            +
                  raise(Error, 'must provide argument or block to Dataset#get, not both') if column && block
         | 
| 66 | 
            +
                  (column ? select(column) : select(&block)).single_value
         | 
| 66 67 | 
             
                end
         | 
| 67 68 |  | 
| 68 69 | 
             
                # Returns a dataset grouped by the given column with count by group.
         | 
| @@ -73,7 +74,7 @@ module Sequel | |
| 73 74 | 
             
                # Returns the interval between minimum and maximum values for the given 
         | 
| 74 75 | 
             
                # column.
         | 
| 75 76 | 
             
                def interval(column)
         | 
| 76 | 
            -
                  get | 
| 77 | 
            +
                  get{|o| o.max(column) - o.min(column)}
         | 
| 77 78 | 
             
                end
         | 
| 78 79 |  | 
| 79 80 | 
             
                # Reverses the order and then runs first.  Note that this
         | 
| @@ -97,12 +98,12 @@ module Sequel | |
| 97 98 |  | 
| 98 99 | 
             
                # Returns the maximum value for the given column.
         | 
| 99 100 | 
             
                def max(column)
         | 
| 100 | 
            -
                  get | 
| 101 | 
            +
                  get{|o| o.max(column)}
         | 
| 101 102 | 
             
                end
         | 
| 102 103 |  | 
| 103 104 | 
             
                # Returns the minimum value for the given column.
         | 
| 104 105 | 
             
                def min(column)
         | 
| 105 | 
            -
                  get | 
| 106 | 
            +
                  get{|o| o.min(column)}
         | 
| 106 107 | 
             
                end
         | 
| 107 108 |  | 
| 108 109 | 
             
                # Inserts multiple records into the associated table. This method can be
         | 
| @@ -170,7 +171,7 @@ module Sequel | |
| 170 171 | 
             
                # Returns a Range object made from the minimum and maximum values for the
         | 
| 171 172 | 
             
                # given column.
         | 
| 172 173 | 
             
                def range(column)
         | 
| 173 | 
            -
                  if r = select | 
| 174 | 
            +
                  if r = select{|o| [o.min(column).as(:v1), o.max(column).as(:v2)]}.first
         | 
| 174 175 | 
             
                    (r[:v1]..r[:v2])
         | 
| 175 176 | 
             
                  end
         | 
| 176 177 | 
             
                end
         | 
| @@ -191,7 +192,7 @@ module Sequel | |
| 191 192 |  | 
| 192 193 | 
             
                # Returns the sum for the given column.
         | 
| 193 194 | 
             
                def sum(column)
         | 
| 194 | 
            -
                  get | 
| 195 | 
            +
                  get{|o| o.sum(column)}
         | 
| 195 196 | 
             
                end
         | 
| 196 197 |  | 
| 197 198 | 
             
                # Returns true if the table exists.  Will raise an error
         | 
| @@ -7,13 +7,11 @@ module Sequel | |
| 7 7 | 
             
                COLUMN_REF_RE2 = /\A([\w ]+)___([\w ]+)\z/.freeze
         | 
| 8 8 | 
             
                COLUMN_REF_RE3 = /\A([\w ]+)__([\w ]+)\z/.freeze
         | 
| 9 9 | 
             
                COUNT_FROM_SELF_OPTS = [:distinct, :group, :sql, :limit, :compounds]
         | 
| 10 | 
            -
                DATE_FORMAT = "DATE '%Y-%m-%d'".freeze
         | 
| 11 10 | 
             
                N_ARITY_OPERATORS = ::Sequel::SQL::ComplexExpression::N_ARITY_OPERATORS
         | 
| 12 11 | 
             
                NULL = "NULL".freeze
         | 
| 13 12 | 
             
                QUESTION_MARK = '?'.freeze
         | 
| 14 | 
            -
                STOCK_COUNT_OPTS = {:select => ["COUNT(*)". | 
| 13 | 
            +
                STOCK_COUNT_OPTS = {:select => [LiteralString.new("COUNT(*)").freeze], :order => nil}.freeze
         | 
| 15 14 | 
             
                SELECT_CLAUSE_ORDER = %w'distinct columns from join where group having compounds order limit'.freeze
         | 
| 16 | 
            -
                TIMESTAMP_FORMAT = "TIMESTAMP '%Y-%m-%d %H:%M:%S'".freeze
         | 
| 17 15 | 
             
                TWO_ARITY_OPERATORS = ::Sequel::SQL::ComplexExpression::TWO_ARITY_OPERATORS
         | 
| 18 16 | 
             
                WILDCARD = '*'.freeze
         | 
| 19 17 |  | 
| @@ -45,6 +43,11 @@ module Sequel | |
| 45 43 | 
             
                  sql << "ELSE #{literal(ce.default)} END)"
         | 
| 46 44 | 
             
                end
         | 
| 47 45 |  | 
| 46 | 
            +
                # SQL fragment for the SQL CAST expression.
         | 
| 47 | 
            +
                def cast_sql(expr, type)
         | 
| 48 | 
            +
                  "CAST(#{literal(expr)} AS #{db.send(:type_literal_base, :type=>type)})"
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
             | 
| 48 51 | 
             
                # SQL fragment for specifying all columns in a given table.
         | 
| 49 52 | 
             
                def column_all_sql(ca)
         | 
| 50 53 | 
             
                  "#{quote_schema_table(ca.table)}.*"
         | 
| @@ -126,7 +129,7 @@ module Sequel | |
| 126 129 | 
             
                #   DB.select(1).where(DB[:items].exists).sql
         | 
| 127 130 | 
             
                #   #=> "SELECT 1 WHERE EXISTS (SELECT * FROM items)"
         | 
| 128 131 | 
             
                def exists(opts = nil)
         | 
| 129 | 
            -
                  "EXISTS (#{select_sql(opts)})" | 
| 132 | 
            +
                  LiteralString.new("EXISTS (#{select_sql(opts)})")
         | 
| 130 133 | 
             
                end
         | 
| 131 134 |  | 
| 132 135 | 
             
                # Returns a copy of the dataset with the given conditions imposed upon it.  
         | 
| @@ -184,8 +187,7 @@ module Sequel | |
| 184 187 | 
             
                alias_method :where, :filter
         | 
| 185 188 |  | 
| 186 189 | 
             
                # The first source (primary table) for this dataset.  If the dataset doesn't
         | 
| 187 | 
            -
                # have a table, raises an error.  If the table is aliased, returns the  | 
| 188 | 
            -
                # table name, not the alias.
         | 
| 190 | 
            +
                # have a table, raises an error.  If the table is aliased, returns the aliased name.
         | 
| 189 191 | 
             
                def first_source
         | 
| 190 192 | 
             
                  source = @opts[:from]
         | 
| 191 193 | 
             
                  if source.nil? || source.empty?
         | 
| @@ -464,38 +466,39 @@ module Sequel | |
| 464 466 | 
             
                # If an unsupported object is given, an exception is raised.
         | 
| 465 467 | 
             
                def literal(v)
         | 
| 466 468 | 
             
                  case v
         | 
| 467 | 
            -
                  when LiteralString
         | 
| 468 | 
            -
                    v
         | 
| 469 469 | 
             
                  when String
         | 
| 470 | 
            -
                     | 
| 471 | 
            -
             | 
| 472 | 
            -
             | 
| 470 | 
            +
                    return v if v.is_a?(LiteralString)
         | 
| 471 | 
            +
                    v.is_a?(SQL::Blob) ? literal_blob(v) : literal_string(v)
         | 
| 472 | 
            +
                  when Symbol
         | 
| 473 | 
            +
                    literal_symbol(v)
         | 
| 474 | 
            +
                  when Integer
         | 
| 475 | 
            +
                    literal_integer(v)
         | 
| 476 | 
            +
                  when Hash
         | 
| 477 | 
            +
                    literal_hash(v)
         | 
| 478 | 
            +
                  when SQL::Expression
         | 
| 479 | 
            +
                    literal_expression(v)
         | 
| 480 | 
            +
                  when Float
         | 
| 481 | 
            +
                    literal_float(v)
         | 
| 473 482 | 
             
                  when BigDecimal
         | 
| 474 | 
            -
                     | 
| 475 | 
            -
                    d = "'#{d}'" if v.nan? || v.infinite?
         | 
| 476 | 
            -
                    d
         | 
| 483 | 
            +
                    literal_big_decimal(v)
         | 
| 477 484 | 
             
                  when NilClass
         | 
| 478 485 | 
             
                    NULL
         | 
| 479 486 | 
             
                  when TrueClass
         | 
| 480 | 
            -
                     | 
| 487 | 
            +
                    literal_true
         | 
| 481 488 | 
             
                  when FalseClass
         | 
| 482 | 
            -
                     | 
| 483 | 
            -
                  when Symbol
         | 
| 484 | 
            -
                    symbol_to_column_ref(v)
         | 
| 485 | 
            -
                  when ::Sequel::SQL::Expression
         | 
| 486 | 
            -
                    v.to_s(self)
         | 
| 489 | 
            +
                    literal_false
         | 
| 487 490 | 
             
                  when Array
         | 
| 488 | 
            -
                     | 
| 489 | 
            -
                  when  | 
| 490 | 
            -
                     | 
| 491 | 
            -
                  when  | 
| 492 | 
            -
                    v | 
| 491 | 
            +
                    literal_array(v)
         | 
| 492 | 
            +
                  when Time
         | 
| 493 | 
            +
                    literal_time(v)
         | 
| 494 | 
            +
                  when DateTime
         | 
| 495 | 
            +
                    literal_datetime(v)
         | 
| 493 496 | 
             
                  when Date
         | 
| 494 | 
            -
                    v | 
| 497 | 
            +
                    literal_date(v)
         | 
| 495 498 | 
             
                  when Dataset
         | 
| 496 | 
            -
                     | 
| 499 | 
            +
                    literal_dataset(v)
         | 
| 497 500 | 
             
                  else
         | 
| 498 | 
            -
                     | 
| 501 | 
            +
                    literal_other(v)
         | 
| 499 502 | 
             
                  end
         | 
| 500 503 | 
             
                end
         | 
| 501 504 |  | 
| @@ -537,15 +540,17 @@ module Sequel | |
| 537 540 | 
             
                #   ds.order(:name.asc).sql #=> 'SELECT * FROM items ORDER BY name ASC'
         | 
| 538 541 | 
             
                #   ds.order(:arr|1).sql #=> 'SELECT * FROM items ORDER BY arr[1]'
         | 
| 539 542 | 
             
                #   ds.order(nil).sql #=> 'SELECT * FROM items'
         | 
| 540 | 
            -
                def order(* | 
| 541 | 
            -
                   | 
| 543 | 
            +
                def order(*columns)
         | 
| 544 | 
            +
                  columns += Array((yield SQL::VirtualRow.new)) if block_given?
         | 
| 545 | 
            +
                  clone(:order => (columns.compact.empty?) ? nil : columns)
         | 
| 542 546 | 
             
                end
         | 
| 543 547 | 
             
                alias_method :order_by, :order
         | 
| 544 548 |  | 
| 545 549 | 
             
                # Returns a copy of the dataset with the order columns added
         | 
| 546 550 | 
             
                # to the existing order.
         | 
| 547 | 
            -
                def order_more(* | 
| 548 | 
            -
                   | 
| 551 | 
            +
                def order_more(*columns)
         | 
| 552 | 
            +
                  columns += Array((yield SQL::VirtualRow.new)) if block_given?
         | 
| 553 | 
            +
                  order(*((@opts[:order] || []) + columns))
         | 
| 549 554 | 
             
                end
         | 
| 550 555 |  | 
| 551 556 | 
             
                # SQL fragment for the ordered expression, used in the ORDER BY
         | 
| @@ -621,6 +626,7 @@ module Sequel | |
| 621 626 | 
             
                # Returns a copy of the dataset with the columns selected changed
         | 
| 622 627 | 
             
                # to the given columns.
         | 
| 623 628 | 
             
                def select(*columns)
         | 
| 629 | 
            +
                  columns += Array((yield SQL::VirtualRow.new)) if block_given?
         | 
| 624 630 | 
             
                  clone(:select => columns)
         | 
| 625 631 | 
             
                end
         | 
| 626 632 |  | 
| @@ -632,6 +638,7 @@ module Sequel | |
| 632 638 | 
             
                # Returns a copy of the dataset with the given columns added
         | 
| 633 639 | 
             
                # to the existing selected columns.
         | 
| 634 640 | 
             
                def select_more(*columns)
         | 
| 641 | 
            +
                  columns += Array((yield SQL::VirtualRow.new)) if block_given?
         | 
| 635 642 | 
             
                  select(*((@opts[:select] || []) + columns))
         | 
| 636 643 | 
             
                end
         | 
| 637 644 |  | 
| @@ -666,9 +673,7 @@ module Sequel | |
| 666 673 | 
             
                #   :items__abc___a.to_column_ref(ds) #=> "items.abc AS a"
         | 
| 667 674 | 
             
                #
         | 
| 668 675 | 
             
                def symbol_to_column_ref(sym)
         | 
| 669 | 
            -
                   | 
| 670 | 
            -
                  qc = "#{"#{quote_identifier(c_table)}." if c_table}#{quote_identifier(column)}"
         | 
| 671 | 
            -
                  c_alias ? as_sql(qc, c_alias) : qc
         | 
| 676 | 
            +
                  literal_symbol(sym)
         | 
| 672 677 | 
             
                end
         | 
| 673 678 |  | 
| 674 679 | 
             
                # Returns a copy of the dataset with no filters (HAVING or WHERE clause) applied.
         | 
| @@ -737,6 +742,12 @@ module Sequel | |
| 737 742 | 
             
                  sql
         | 
| 738 743 | 
             
                end
         | 
| 739 744 |  | 
| 745 | 
            +
                # Returns a copy of the dataset with the static SQL used.  This is useful if you want
         | 
| 746 | 
            +
                # to keep the same row_proc/transform/graph, but change the SQL used to custom SQL.
         | 
| 747 | 
            +
                def with_sql(sql)
         | 
| 748 | 
            +
                  clone(:sql=>sql)
         | 
| 749 | 
            +
                end
         | 
| 750 | 
            +
             | 
| 740 751 | 
             
                [:inner, :full_outer, :right_outer, :left_outer].each do |jtype|
         | 
| 741 752 | 
             
                  class_eval("def #{jtype}_join(*args, &block); join_table(:#{jtype}, *args, &block) end")
         | 
| 742 753 | 
             
                end
         | 
| @@ -808,7 +819,7 @@ module Sequel | |
| 808 819 | 
             
                  when TrueClass, FalseClass
         | 
| 809 820 | 
             
                    SQL::BooleanExpression.new(:NOOP, expr)
         | 
| 810 821 | 
             
                  when String
         | 
| 811 | 
            -
                    "(#{expr})" | 
| 822 | 
            +
                    LiteralString.new("(#{expr})")
         | 
| 812 823 | 
             
                  else
         | 
| 813 824 | 
             
                    raise(Error, 'Invalid filter argument')
         | 
| 814 825 | 
             
                  end
         | 
| @@ -849,6 +860,89 @@ module Sequel | |
| 849 860 | 
             
                  "#{join_type.to_s.gsub('_', ' ').upcase} JOIN"
         | 
| 850 861 | 
             
                end
         | 
| 851 862 |  | 
| 863 | 
            +
                # SQL fragment for Array.  Treats as an expression if an array of all two pairs, or as a SQL array otherwise.
         | 
| 864 | 
            +
                def literal_array(v)
         | 
| 865 | 
            +
                  v.all_two_pairs? ? literal_expression(v.sql_expr) : array_sql(v)
         | 
| 866 | 
            +
                end
         | 
| 867 | 
            +
             | 
| 868 | 
            +
                # SQL fragment for BigDecimal
         | 
| 869 | 
            +
                def literal_big_decimal(v)
         | 
| 870 | 
            +
                  d = v.to_s("F")
         | 
| 871 | 
            +
                  v.nan? || v.infinite? ?  "'#{d}'" : d
         | 
| 872 | 
            +
                end
         | 
| 873 | 
            +
             | 
| 874 | 
            +
                # SQL fragment for SQL::Blob
         | 
| 875 | 
            +
                def literal_blob(v)
         | 
| 876 | 
            +
                  literal_string(v)
         | 
| 877 | 
            +
                end
         | 
| 878 | 
            +
             | 
| 879 | 
            +
                # SQL fragment for Dataset.  Does a subselect inside parantheses.
         | 
| 880 | 
            +
                def literal_dataset(v)
         | 
| 881 | 
            +
                  "(#{subselect_sql(v)})"
         | 
| 882 | 
            +
                end
         | 
| 883 | 
            +
             | 
| 884 | 
            +
                # SQL fragment for Date, using the ISO8601 format.
         | 
| 885 | 
            +
                def literal_date(v)
         | 
| 886 | 
            +
                  "'#{v}'"
         | 
| 887 | 
            +
                end
         | 
| 888 | 
            +
             | 
| 889 | 
            +
                # SQL fragment for DateTime, using the ISO8601 format.
         | 
| 890 | 
            +
                def literal_datetime(v)
         | 
| 891 | 
            +
                  "'#{v}'"
         | 
| 892 | 
            +
                end
         | 
| 893 | 
            +
             | 
| 894 | 
            +
                # SQL fragment for SQL::Expression, result depends on the specific type of expression.
         | 
| 895 | 
            +
                def literal_expression(v)
         | 
| 896 | 
            +
                  v.to_s(self)
         | 
| 897 | 
            +
                end
         | 
| 898 | 
            +
             | 
| 899 | 
            +
                # SQL fragment for false
         | 
| 900 | 
            +
                def literal_false
         | 
| 901 | 
            +
                  BOOL_FALSE
         | 
| 902 | 
            +
                end
         | 
| 903 | 
            +
             | 
| 904 | 
            +
                # SQL fragment for Float
         | 
| 905 | 
            +
                def literal_float(v)
         | 
| 906 | 
            +
                  v.to_s
         | 
| 907 | 
            +
                end
         | 
| 908 | 
            +
             | 
| 909 | 
            +
                # SQL fragment for Hash, treated as an expression
         | 
| 910 | 
            +
                def literal_hash(v)
         | 
| 911 | 
            +
                  literal_expression(v.sql_expr)
         | 
| 912 | 
            +
                end
         | 
| 913 | 
            +
             | 
| 914 | 
            +
                # SQL fragment for Integer
         | 
| 915 | 
            +
                def literal_integer(v)
         | 
| 916 | 
            +
                  v.to_s
         | 
| 917 | 
            +
                end
         | 
| 918 | 
            +
             | 
| 919 | 
            +
                # SQL fragmento for a type of object not handled by Dataset#literal.  Raises an error.  If a database specific type is allowed, this should be overriden in a subclass.
         | 
| 920 | 
            +
                def literal_other(v)
         | 
| 921 | 
            +
                  raise Error, "can't express #{v.inspect} as a SQL literal"
         | 
| 922 | 
            +
                end
         | 
| 923 | 
            +
             | 
| 924 | 
            +
                # SQL fragment for String.  Doubles \ and ' by default.
         | 
| 925 | 
            +
                def literal_string(v)
         | 
| 926 | 
            +
                  "'#{v.gsub(/\\/, "\\\\\\\\").gsub(/'/, "''")}'"
         | 
| 927 | 
            +
                end
         | 
| 928 | 
            +
             | 
| 929 | 
            +
                # SQL fragment for Symbol, treated as an identifier, possibly aliased and/or qualified.
         | 
| 930 | 
            +
                def literal_symbol(v)
         | 
| 931 | 
            +
                  c_table, column, c_alias = split_symbol(v)
         | 
| 932 | 
            +
                  qc = "#{"#{quote_identifier(c_table)}." if c_table}#{quote_identifier(column)}"
         | 
| 933 | 
            +
                  c_alias ? as_sql(qc, c_alias) : qc
         | 
| 934 | 
            +
                end
         | 
| 935 | 
            +
             | 
| 936 | 
            +
                # SQL fragment for Time, uses the ISO8601 format.
         | 
| 937 | 
            +
                def literal_time(v)
         | 
| 938 | 
            +
                  "'#{v.iso8601}'"
         | 
| 939 | 
            +
                end
         | 
| 940 | 
            +
             | 
| 941 | 
            +
                # SQL fragment for true.
         | 
| 942 | 
            +
                def literal_true
         | 
| 943 | 
            +
                  BOOL_TRUE
         | 
| 944 | 
            +
                end
         | 
| 945 | 
            +
             | 
| 852 946 | 
             
                # Returns a qualified column name (including a table name) if the column
         | 
| 853 947 | 
             
                # name isn't already qualified.
         | 
| 854 948 | 
             
                def qualified_column_name(column, table)
         |