sequel 3.26.0 → 3.27.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 +26 -0
 - data/Rakefile +2 -3
 - data/doc/mass_assignment.rdoc +54 -0
 - data/doc/migration.rdoc +9 -533
 - data/doc/prepared_statements.rdoc +8 -7
 - data/doc/release_notes/3.27.0.txt +82 -0
 - data/doc/schema_modification.rdoc +547 -0
 - data/doc/testing.rdoc +64 -0
 - data/lib/sequel/adapters/amalgalite.rb +4 -0
 - data/lib/sequel/adapters/jdbc.rb +3 -1
 - data/lib/sequel/adapters/jdbc/h2.rb +11 -5
 - data/lib/sequel/adapters/mysql.rb +4 -122
 - data/lib/sequel/adapters/mysql2.rb +4 -13
 - data/lib/sequel/adapters/odbc.rb +4 -1
 - data/lib/sequel/adapters/odbc/db2.rb +21 -0
 - data/lib/sequel/adapters/shared/mysql.rb +12 -0
 - data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +143 -0
 - data/lib/sequel/adapters/tinytds.rb +122 -3
 - data/lib/sequel/core.rb +4 -3
 - data/lib/sequel/database/misc.rb +7 -10
 - data/lib/sequel/dataset/misc.rb +1 -1
 - data/lib/sequel/dataset/sql.rb +7 -0
 - data/lib/sequel/model/associations.rb +2 -2
 - data/lib/sequel/model/base.rb +60 -10
 - data/lib/sequel/plugins/prepared_statements_safe.rb +17 -7
 - data/lib/sequel/sql.rb +5 -0
 - data/lib/sequel/timezones.rb +12 -3
 - data/lib/sequel/version.rb +1 -1
 - data/spec/adapters/mysql_spec.rb +25 -21
 - data/spec/core/database_spec.rb +200 -0
 - data/spec/core/dataset_spec.rb +6 -0
 - data/spec/extensions/prepared_statements_safe_spec.rb +10 -0
 - data/spec/extensions/schema_dumper_spec.rb +2 -2
 - data/spec/integration/schema_test.rb +30 -1
 - data/spec/integration/type_test.rb +10 -3
 - data/spec/model/base_spec.rb +44 -0
 - data/spec/model/model_spec.rb +14 -0
 - data/spec/model/record_spec.rb +131 -12
 - metadata +14 -4
 
| 
         @@ -32,9 +32,32 @@ module Sequel 
     | 
|
| 
       32 
32 
     | 
    
         
             
                      begin
         
     | 
| 
       33 
33 
     | 
    
         
             
                        m = opts[:return]
         
     | 
| 
       34 
34 
     | 
    
         
             
                        r = nil
         
     | 
| 
       35 
     | 
    
         
            -
                         
     | 
| 
       36 
     | 
    
         
            -
                           
     | 
| 
       37 
     | 
    
         
            -
                           
     | 
| 
      
 35 
     | 
    
         
            +
                        if (args = opts[:arguments]) && !args.empty?
         
     | 
| 
      
 36 
     | 
    
         
            +
                          types = []
         
     | 
| 
      
 37 
     | 
    
         
            +
                          values = []
         
     | 
| 
      
 38 
     | 
    
         
            +
                          args.each_with_index do |(k, v), i|
         
     | 
| 
      
 39 
     | 
    
         
            +
                            v, type = ps_arg_type(v)
         
     | 
| 
      
 40 
     | 
    
         
            +
                            types << "@#{k} #{type}"
         
     | 
| 
      
 41 
     | 
    
         
            +
                            values << "@#{k} = #{v}"
         
     | 
| 
      
 42 
     | 
    
         
            +
                          end
         
     | 
| 
      
 43 
     | 
    
         
            +
                          case m
         
     | 
| 
      
 44 
     | 
    
         
            +
                          when :do
         
     | 
| 
      
 45 
     | 
    
         
            +
                            sql = "#{sql}; SELECT @@ROWCOUNT AS AffectedRows"
         
     | 
| 
      
 46 
     | 
    
         
            +
                            single_value = true
         
     | 
| 
      
 47 
     | 
    
         
            +
                          when :insert
         
     | 
| 
      
 48 
     | 
    
         
            +
                            sql = "#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident"
         
     | 
| 
      
 49 
     | 
    
         
            +
                            single_value = true
         
     | 
| 
      
 50 
     | 
    
         
            +
                          end
         
     | 
| 
      
 51 
     | 
    
         
            +
                          sql = "EXEC sp_executesql N'#{c.escape(sql)}', N'#{c.escape(types.join(', '))}', #{values.join(', ')}"
         
     | 
| 
      
 52 
     | 
    
         
            +
                          log_yield(sql) do
         
     | 
| 
      
 53 
     | 
    
         
            +
                            r = c.execute(sql)
         
     | 
| 
      
 54 
     | 
    
         
            +
                            r.each{|row| return row.values.first} if single_value
         
     | 
| 
      
 55 
     | 
    
         
            +
                          end
         
     | 
| 
      
 56 
     | 
    
         
            +
                        else
         
     | 
| 
      
 57 
     | 
    
         
            +
                          log_yield(sql) do
         
     | 
| 
      
 58 
     | 
    
         
            +
                            r = c.execute(sql)
         
     | 
| 
      
 59 
     | 
    
         
            +
                            return r.send(m) if m
         
     | 
| 
      
 60 
     | 
    
         
            +
                          end
         
     | 
| 
       38 
61 
     | 
    
         
             
                        end
         
     | 
| 
       39 
62 
     | 
    
         
             
                        yield(r) if block_given?
         
     | 
| 
       40 
63 
     | 
    
         
             
                      rescue TinyTds::Error => e
         
     | 
| 
         @@ -84,14 +107,98 @@ module Sequel 
     | 
|
| 
       84 
107 
     | 
    
         
             
                    c.close
         
     | 
| 
       85 
108 
     | 
    
         
             
                  end
         
     | 
| 
       86 
109 
     | 
    
         | 
| 
      
 110 
     | 
    
         
            +
                  # Return true if the :conn argument is present and not active.
         
     | 
| 
       87 
111 
     | 
    
         
             
                  def disconnect_error?(e, opts)
         
     | 
| 
       88 
112 
     | 
    
         
             
                    super || (opts[:conn] && !opts[:conn].active?)
         
     | 
| 
       89 
113 
     | 
    
         
             
                  end
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                  # Return a 2 element array with the literal value and type to use
         
     | 
| 
      
 116 
     | 
    
         
            +
                  # in the prepared statement call for the given value and connection.
         
     | 
| 
      
 117 
     | 
    
         
            +
                  def ps_arg_type(v)
         
     | 
| 
      
 118 
     | 
    
         
            +
                    case v
         
     | 
| 
      
 119 
     | 
    
         
            +
                    when Fixnum
         
     | 
| 
      
 120 
     | 
    
         
            +
                      [v, 'int']
         
     | 
| 
      
 121 
     | 
    
         
            +
                    when Bignum
         
     | 
| 
      
 122 
     | 
    
         
            +
                      [v, 'bigint']
         
     | 
| 
      
 123 
     | 
    
         
            +
                    when Float
         
     | 
| 
      
 124 
     | 
    
         
            +
                      [v, 'double precision']
         
     | 
| 
      
 125 
     | 
    
         
            +
                    when Numeric
         
     | 
| 
      
 126 
     | 
    
         
            +
                      [v, 'numeric']
         
     | 
| 
      
 127 
     | 
    
         
            +
                    when SQLTime
         
     | 
| 
      
 128 
     | 
    
         
            +
                      [literal(v), 'time']
         
     | 
| 
      
 129 
     | 
    
         
            +
                    when DateTime, Time
         
     | 
| 
      
 130 
     | 
    
         
            +
                      [literal(v), 'datetime']
         
     | 
| 
      
 131 
     | 
    
         
            +
                    when Date
         
     | 
| 
      
 132 
     | 
    
         
            +
                      [literal(v), 'date']
         
     | 
| 
      
 133 
     | 
    
         
            +
                    when nil
         
     | 
| 
      
 134 
     | 
    
         
            +
                      ['NULL', 'nvarchar(max)']
         
     | 
| 
      
 135 
     | 
    
         
            +
                    when true
         
     | 
| 
      
 136 
     | 
    
         
            +
                      ['1', 'int']
         
     | 
| 
      
 137 
     | 
    
         
            +
                    when false
         
     | 
| 
      
 138 
     | 
    
         
            +
                      ['0', 'int']
         
     | 
| 
      
 139 
     | 
    
         
            +
                    when SQL::Blob
         
     | 
| 
      
 140 
     | 
    
         
            +
                      [literal(v), 'varbinary(max)']
         
     | 
| 
      
 141 
     | 
    
         
            +
                    else
         
     | 
| 
      
 142 
     | 
    
         
            +
                      [literal(v), 'nvarchar(max)']
         
     | 
| 
      
 143 
     | 
    
         
            +
                    end
         
     | 
| 
      
 144 
     | 
    
         
            +
                  end
         
     | 
| 
       90 
145 
     | 
    
         
             
                end
         
     | 
| 
       91 
146 
     | 
    
         | 
| 
       92 
147 
     | 
    
         
             
                class Dataset < Sequel::Dataset
         
     | 
| 
       93 
148 
     | 
    
         
             
                  include Sequel::MSSQL::DatasetMethods
         
     | 
| 
       94 
149 
     | 
    
         | 
| 
      
 150 
     | 
    
         
            +
                  # SQLite already supports named bind arguments, so use directly.
         
     | 
| 
      
 151 
     | 
    
         
            +
                  module ArgumentMapper
         
     | 
| 
      
 152 
     | 
    
         
            +
                    include Sequel::Dataset::ArgumentMapper
         
     | 
| 
      
 153 
     | 
    
         
            +
                    
         
     | 
| 
      
 154 
     | 
    
         
            +
                    protected
         
     | 
| 
      
 155 
     | 
    
         
            +
                    
         
     | 
| 
      
 156 
     | 
    
         
            +
                    # Return a hash with the same values as the given hash,
         
     | 
| 
      
 157 
     | 
    
         
            +
                    # but with the keys converted to strings.
         
     | 
| 
      
 158 
     | 
    
         
            +
                    def map_to_prepared_args(hash)
         
     | 
| 
      
 159 
     | 
    
         
            +
                      args = {}
         
     | 
| 
      
 160 
     | 
    
         
            +
                      hash.each{|k,v| args[k.to_s.gsub('.', '__')] = v}
         
     | 
| 
      
 161 
     | 
    
         
            +
                      args
         
     | 
| 
      
 162 
     | 
    
         
            +
                    end
         
     | 
| 
      
 163 
     | 
    
         
            +
                    
         
     | 
| 
      
 164 
     | 
    
         
            +
                    private
         
     | 
| 
      
 165 
     | 
    
         
            +
                    
         
     | 
| 
      
 166 
     | 
    
         
            +
                    # SQLite uses a : before the name of the argument for named
         
     | 
| 
      
 167 
     | 
    
         
            +
                    # arguments.
         
     | 
| 
      
 168 
     | 
    
         
            +
                    def prepared_arg(k)
         
     | 
| 
      
 169 
     | 
    
         
            +
                      LiteralString.new("@#{k.to_s.gsub('.', '__')}")
         
     | 
| 
      
 170 
     | 
    
         
            +
                    end
         
     | 
| 
      
 171 
     | 
    
         
            +
             
     | 
| 
      
 172 
     | 
    
         
            +
                    # Always assume a prepared argument.
         
     | 
| 
      
 173 
     | 
    
         
            +
                    def prepared_arg?(k)
         
     | 
| 
      
 174 
     | 
    
         
            +
                      true
         
     | 
| 
      
 175 
     | 
    
         
            +
                    end
         
     | 
| 
      
 176 
     | 
    
         
            +
                  end
         
     | 
| 
      
 177 
     | 
    
         
            +
                  
         
     | 
| 
      
 178 
     | 
    
         
            +
                  # SQLite prepared statement uses a new prepared statement each time
         
     | 
| 
      
 179 
     | 
    
         
            +
                  # it is called, but it does use the bind arguments.
         
     | 
| 
      
 180 
     | 
    
         
            +
                  module PreparedStatementMethods
         
     | 
| 
      
 181 
     | 
    
         
            +
                    include ArgumentMapper
         
     | 
| 
      
 182 
     | 
    
         
            +
                    
         
     | 
| 
      
 183 
     | 
    
         
            +
                    private
         
     | 
| 
      
 184 
     | 
    
         
            +
                    
         
     | 
| 
      
 185 
     | 
    
         
            +
                    # Run execute_select on the database with the given SQL and the stored
         
     | 
| 
      
 186 
     | 
    
         
            +
                    # bind arguments.
         
     | 
| 
      
 187 
     | 
    
         
            +
                    def execute(sql, opts={}, &block)
         
     | 
| 
      
 188 
     | 
    
         
            +
                      super(sql, {:arguments=>bind_arguments}.merge(opts), &block)
         
     | 
| 
      
 189 
     | 
    
         
            +
                    end
         
     | 
| 
      
 190 
     | 
    
         
            +
                    
         
     | 
| 
      
 191 
     | 
    
         
            +
                    # Same as execute, explicit due to intricacies of alias and super.
         
     | 
| 
      
 192 
     | 
    
         
            +
                    def execute_dui(sql, opts={}, &block)
         
     | 
| 
      
 193 
     | 
    
         
            +
                      super(sql, {:arguments=>bind_arguments}.merge(opts), &block)
         
     | 
| 
      
 194 
     | 
    
         
            +
                    end
         
     | 
| 
      
 195 
     | 
    
         
            +
                    
         
     | 
| 
      
 196 
     | 
    
         
            +
                    # Same as execute, explicit due to intricacies of alias and super.
         
     | 
| 
      
 197 
     | 
    
         
            +
                    def execute_insert(sql, opts={}, &block)
         
     | 
| 
      
 198 
     | 
    
         
            +
                      super(sql, {:arguments=>bind_arguments}.merge(opts), &block)
         
     | 
| 
      
 199 
     | 
    
         
            +
                    end
         
     | 
| 
      
 200 
     | 
    
         
            +
                  end
         
     | 
| 
      
 201 
     | 
    
         
            +
                  
         
     | 
| 
       95 
202 
     | 
    
         
             
                  # Yield hashes with symbol keys, attempting to optimize for
         
     | 
| 
       96 
203 
     | 
    
         
             
                  # various cases.
         
     | 
| 
       97 
204 
     | 
    
         
             
                  def fetch_rows(sql)
         
     | 
| 
         @@ -123,6 +230,18 @@ module Sequel 
     | 
|
| 
       123 
230 
     | 
    
         
             
                    self
         
     | 
| 
       124 
231 
     | 
    
         
             
                  end
         
     | 
| 
       125 
232 
     | 
    
         | 
| 
      
 233 
     | 
    
         
            +
                  # Create a named prepared statement that is stored in the
         
     | 
| 
      
 234 
     | 
    
         
            +
                  # database (and connection) for reuse.
         
     | 
| 
      
 235 
     | 
    
         
            +
                  def prepare(type, name=nil, *values)
         
     | 
| 
      
 236 
     | 
    
         
            +
                    ps = to_prepared_statement(type, values)
         
     | 
| 
      
 237 
     | 
    
         
            +
                    ps.extend(PreparedStatementMethods)
         
     | 
| 
      
 238 
     | 
    
         
            +
                    if name
         
     | 
| 
      
 239 
     | 
    
         
            +
                      ps.prepared_statement_name = name
         
     | 
| 
      
 240 
     | 
    
         
            +
                      db.prepared_statements[name] = ps
         
     | 
| 
      
 241 
     | 
    
         
            +
                    end
         
     | 
| 
      
 242 
     | 
    
         
            +
                    ps
         
     | 
| 
      
 243 
     | 
    
         
            +
                  end
         
     | 
| 
      
 244 
     | 
    
         
            +
                  
         
     | 
| 
       126 
245 
     | 
    
         
             
                  private
         
     | 
| 
       127 
246 
     | 
    
         | 
| 
       128 
247 
     | 
    
         
             
                  # Properly escape the given string +v+.
         
     | 
    
        data/lib/sequel/core.rb
    CHANGED
    
    | 
         @@ -228,12 +228,13 @@ module Sequel 
     | 
|
| 
       228 
228 
     | 
    
         
             
                end
         
     | 
| 
       229 
229 
     | 
    
         
             
              end
         
     | 
| 
       230 
230 
     | 
    
         | 
| 
       231 
     | 
    
         
            -
              # Converts the given +string+ into a  
     | 
| 
      
 231 
     | 
    
         
            +
              # Converts the given +string+ into a <tt>Sequel::SQLTime</tt> object.
         
     | 
| 
       232 
232 
     | 
    
         
             
              #
         
     | 
| 
       233 
     | 
    
         
            -
              #   Sequel.string_to_time('10:20:30') #  
     | 
| 
      
 233 
     | 
    
         
            +
              #   v = Sequel.string_to_time('10:20:30') # Sequel::SQLTime.parse('10:20:30')
         
     | 
| 
      
 234 
     | 
    
         
            +
              #   DB.literal(v) # => '10:20:30'
         
     | 
| 
       234 
235 
     | 
    
         
             
              def self.string_to_time(string)
         
     | 
| 
       235 
236 
     | 
    
         
             
                begin
         
     | 
| 
       236 
     | 
    
         
            -
                   
     | 
| 
      
 237 
     | 
    
         
            +
                  SQLTime.parse(string)
         
     | 
| 
       237 
238 
     | 
    
         
             
                rescue => e
         
     | 
| 
       238 
239 
     | 
    
         
             
                  raise convert_exception_class(e, InvalidValue)
         
     | 
| 
       239 
240 
     | 
    
         
             
                end
         
     | 
    
        data/lib/sequel/database/misc.rb
    CHANGED
    
    | 
         @@ -215,10 +215,10 @@ module Sequel 
     | 
|
| 
       215 
215 
     | 
    
         
             
                # Typecast the value to a Date
         
     | 
| 
       216 
216 
     | 
    
         
             
                def typecast_value_date(value)
         
     | 
| 
       217 
217 
     | 
    
         
             
                  case value
         
     | 
| 
       218 
     | 
    
         
            -
                  when Date
         
     | 
| 
       219 
     | 
    
         
            -
                    value
         
     | 
| 
       220 
218 
     | 
    
         
             
                  when DateTime, Time
         
     | 
| 
       221 
219 
     | 
    
         
             
                    Date.new(value.year, value.month, value.day)
         
     | 
| 
      
 220 
     | 
    
         
            +
                  when Date
         
     | 
| 
      
 221 
     | 
    
         
            +
                    value
         
     | 
| 
       222 
222 
     | 
    
         
             
                  when String
         
     | 
| 
       223 
223 
     | 
    
         
             
                    Sequel.string_to_date(value)
         
     | 
| 
       224 
224 
     | 
    
         
             
                  when Hash
         
     | 
| 
         @@ -230,12 +230,7 @@ module Sequel 
     | 
|
| 
       230 
230 
     | 
    
         | 
| 
       231 
231 
     | 
    
         
             
                # Typecast the value to a DateTime or Time depending on Sequel.datetime_class
         
     | 
| 
       232 
232 
     | 
    
         
             
                def typecast_value_datetime(value)
         
     | 
| 
       233 
     | 
    
         
            -
                   
     | 
| 
       234 
     | 
    
         
            -
                  if value.is_a?(Hash)
         
     | 
| 
       235 
     | 
    
         
            -
                    klass.send(klass == Time ? :mktime : :new, *[:year, :month, :day, :hour, :minute, :second].map{|x| (value[x] || value[x.to_s]).to_i})
         
     | 
| 
       236 
     | 
    
         
            -
                  else
         
     | 
| 
       237 
     | 
    
         
            -
                    Sequel.typecast_to_application_timestamp(value)
         
     | 
| 
       238 
     | 
    
         
            -
                  end
         
     | 
| 
      
 233 
     | 
    
         
            +
                  Sequel.typecast_to_application_timestamp(value)
         
     | 
| 
       239 
234 
     | 
    
         
             
                end
         
     | 
| 
       240 
235 
     | 
    
         | 
| 
       241 
236 
     | 
    
         
             
                # Typecast the value to a BigDecimal
         
     | 
| 
         @@ -280,13 +275,15 @@ module Sequel 
     | 
|
| 
       280 
275 
     | 
    
         
             
                # Typecast the value to a Time
         
     | 
| 
       281 
276 
     | 
    
         
             
                def typecast_value_time(value)
         
     | 
| 
       282 
277 
     | 
    
         
             
                  case value
         
     | 
| 
       283 
     | 
    
         
            -
                  when  
     | 
| 
      
 278 
     | 
    
         
            +
                  when SQLTime
         
     | 
| 
       284 
279 
     | 
    
         
             
                    value
         
     | 
| 
      
 280 
     | 
    
         
            +
                  when Time
         
     | 
| 
      
 281 
     | 
    
         
            +
                    SQLTime.local(value.year, value.month, value.day, value.hour, value.min, value.sec, value.respond_to?(:nsec) ? value.nsec : value.usec)
         
     | 
| 
       285 
282 
     | 
    
         
             
                  when String
         
     | 
| 
       286 
283 
     | 
    
         
             
                    Sequel.string_to_time(value)
         
     | 
| 
       287 
284 
     | 
    
         
             
                  when Hash
         
     | 
| 
       288 
285 
     | 
    
         
             
                    t = Time.now
         
     | 
| 
       289 
     | 
    
         
            -
                     
     | 
| 
      
 286 
     | 
    
         
            +
                    SQLTime.local(t.year, t.month, t.day, *[:hour, :minute, :second].map{|x| (value[x] || value[x.to_s]).to_i})
         
     | 
| 
       290 
287 
     | 
    
         
             
                  else
         
     | 
| 
       291 
288 
     | 
    
         
             
                    raise Sequel::InvalidValue, "invalid value for Time: #{value.inspect}"
         
     | 
| 
       292 
289 
     | 
    
         
             
                  end
         
     | 
    
        data/lib/sequel/dataset/misc.rb
    CHANGED
    
    | 
         @@ -107,7 +107,7 @@ module Sequel 
     | 
|
| 
       107 
107 
     | 
    
         
             
                # Define a hash value such that datasets with the same DB, opts, and SQL
         
     | 
| 
       108 
108 
     | 
    
         
             
                # will have the same hash value
         
     | 
| 
       109 
109 
     | 
    
         
             
                def hash
         
     | 
| 
       110 
     | 
    
         
            -
                  [db, opts.sort_by{|k| k.to_s}, sql].hash
         
     | 
| 
      
 110 
     | 
    
         
            +
                  [db, opts.sort_by{|k, v| k.to_s}, sql].hash
         
     | 
| 
       111 
111 
     | 
    
         
             
                end
         
     | 
| 
       112 
112 
     | 
    
         | 
| 
       113 
113 
     | 
    
         
             
                # The String instance method to call on identifiers before sending them to
         
     | 
    
        data/lib/sequel/dataset/sql.rb
    CHANGED
    
    | 
         @@ -100,6 +100,8 @@ module Sequel 
     | 
|
| 
       100 
100 
     | 
    
         
             
                    literal_false
         
     | 
| 
       101 
101 
     | 
    
         
             
                  when Array
         
     | 
| 
       102 
102 
     | 
    
         
             
                    literal_array(v)
         
     | 
| 
      
 103 
     | 
    
         
            +
                  when SQLTime
         
     | 
| 
      
 104 
     | 
    
         
            +
                    literal_sqltime(v)
         
     | 
| 
       103 
105 
     | 
    
         
             
                  when Time
         
     | 
| 
       104 
106 
     | 
    
         
             
                    literal_time(v)
         
     | 
| 
       105 
107 
     | 
    
         
             
                  when DateTime
         
     | 
| 
         @@ -723,6 +725,11 @@ module Sequel 
     | 
|
| 
       723 
725 
     | 
    
         
             
                  end
         
     | 
| 
       724 
726 
     | 
    
         
             
                end
         
     | 
| 
       725 
727 
     | 
    
         | 
| 
      
 728 
     | 
    
         
            +
                # SQL fragment for Sequel::SQLTime, containing just the time part
         
     | 
| 
      
 729 
     | 
    
         
            +
                def literal_sqltime(v)
         
     | 
| 
      
 730 
     | 
    
         
            +
                  v.strftime("'%H:%M:%S#{format_timestamp_usec(v.usec) if supports_timestamp_usecs?}'")
         
     | 
| 
      
 731 
     | 
    
         
            +
                end
         
     | 
| 
      
 732 
     | 
    
         
            +
             
     | 
| 
       726 
733 
     | 
    
         
             
                # SQL fragment for String.  Doubles \ and ' by default.
         
     | 
| 
       727 
734 
     | 
    
         
             
                def literal_string(v)
         
     | 
| 
       728 
735 
     | 
    
         
             
                  "'#{v.gsub(/\\/, "\\\\\\\\").gsub(/'/, "''")}'"
         
     | 
| 
         @@ -1037,8 +1037,8 @@ module Sequel 
     | 
|
| 
       1037 
1037 
     | 
    
         
             
                      @associations ||= {}
         
     | 
| 
       1038 
1038 
     | 
    
         
             
                    end
         
     | 
| 
       1039 
1039 
     | 
    
         | 
| 
       1040 
     | 
    
         
            -
                    #  
     | 
| 
       1041 
     | 
    
         
            -
                    # an Error if the model has no primary key.
         
     | 
| 
      
 1040 
     | 
    
         
            +
                    # Formally used internally by the associations code, like pk but doesn't raise
         
     | 
| 
      
 1041 
     | 
    
         
            +
                    # an Error if the model has no primary key.  Not used any longer, deprecated.
         
     | 
| 
       1042 
1042 
     | 
    
         
             
                    def pk_or_nil
         
     | 
| 
       1043 
1043 
     | 
    
         
             
                      key = primary_key
         
     | 
| 
       1044 
1044 
     | 
    
         
             
                      key.is_a?(Array) ? key.map{|k| @values[k]} : @values[key]
         
     | 
    
        data/lib/sequel/model/base.rb
    CHANGED
    
    | 
         @@ -148,6 +148,26 @@ module Sequel 
     | 
|
| 
       148 
148 
     | 
    
         
             
                  def dataset=(ds)
         
     | 
| 
       149 
149 
     | 
    
         
             
                    set_dataset(ds)
         
     | 
| 
       150 
150 
     | 
    
         
             
                  end
         
     | 
| 
      
 151 
     | 
    
         
            +
             
     | 
| 
      
 152 
     | 
    
         
            +
                  # Extend the dataset with an anonymous module, similar to adding
         
     | 
| 
      
 153 
     | 
    
         
            +
                  # a plugin with the methods defined in DatasetMethods.  If a block
         
     | 
| 
      
 154 
     | 
    
         
            +
                  # is given, it is module_evaled.
         
     | 
| 
      
 155 
     | 
    
         
            +
                  #
         
     | 
| 
      
 156 
     | 
    
         
            +
                  #   Artist.dataset_module do
         
     | 
| 
      
 157 
     | 
    
         
            +
                  #     def foo
         
     | 
| 
      
 158 
     | 
    
         
            +
                  #       :bar
         
     | 
| 
      
 159 
     | 
    
         
            +
                  #     end
         
     | 
| 
      
 160 
     | 
    
         
            +
                  #   end
         
     | 
| 
      
 161 
     | 
    
         
            +
                  #   Artist.dataset.foo
         
     | 
| 
      
 162 
     | 
    
         
            +
                  #   # => :bar
         
     | 
| 
      
 163 
     | 
    
         
            +
                  #   Artist.foo
         
     | 
| 
      
 164 
     | 
    
         
            +
                  #   # => :bar
         
     | 
| 
      
 165 
     | 
    
         
            +
                  def dataset_module
         
     | 
| 
      
 166 
     | 
    
         
            +
                    @dataset_module ||= Module.new
         
     | 
| 
      
 167 
     | 
    
         
            +
                    @dataset_module.module_eval(&Proc.new) if block_given?
         
     | 
| 
      
 168 
     | 
    
         
            +
                    dataset_extend(@dataset_module)
         
     | 
| 
      
 169 
     | 
    
         
            +
                    @dataset_module
         
     | 
| 
      
 170 
     | 
    
         
            +
                  end
         
     | 
| 
       151 
171 
     | 
    
         | 
| 
       152 
172 
     | 
    
         
             
                  # Returns the database associated with the Model class.
         
     | 
| 
       153 
173 
     | 
    
         
             
                  # If this model doesn't have a database associated with it,
         
     | 
| 
         @@ -248,6 +268,13 @@ module Sequel 
     | 
|
| 
       248 
268 
     | 
    
         
             
                    find(cond) || create(cond, &block)
         
     | 
| 
       249 
269 
     | 
    
         
             
                  end
         
     | 
| 
       250 
270 
     | 
    
         | 
| 
      
 271 
     | 
    
         
            +
                  # Clear the setter_methods cache when a module is included, as it
         
     | 
| 
      
 272 
     | 
    
         
            +
                  # may contain setter methods.
         
     | 
| 
      
 273 
     | 
    
         
            +
                  def include(mod)
         
     | 
| 
      
 274 
     | 
    
         
            +
                    clear_setter_methods_cache
         
     | 
| 
      
 275 
     | 
    
         
            +
                    super
         
     | 
| 
      
 276 
     | 
    
         
            +
                  end
         
     | 
| 
      
 277 
     | 
    
         
            +
              
         
     | 
| 
       251 
278 
     | 
    
         
             
                  # If possible, set the dataset for the model subclass as soon as it
         
     | 
| 
       252 
279 
     | 
    
         
             
                  # is created.  Also, make sure the inherited class instance variables
         
     | 
| 
       253 
280 
     | 
    
         
             
                  # are copied into the subclass.
         
     | 
| 
         @@ -328,12 +355,7 @@ module Sequel 
     | 
|
| 
       328 
355 
     | 
    
         
             
                      m.apply(self, *args, &blk) if m.respond_to?(:apply)
         
     | 
| 
       329 
356 
     | 
    
         
             
                      include(m::InstanceMethods) if plugin_module_defined?(m, :InstanceMethods)
         
     | 
| 
       330 
357 
     | 
    
         
             
                      extend(m::ClassMethods)if plugin_module_defined?(m, :ClassMethods)
         
     | 
| 
       331 
     | 
    
         
            -
                      if plugin_module_defined?(m, :DatasetMethods)
         
     | 
| 
       332 
     | 
    
         
            -
                        dataset.extend(m::DatasetMethods) if @dataset
         
     | 
| 
       333 
     | 
    
         
            -
                        dataset_method_modules << m::DatasetMethods
         
     | 
| 
       334 
     | 
    
         
            -
                        meths = m::DatasetMethods.public_instance_methods.reject{|x| NORMAL_METHOD_NAME_REGEXP !~ x.to_s}
         
     | 
| 
       335 
     | 
    
         
            -
                        def_dataset_method(*meths) unless meths.empty?
         
     | 
| 
       336 
     | 
    
         
            -
                      end
         
     | 
| 
      
 358 
     | 
    
         
            +
                      dataset_extend(m::DatasetMethods) if plugin_module_defined?(m, :DatasetMethods)
         
     | 
| 
       337 
359 
     | 
    
         
             
                    end
         
     | 
| 
       338 
360 
     | 
    
         
             
                    m.configure(self, *args, &blk) if m.respond_to?(:configure)
         
     | 
| 
       339 
361 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -553,6 +575,16 @@ module Sequel 
     | 
|
| 
       553 
575 
     | 
    
         
             
                    end
         
     | 
| 
       554 
576 
     | 
    
         
             
                  end
         
     | 
| 
       555 
577 
     | 
    
         | 
| 
      
 578 
     | 
    
         
            +
                  # Add the module to the class's dataset_method_modules.  Extend the dataset with the
         
     | 
| 
      
 579 
     | 
    
         
            +
                  # module if the model has a dataset.  Add dataset methods to the class for all
         
     | 
| 
      
 580 
     | 
    
         
            +
                  # public dataset methods.
         
     | 
| 
      
 581 
     | 
    
         
            +
                  def dataset_extend(mod)
         
     | 
| 
      
 582 
     | 
    
         
            +
                    dataset.extend(mod) if @dataset
         
     | 
| 
      
 583 
     | 
    
         
            +
                    dataset_method_modules << mod
         
     | 
| 
      
 584 
     | 
    
         
            +
                    meths = mod.public_instance_methods.reject{|x| NORMAL_METHOD_NAME_REGEXP !~ x.to_s}
         
     | 
| 
      
 585 
     | 
    
         
            +
                    def_dataset_method(*meths) unless meths.empty?
         
     | 
| 
      
 586 
     | 
    
         
            +
                  end
         
     | 
| 
      
 587 
     | 
    
         
            +
             
     | 
| 
       556 
588 
     | 
    
         
             
                  # Create a column accessor for a column with a method name that is hard to use in ruby code.
         
     | 
| 
       557 
589 
     | 
    
         
             
                  def def_bad_column_accessor(column)
         
     | 
| 
       558 
590 
     | 
    
         
             
                    overridable_methods_module.module_eval do
         
     | 
| 
         @@ -705,7 +737,7 @@ module Sequel 
     | 
|
| 
       705 
737 
     | 
    
         
             
                #   a model object, Sequel will call +around_destory+, which will call +before_destroy+, do
         
     | 
| 
       706 
738 
     | 
    
         
             
                #   the destroy, and then call +after_destroy+.
         
     | 
| 
       707 
739 
     | 
    
         
             
                # * The following instance_methods all call the class method of the same
         
     | 
| 
       708 
     | 
    
         
            -
                #   name: columns,  
     | 
| 
      
 740 
     | 
    
         
            +
                #   name: columns, db, primary_key, db_schema.
         
     | 
| 
       709 
741 
     | 
    
         
             
                # * All of the methods in +BOOLEAN_SETTINGS+ create attr_writers allowing you
         
     | 
| 
       710 
742 
     | 
    
         
             
                #   to set values for the attribute.  It also creates instnace getters returning
         
     | 
| 
       711 
743 
     | 
    
         
             
                #   the value of the setting.  If the value has not yet been set, it
         
     | 
| 
         @@ -889,14 +921,25 @@ module Sequel 
     | 
|
| 
       889 
921 
     | 
    
         | 
| 
       890 
922 
     | 
    
         
             
                  # Returns true when current instance exists, false otherwise.
         
     | 
| 
       891 
923 
     | 
    
         
             
                  # Generally an object that isn't new will exist unless it has
         
     | 
| 
       892 
     | 
    
         
            -
                  # been deleted.  Uses a database query to check for existence 
     | 
| 
      
 924 
     | 
    
         
            +
                  # been deleted.  Uses a database query to check for existence,
         
     | 
| 
      
 925 
     | 
    
         
            +
                  # unless the model object is new, in which case this is always
         
     | 
| 
      
 926 
     | 
    
         
            +
                  # false.
         
     | 
| 
       893 
927 
     | 
    
         
             
                  #
         
     | 
| 
       894 
928 
     | 
    
         
             
                  #   Artist[1].exists? # SELECT 1 FROM artists WHERE (id = 1)
         
     | 
| 
       895 
929 
     | 
    
         
             
                  #   # => true
         
     | 
| 
      
 930 
     | 
    
         
            +
                  #   Artist.new.exists?
         
     | 
| 
      
 931 
     | 
    
         
            +
                  #   # => false
         
     | 
| 
       896 
932 
     | 
    
         
             
                  def exists?
         
     | 
| 
       897 
     | 
    
         
            -
                    !this.get(1).nil?
         
     | 
| 
      
 933 
     | 
    
         
            +
                    new? ? false : !this.get(1).nil?
         
     | 
| 
       898 
934 
     | 
    
         
             
                  end
         
     | 
| 
       899 
935 
     | 
    
         | 
| 
      
 936 
     | 
    
         
            +
                  # Ignore the model's setter method cache when this instances extends a module, as the
         
     | 
| 
      
 937 
     | 
    
         
            +
                  # module may contain setter methods.
         
     | 
| 
      
 938 
     | 
    
         
            +
                  def extend(mod)
         
     | 
| 
      
 939 
     | 
    
         
            +
                    @singleton_setter_added = true
         
     | 
| 
      
 940 
     | 
    
         
            +
                    super
         
     | 
| 
      
 941 
     | 
    
         
            +
                  end
         
     | 
| 
      
 942 
     | 
    
         
            +
              
         
     | 
| 
       900 
943 
     | 
    
         
             
                  # Value that should be unique for objects with the same class and pk (if pk is not nil), or
         
     | 
| 
       901 
944 
     | 
    
         
             
                  # the same class and values (if pk is nil).
         
     | 
| 
       902 
945 
     | 
    
         
             
                  #
         
     | 
| 
         @@ -905,7 +948,14 @@ module Sequel 
     | 
|
| 
       905 
948 
     | 
    
         
             
                  #   Artist.new.hash == Artist.new.hash # true
         
     | 
| 
       906 
949 
     | 
    
         
             
                  #   Artist.new(:name=>'Bob').hash == Artist.new.hash # false
         
     | 
| 
       907 
950 
     | 
    
         
             
                  def hash
         
     | 
| 
       908 
     | 
    
         
            -
                     
     | 
| 
      
 951 
     | 
    
         
            +
                    case primary_key
         
     | 
| 
      
 952 
     | 
    
         
            +
                    when Array
         
     | 
| 
      
 953 
     | 
    
         
            +
                      [model, !pk.all? ? @values.sort_by{|k,v| k.to_s} : pk].hash
         
     | 
| 
      
 954 
     | 
    
         
            +
                    when Symbol
         
     | 
| 
      
 955 
     | 
    
         
            +
                      [model, pk.nil? ? @values.sort_by{|k,v| k.to_s} : pk].hash
         
     | 
| 
      
 956 
     | 
    
         
            +
                    else
         
     | 
| 
      
 957 
     | 
    
         
            +
                      [model, @values.sort_by{|k,v| k.to_s}].hash
         
     | 
| 
      
 958 
     | 
    
         
            +
                    end
         
     | 
| 
       909 
959 
     | 
    
         
             
                  end
         
     | 
| 
       910 
960 
     | 
    
         | 
| 
       911 
961 
     | 
    
         
             
                  # Returns value for the :id attribute, even if the primary key is
         
     | 
| 
         @@ -33,10 +33,16 @@ module Sequel 
     | 
|
| 
       33 
33 
     | 
    
         
             
                    # that can be created is 2^N (where N is the number of free columns).
         
     | 
| 
       34 
34 
     | 
    
         
             
                    attr_reader :prepared_statements_column_defaults
         
     | 
| 
       35 
35 
     | 
    
         | 
| 
       36 
     | 
    
         
            -
                    # Set the column defaults to use when creating on the subclass.
         
     | 
| 
       37 
36 
     | 
    
         
             
                    def inherited(subclass)
         
     | 
| 
       38 
37 
     | 
    
         
             
                      super
         
     | 
| 
       39 
     | 
    
         
            -
                      subclass. 
     | 
| 
      
 38 
     | 
    
         
            +
                      subclass.instance_variable_set(:@prepared_statements_column_defaults, @prepared_statements_column_defaults) if @prepared_statements_column_defaults && !subclass.prepared_statements_column_defaults
         
     | 
| 
      
 39 
     | 
    
         
            +
                    end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                    # Set the column defaults to use when creating on the subclass.
         
     | 
| 
      
 42 
     | 
    
         
            +
                    def set_dataset(*)
         
     | 
| 
      
 43 
     | 
    
         
            +
                      x = super
         
     | 
| 
      
 44 
     | 
    
         
            +
                      set_prepared_statements_column_defaults
         
     | 
| 
      
 45 
     | 
    
         
            +
                      x
         
     | 
| 
       40 
46 
     | 
    
         
             
                    end
         
     | 
| 
       41 
47 
     | 
    
         | 
| 
       42 
48 
     | 
    
         
             
                    private
         
     | 
| 
         @@ -45,11 +51,13 @@ module Sequel 
     | 
|
| 
       45 
51 
     | 
    
         
             
                    # are set to a default value unless they are a primary key column or
         
     | 
| 
       46 
52 
     | 
    
         
             
                    # they don't have a parseable default.
         
     | 
| 
       47 
53 
     | 
    
         
             
                    def set_prepared_statements_column_defaults
         
     | 
| 
       48 
     | 
    
         
            -
                       
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
                         
     | 
| 
      
 54 
     | 
    
         
            +
                      if db_schema
         
     | 
| 
      
 55 
     | 
    
         
            +
                        h = {}
         
     | 
| 
      
 56 
     | 
    
         
            +
                        db_schema.each do |k, v|
         
     | 
| 
      
 57 
     | 
    
         
            +
                          h[k] = v[:ruby_default] if (v[:ruby_default] || !v[:default]) && !v[:primary_key]
         
     | 
| 
      
 58 
     | 
    
         
            +
                        end
         
     | 
| 
      
 59 
     | 
    
         
            +
                        @prepared_statements_column_defaults = h
         
     | 
| 
       51 
60 
     | 
    
         
             
                      end
         
     | 
| 
       52 
     | 
    
         
            -
                      @prepared_statements_column_defaults = h
         
     | 
| 
       53 
61 
     | 
    
         
             
                    end
         
     | 
| 
       54 
62 
     | 
    
         
             
                  end
         
     | 
| 
       55 
63 
     | 
    
         | 
| 
         @@ -57,7 +65,9 @@ module Sequel 
     | 
|
| 
       57 
65 
     | 
    
         
             
                    # Merge the current values into the default values to reduce the number
         
     | 
| 
       58 
66 
     | 
    
         
             
                    # of free columns.
         
     | 
| 
       59 
67 
     | 
    
         
             
                    def before_create
         
     | 
| 
       60 
     | 
    
         
            -
                       
     | 
| 
      
 68 
     | 
    
         
            +
                      if v = model.prepared_statements_column_defaults
         
     | 
| 
      
 69 
     | 
    
         
            +
                        set_values(v.merge(values))
         
     | 
| 
      
 70 
     | 
    
         
            +
                      end
         
     | 
| 
       61 
71 
     | 
    
         
             
                      super
         
     | 
| 
       62 
72 
     | 
    
         
             
                    end
         
     | 
| 
       63 
73 
     | 
    
         | 
    
        data/lib/sequel/sql.rb
    CHANGED
    
    | 
         @@ -38,6 +38,11 @@ module Sequel 
     | 
|
| 
       38 
38 
     | 
    
         
             
              class LiteralString < ::String
         
     | 
| 
       39 
39 
     | 
    
         
             
              end
         
     | 
| 
       40 
40 
     | 
    
         | 
| 
      
 41 
     | 
    
         
            +
              # Time subclass that gets literalized with only the time value, so it operates
         
     | 
| 
      
 42 
     | 
    
         
            +
              # like a standard SQL time type.
         
     | 
| 
      
 43 
     | 
    
         
            +
              class SQLTime < ::Time
         
     | 
| 
      
 44 
     | 
    
         
            +
              end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
       41 
46 
     | 
    
         
             
              # The SQL module holds classes whose instances represent SQL fragments.
         
     | 
| 
       42 
47 
     | 
    
         
             
              # It also holds modules that are included in core ruby classes that
         
     | 
| 
       43 
48 
     | 
    
         
             
              # make Sequel a friendly DSL.
         
     | 
    
        data/lib/sequel/timezones.rb
    CHANGED
    
    | 
         @@ -109,6 +109,8 @@ module Sequel 
     | 
|
| 
       109 
109 
     | 
    
         
             
                    else
         
     | 
| 
       110 
110 
     | 
    
         
             
                      Time.send(input_timezone == :utc ? :utc : :local, y, mo, d, h, mi, s)
         
     | 
| 
       111 
111 
     | 
    
         
             
                    end
         
     | 
| 
      
 112 
     | 
    
         
            +
                  when Hash
         
     | 
| 
      
 113 
     | 
    
         
            +
                    convert_input_timestamp([:year, :month, :day, :hour, :minute, :second].map{|x| (v[x] || v[x.to_s]).to_i}, input_timezone)
         
     | 
| 
       112 
114 
     | 
    
         
             
                  when Time
         
     | 
| 
       113 
115 
     | 
    
         
             
                    if datetime_class == DateTime
         
     | 
| 
       114 
116 
     | 
    
         
             
                      v.respond_to?(:to_datetime) ? v.to_datetime : string_to_datetime(v.iso8601)
         
     | 
| 
         @@ -121,8 +123,6 @@ module Sequel 
     | 
|
| 
       121 
123 
     | 
    
         
             
                    else
         
     | 
| 
       122 
124 
     | 
    
         
             
                      v.respond_to?(:to_time) ? v.to_time : string_to_datetime(v.to_s)
         
     | 
| 
       123 
125 
     | 
    
         
             
                    end
         
     | 
| 
       124 
     | 
    
         
            -
                  when Date
         
     | 
| 
       125 
     | 
    
         
            -
                    convert_input_timestamp(v.to_s, input_timezone)
         
     | 
| 
       126 
126 
     | 
    
         
             
                  else
         
     | 
| 
       127 
127 
     | 
    
         
             
                    raise InvalidValue, "Invalid convert_input_timestamp type: #{v.inspect}"
         
     | 
| 
       128 
128 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -160,7 +160,16 @@ module Sequel 
     | 
|
| 
       160 
160 
     | 
    
         
             
                # +convert_output_timestamp+.
         
     | 
| 
       161 
161 
     | 
    
         
             
                def convert_timestamp(v, input_timezone)
         
     | 
| 
       162 
162 
     | 
    
         
             
                  begin
         
     | 
| 
       163 
     | 
    
         
            -
                     
     | 
| 
      
 163 
     | 
    
         
            +
                    if v.is_a?(Date) && !v.is_a?(DateTime)
         
     | 
| 
      
 164 
     | 
    
         
            +
                      # Dates handled specially as they are assumed to already be in the application_timezone
         
     | 
| 
      
 165 
     | 
    
         
            +
                      if datetime_class == DateTime
         
     | 
| 
      
 166 
     | 
    
         
            +
                        DateTime.civil(v.year, v.month, v.day, 0, 0, 0, application_timezone == :local ? (defined?(Rational) ? Rational(Time.local(v.year, v.month, v.day).utc_offset, 86400) : Time.local(v.year, v.month, v.day).utc_offset/86400.0) : 0)
         
     | 
| 
      
 167 
     | 
    
         
            +
                      else
         
     | 
| 
      
 168 
     | 
    
         
            +
                        Time.send(application_timezone == :utc ? :utc : :local, v.year, v.month, v.day)
         
     | 
| 
      
 169 
     | 
    
         
            +
                      end
         
     | 
| 
      
 170 
     | 
    
         
            +
                    else
         
     | 
| 
      
 171 
     | 
    
         
            +
                      convert_output_timestamp(convert_input_timestamp(v, input_timezone), application_timezone)
         
     | 
| 
      
 172 
     | 
    
         
            +
                    end
         
     | 
| 
       164 
173 
     | 
    
         
             
                  rescue InvalidValue
         
     | 
| 
       165 
174 
     | 
    
         
             
                    raise
         
     | 
| 
       166 
175 
     | 
    
         
             
                  rescue => e
         
     |