abstract_adapter.rb 20.7 KB
Newer Older
D
Initial  
David Heinemeier Hansson 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
require 'benchmark'
require 'date'

# Method that requires a library, ensuring that rubygems is loaded
# This is used in the database adaptors to require DB drivers. Reasons:
# (1) database drivers are the only third-party library that Rails depend upon
# (2) they are often installed as gems
def require_library_or_gem(library_name)
  begin
    require library_name
  rescue LoadError => cannot_require
    # 1. Requiring the module is unsuccessful, maybe it's a gem and nobody required rubygems yet. Try.
    begin
      require 'rubygems'
    rescue LoadError => rubygems_not_installed
      raise cannot_require
    end
    # 2. Rubygems is installed and loaded. Try to load the library again
19
    begin
D
Initial  
David Heinemeier Hansson 已提交
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
      require library_name
    rescue LoadError => gem_not_installed
      raise cannot_require
    end
  end
end

module ActiveRecord
  class Base
    class ConnectionSpecification #:nodoc:
      attr_reader :config, :adapter_method
      def initialize (config, adapter_method)
        @config, @adapter_method = config, adapter_method
      end
    end
35

D
Initial  
David Heinemeier Hansson 已提交
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
    # The class -> [adapter_method, config] map
    @@defined_connections = {}

    # Establishes the connection to the database. Accepts a hash as input where
    # the :adapter key must be specified with the name of a database adapter (in lower-case)
    # example for regular databases (MySQL, Postgresql, etc):
    #
    #   ActiveRecord::Base.establish_connection(
    #     :adapter  => "mysql",
    #     :host     => "localhost",
    #     :username => "myuser",
    #     :password => "mypass",
    #     :database => "somedatabase"
    #   )
    #
    # Example for SQLite database:
    #
    #   ActiveRecord::Base.establish_connection(
    #     :adapter => "sqlite",
    #     :dbfile  => "path/to/dbfile"
    #   )
    #
    # Also accepts keys as strings (for parsing from yaml for example):
    #   ActiveRecord::Base.establish_connection(
    #     "adapter" => "sqlite",
    #     "dbfile"  => "path/to/dbfile"
    #   )
    #
    # The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError
    # may be returned on an error.
66 67 68 69 70 71 72 73
    def self.establish_connection(spec = nil)
      case spec
        when nil
          raise AdapterNotSpecified unless defined? RAILS_ENV
          establish_connection(RAILS_ENV)
        when ConnectionSpecification
          @@defined_connections[self] = spec
        when Symbol, String
74 75 76
          if configuration = configurations[spec.to_s]
            establish_connection(configuration)
          else
77
            raise AdapterNotSpecified, "#{spec} database is not configured"
78
          end
79
        else
80
          spec = spec.symbolize_keys
81
          unless spec.key?(:adapter) then raise AdapterNotSpecified, "database configuration does not specify adapter" end
82
          adapter_method = "#{spec[:adapter]}_connection"
83
          unless respond_to?(adapter_method) then raise AdapterNotFound, "database configuration specifies nonexistent #{spec[:adapter]} adapter" end
84 85
          remove_connection
          establish_connection(ConnectionSpecification.new(spec, adapter_method))
D
Initial  
David Heinemeier Hansson 已提交
86 87 88
      end
    end

89 90 91 92 93 94 95 96
    def self.active_connections #:nodoc:
      if threaded_connections
        Thread.current['active_connections'] ||= {}
      else
        @@active_connections ||= {}
      end
    end

D
Initial  
David Heinemeier Hansson 已提交
97 98 99 100 101 102
    # Locate the connection of the nearest super class. This can be an
    # active or defined connections: if it is the latter, it will be
    # opened and set as the active connection for the class it was defined
    # for (not necessarily the current class).
    def self.retrieve_connection #:nodoc:
      klass = self
103 104
      ar_super = ActiveRecord::Base.superclass
      until klass == ar_super
105
        if conn = active_connections[klass]
106 107 108
          return conn
        elsif conn = @@defined_connections[klass]
          klass.connection = conn
D
Initial  
David Heinemeier Hansson 已提交
109 110 111 112 113 114
          return self.connection
        end
        klass = klass.superclass
      end
      raise ConnectionNotEstablished
    end
115

D
Initial  
David Heinemeier Hansson 已提交
116 117 118 119
    # Returns true if a connection that's accessible to this class have already been opened.
    def self.connected?
      klass = self
      until klass == ActiveRecord::Base.superclass
120
        if active_connections[klass]
121
          return true
D
Initial  
David Heinemeier Hansson 已提交
122 123 124 125 126 127
        else
          klass = klass.superclass
        end
      end
      return false
    end
128

D
Initial  
David Heinemeier Hansson 已提交
129 130 131 132 133 134 135
    # Remove the connection for this class. This will close the active
    # connection and the defined connection (if they exist). The result
    # can be used as argument for establish_connection, for easy
    # re-establishing of the connection.
    def self.remove_connection(klass=self)
      conn = @@defined_connections[klass]
      @@defined_connections.delete(klass)
136
      active_connections[klass] = nil
D
Initial  
David Heinemeier Hansson 已提交
137 138
      conn.config if conn
    end
139

D
Initial  
David Heinemeier Hansson 已提交
140 141 142 143
    # Set the connection for the class.
    def self.connection=(spec)
      raise ConnectionNotEstablished unless spec
      conn = self.send(spec.adapter_method, spec.config)
144
      active_connections[self] = conn
D
Initial  
David Heinemeier Hansson 已提交
145 146 147
    end

    # Converts all strings in a hash to symbols.
148
    def self.symbolize_strings_in_hash(hash) #:nodoc:
149
      hash.symbolize_keys
D
Initial  
David Heinemeier Hansson 已提交
150 151 152 153 154
    end
  end

  module ConnectionAdapters # :nodoc:
    class Column # :nodoc:
155
      attr_reader :name, :default, :type, :limit, :null
D
Initial  
David Heinemeier Hansson 已提交
156 157 158 159
      # The name should contain the name of the column, such as "name" in "name varchar(250)"
      # The default should contain the type-casted default of the column, such as 1 in "count int(11) DEFAULT 1"
      # The type parameter should either contain :integer, :float, :datetime, :date, :text, or :string
      # The sql_type is just used for extracting the limit, such as 10 in "varchar(10)"
160
      def initialize(name, default, sql_type = nil, null = true)
161 162 163
        @name, @type, @null = name, simplified_type(sql_type), null
        # have to do this one separately because type_cast depends on #type
        @default = type_cast(default)
D
Initial  
David Heinemeier Hansson 已提交
164 165 166 167 168 169 170 171 172
        @limit = extract_limit(sql_type) unless sql_type.nil?
      end

      def klass
        case type
          when :integer       then Fixnum
          when :float         then Float
          when :datetime      then Time
          when :date          then Date
173 174
          when :timestamp     then Time
          when :time          then Time
D
Initial  
David Heinemeier Hansson 已提交
175
          when :text, :string then String
176
          when :binary        then String
D
Initial  
David Heinemeier Hansson 已提交
177 178 179
          when :boolean       then Object
        end
      end
180

D
Initial  
David Heinemeier Hansson 已提交
181 182 183
      def type_cast(value)
        if value.nil? then return nil end
        case type
184 185
          when :string    then value
          when :text      then value
186
          when :integer   then value.to_i rescue value ? 1 : 0
187 188 189 190 191
          when :float     then value.to_f
          when :datetime  then string_to_time(value)
          when :timestamp then string_to_time(value)
          when :time      then string_to_dummy_time(value)
          when :date      then string_to_date(value)
192
          when :binary    then binary_to_string(value)
193
          when :boolean   then value == true or (value =~ /^t(rue)?$/i) == 0 or value.to_s == '1'
D
Initial  
David Heinemeier Hansson 已提交
194 195 196
          else value
        end
      end
197

D
Initial  
David Heinemeier Hansson 已提交
198 199 200
      def human_name
        Base.human_attribute_name(@name)
      end
201

202 203
      def string_to_binary(value)
        value
204
      end
205 206 207

      def binary_to_string(value)
        value
208
      end
D
Initial  
David Heinemeier Hansson 已提交
209 210 211

      private
        def string_to_date(string)
212 213
          return string unless string.is_a?(String)
          date_array = ParseDate.parsedate(string.to_s)
D
Initial  
David Heinemeier Hansson 已提交
214 215 216
          # treat 0000-00-00 as nil
          Date.new(date_array[0], date_array[1], date_array[2]) rescue nil
        end
217

D
Initial  
David Heinemeier Hansson 已提交
218
        def string_to_time(string)
219 220
          return string unless string.is_a?(String)
          time_array = ParseDate.parsedate(string.to_s).compact
D
Initial  
David Heinemeier Hansson 已提交
221
          # treat 0000-00-00 00:00:00 as nil
222
          Time.send(Base.default_timezone, *time_array) rescue nil
D
Initial  
David Heinemeier Hansson 已提交
223 224
        end

225
        def string_to_dummy_time(string)
226 227
          return string unless string.is_a?(String)
          time_array = ParseDate.parsedate(string.to_s)
228 229 230
          # pad the resulting array with dummy date information
          time_array[0] = 2000; time_array[1] = 1; time_array[2] = 1;
          Time.send(Base.default_timezone, *time_array) rescue nil
231 232
        end

D
Initial  
David Heinemeier Hansson 已提交
233 234 235
        def extract_limit(sql_type)
          $1.to_i if sql_type =~ /\((.*)\)/
        end
236

D
Initial  
David Heinemeier Hansson 已提交
237 238 239 240 241 242
        def simplified_type(field_type)
          case field_type
            when /int/i
              :integer
            when /float|double|decimal|numeric/i
              :float
243
            when /datetime/i
D
Initial  
David Heinemeier Hansson 已提交
244
              :datetime
245 246 247 248
            when /timestamp/i
              :timestamp
            when /time/i
              :time
D
Initial  
David Heinemeier Hansson 已提交
249 250
            when /date/i
              :date
251
            when /clob/i, /text/i
D
Initial  
David Heinemeier Hansson 已提交
252
              :text
253 254
            when /blob/i, /binary/i
              :binary
D
Initial  
David Heinemeier Hansson 已提交
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
            when /char/i, /string/i
              :string
            when /boolean/i
              :boolean
          end
        end
    end

    # All the concrete database adapters follow the interface laid down in this class.
    # You can use this interface directly by borrowing the database connection from the Base with
    # Base.connection.
    class AbstractAdapter
      @@row_even = true

      def initialize(connection, logger = nil) # :nodoc:
        @connection, @logger = connection, logger
        @runtime = 0
      end

      # Returns an array of record hashes with the column names as a keys and fields as values.
      def select_all(sql, name = nil) end
276

D
Initial  
David Heinemeier Hansson 已提交
277 278 279
      # Returns a record hash with the column names as a keys and fields as values.
      def select_one(sql, name = nil) end

280 281 282 283 284 285 286 287 288 289 290 291 292
      # Returns a single value from a record
      def select_value(sql, name = nil)
        result = select_one(sql, name)
        result.nil? ? nil : result.values.first
      end

      # Returns an array of the values of the first column in a select:
      #   select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
      def select_values(sql, name = nil)
        result = select_all(sql, name)
        result.map{ |v| v.values.first }
      end

293
      # Returns an array of table names for the current database.
294
      # def tables(name = nil) end
295 296

      # Returns an array of indexes for the given table.
297
      # def indexes(table_name, name = nil) end
298

D
Initial  
David Heinemeier Hansson 已提交
299 300 301 302
      # Returns an array of column objects for the table specified by +table_name+.
      def columns(table_name, name = nil) end

      # Returns the last auto-generated ID from the affected table.
303
      def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) end
D
Initial  
David Heinemeier Hansson 已提交
304

305
      # Executes the update statement and returns the number of rows affected.
D
Initial  
David Heinemeier Hansson 已提交
306 307
      def update(sql, name = nil) end

308
      # Executes the delete statement and returns the number of rows affected.
D
Initial  
David Heinemeier Hansson 已提交
309 310 311 312 313 314 315 316 317
      def delete(sql, name = nil) end

      def reset_runtime # :nodoc:
        rt = @runtime
        @runtime = 0
        return rt
      end

      # Wrap a block in a transaction.  Returns result of block.
318
      def transaction(start_db_transaction = true)
D
Initial  
David Heinemeier Hansson 已提交
319 320
        begin
          if block_given?
321
            begin_db_transaction if start_db_transaction
D
Initial  
David Heinemeier Hansson 已提交
322
            result = yield
323
            commit_db_transaction if start_db_transaction
D
Initial  
David Heinemeier Hansson 已提交
324 325 326
            result
          end
        rescue Exception => database_transaction_rollback
327
          rollback_db_transaction if start_db_transaction
D
Initial  
David Heinemeier Hansson 已提交
328 329 330 331 332 333
          raise
        end
      end

      # Begins the transaction (and turns off auto-committing).
      def begin_db_transaction()    end
334 335

      # Commits the transaction (and turns on auto-committing).
D
Initial  
David Heinemeier Hansson 已提交
336 337
      def commit_db_transaction()   end

338
      # Rolls back the transaction (and turns on auto-committing). Must be done if the transaction block
D
Initial  
David Heinemeier Hansson 已提交
339 340 341
      # raises an exception or returns false.
      def rollback_db_transaction() end

342 343 344
      def quoted_true() "'t'" end
      def quoted_false() "'f'" end

D
Initial  
David Heinemeier Hansson 已提交
345 346
      def quote(value, column = nil)
        case value
347
          when String
348 349
            if column && column.type == :binary
              "'#{quote_string(column.string_to_binary(value))}'" # ' (for ruby-mode)
350 351
            elsif column && [:integer, :float].include?(column.type)
              value.to_s
352 353 354
            else
              "'#{quote_string(value)}'" # ' (for ruby-mode)
            end
355
          when NilClass              then "NULL"
356 357
          when TrueClass             then (column && column.type == :boolean ? quoted_true : "1")
          when FalseClass            then (column && column.type == :boolean ? quoted_false : "0")
358
          when Float, Fixnum, Bignum then value.to_s
359
          when Date                  then "'#{value.to_s}'"
360 361
          when Time, DateTime        then "'#{value.strftime("%Y-%m-%d %H:%M:%S")}'"
          else                            "'#{quote_string(value.to_yaml)}'"
D
Initial  
David Heinemeier Hansson 已提交
362 363 364 365 366 367 368 369
        end
      end

      def quote_string(s)
        s.gsub(/\\/, '\&\&').gsub(/'/, "''") # ' (for ruby-mode)
      end

      def quote_column_name(name)
370
        name
D
Initial  
David Heinemeier Hansson 已提交
371 372
      end

373 374 375 376 377
      # Returns the human-readable name of the adapter.  Use mixed case - one can always use downcase if needed.
      def adapter_name()
        'Abstract'
      end

D
Initial  
David Heinemeier Hansson 已提交
378 379 380
      # Returns a string of the CREATE TABLE SQL statements for recreating the entire structure of the database.
      def structure_dump() end

381 382 383
      def add_limit!(sql, options)
        return unless options
        add_limit_offset!(sql, options)
384
      end
385

386 387 388 389
      def add_limit_offset!(sql, options)
        return if options[:limit].nil?
        sql << " LIMIT #{options[:limit]}"
        sql << " OFFSET #{options[:offset]}" if options.has_key?(:offset) and !options[:offset].nil?
390 391
      end

392

393 394
      def initialize_schema_information
        begin
395
          execute "CREATE TABLE schema_info (version #{type_to_sql(:integer)})"
396
          execute "INSERT INTO schema_info (version) VALUES(0)"
397 398 399 400
        rescue ActiveRecord::StatementInvalid
          # Schema has been intialized
        end
      end
401 402 403 404 405 406 407 408 409 410 411

      def dump_schema_information
        begin
          if (current_schema = ActiveRecord::Migrator.current_version) > 0
            return "INSERT INTO schema_info (version) VALUES (#{current_schema});" 
          end
        rescue ActiveRecord::StatementInvalid 
          # No Schema Info
        end
      end

412 413 414 415 416
      def create_table(name, options = {})
        table_definition = TableDefinition.new(self)
        table_definition.primary_key(options[:primary_key] || "id") unless options[:id] == false

        yield table_definition
417 418
        create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
        create_sql << "#{name} ("
419 420 421
        create_sql << table_definition.to_sql
        create_sql << ") #{options[:options]}"
        execute create_sql
422 423 424 425 426 427 428
      end

      def drop_table(name)
        execute "DROP TABLE #{name}"
      end

      def add_column(table_name, column_name, type, options = {})
429 430
        add_column_sql = "ALTER TABLE #{table_name} ADD #{column_name} #{type_to_sql(type, options[:limit])}"
        add_column_options!(add_column_sql, options)
431 432
        execute(add_column_sql)
      end
433
      
434 435
      def remove_column(table_name, column_name)
        execute "ALTER TABLE #{table_name} DROP #{column_name}"
436 437 438 439
      end      

      def change_column(table_name, column_name, type, options = {})
        raise NotImplementedError, "change_column is not implemented"
440
      end
441
      
442 443 444 445
      def change_column_default(table_name, column_name, default)
        raise NotImplementedError, "change_column_default is not implemented"
      end
      
446 447 448 449
      def rename_column(table_name, column_name, new_column_name)
        raise NotImplementedError, "rename_column is not implemented"
      end

450 451 452 453 454 455 456
      # Create a new index on the given table. By default, it will be named
      # <code>"#{table_name}_#{column_name.to_a.first}_index"</code>, but you
      # can explicitly name the index by passing <code>:name => "..."</code>
      # as the last parameter. Unique indexes may be created by passing
      # <code>:unique => true</code>.
      def add_index(table_name, column_name, options = {})
        index_name = "#{table_name}_#{column_name.to_a.first}_index"
457

458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
        if Hash === options # legacy support, since this param was a string
          index_type = options[:unique] ? "UNIQUE" : ""
          index_name = options[:name] || index_name
        else
          index_type = options
        end

        execute "CREATE #{index_type} INDEX #{index_name} ON #{table_name} (#{column_name.to_a.join(", ")})"
      end

      # Remove the given index from the table.
      #
      #   remove_index :my_table, :column => :foo
      #   remove_index :my_table, :name => :my_index_on_foo
      #
      # The first version will remove the index named
      # <code>"#{my_table}_#{column}_index"</code> from the table. The
      # second removes the named column from the table.
      def remove_index(table_name, options = {})
        if Hash === options # legacy support
          if options[:column]
            index_name = "#{table_name}_#{options[:column]}_index"
          elsif options[:name]
            index_name = options[:name]
          else
            raise ArgumentError, "You must specify the index name"
          end
        else
          index_name = "#{table_name}_#{options}_index"
        end

        execute "DROP INDEX #{index_name} ON #{table_name}"
490 491 492 493 494 495 496 497 498 499 500
      end
      
      def supports_migrations?
        false
      end           

      def native_database_types
        {}
      end       

      def type_to_sql(type, limit = nil)
501
        native = native_database_types[type]
502 503 504 505 506
        limit ||= native[:limit]
        column_type_sql = native[:name]
        column_type_sql << "(#{limit})" if limit
        column_type_sql
      end            
507 508 509
      
      def add_column_options!(sql, options)
        sql << " NOT NULL" if options[:null] == false
510
        sql << " DEFAULT #{quote(options[:default], options[:column])}" unless options[:default].nil?
511
      end
512

513
      protected  
514
        def log(sql, name)
D
Initial  
David Heinemeier Hansson 已提交
515
          begin
516 517 518
            if block_given?
              if @logger and @logger.level <= Logger::INFO
                result = nil
519 520 521
                seconds = Benchmark.realtime { result = yield }
                @runtime += seconds
                log_info(sql, name, seconds)
522 523 524 525
                result
              else
                yield
              end
526 527 528
            else
              log_info(sql, name, 0)
              nil
D
Initial  
David Heinemeier Hansson 已提交
529
            end
530
          rescue Exception => e
D
Initial  
David Heinemeier Hansson 已提交
531 532 533 534 535 536
            log_info("#{e.message}: #{sql}", name, 0)
            raise ActiveRecord::StatementInvalid, "#{e.message}: #{sql}"
          end
        end

        def log_info(sql, name, runtime)
537
          return unless @logger
D
Initial  
David Heinemeier Hansson 已提交
538

539
          @logger.debug(
D
Initial  
David Heinemeier Hansson 已提交
540
            format_log_entry(
541
              "#{name.nil? ? "SQL" : name} (#{sprintf("%f", runtime)})",
D
Initial  
David Heinemeier Hansson 已提交
542 543 544 545 546 547
              sql.gsub(/ +/, " ")
            )
          )
        end

        def format_log_entry(message, dump = nil)
548 549 550 551 552 553
          if ActiveRecord::Base.colorize_logging
            if @@row_even then
              @@row_even = false; caller_color = "1;32"; message_color = "4;33"; dump_color = "1;37"
            else
              @@row_even = true; caller_color = "1;36"; message_color = "4;35"; dump_color = "0;37"
            end
554

555 556 557 558
            log_entry = "  \e[#{message_color}m#{message}\e[m"
            log_entry << "   \e[#{dump_color}m%s\e[m" % dump if dump.kind_of?(String) && !dump.nil?
            log_entry << "   \e[#{dump_color}m%p\e[m" % dump if !dump.kind_of?(String) && !dump.nil?
            log_entry
D
Initial  
David Heinemeier Hansson 已提交
559
          else
560
            "%s  %s" % [message, dump]
D
Initial  
David Heinemeier Hansson 已提交
561 562
          end
        end
563
    end
564

565 566 567
    class IndexDefinition < Struct.new(:table, :name, :unique, :columns)
    end

568
    class ColumnDefinition < Struct.new(:base, :name, :type, :limit, :default, :null)
569 570
      def to_sql
        column_sql = "#{name} #{type_to_sql(type.to_sym, limit)}"
571
        add_column_options!(column_sql, :null => null, :default => default)
572 573 574 575 576 577 578 579
        column_sql
      end
      alias to_s :to_sql
      
    private
      def type_to_sql(name, limit)
        base.type_to_sql(name, limit) rescue name
      end   
580 581

      def add_column_options!(sql, options)
582
        base.add_column_options!(sql, options.merge(:column => self))
583
      end
584 585
    end

586 587
    class TableDefinition
      attr_accessor :columns
588

589
      def initialize(base)
590
        @columns = []
591 592 593 594
        @base = base
      end

      def primary_key(name)
595
        column(name, native[:primary_key])
596
      end
597 598 599 600
      
      def [](name)
        @columns.find {|column| column.name == name}
      end
601 602

      def column(name, type, options = {})
603
        column = self[name] || ColumnDefinition.new(@base, name, type)
604
        column.limit = options[:limit] || native[type.to_sym][:limit] if options[:limit] or native[type.to_sym]
605
        column.default = options[:default]
606
        column.null = options[:null]
607
        @columns << column unless @columns.include? column
608 609
        self
      end
610
            
611
      def to_sql
612
        @columns * ', '
613 614
      end
      
615
    private
616 617 618
      def native
        @base.native_database_types
      end
619
    end
D
Initial  
David Heinemeier Hansson 已提交
620
  end
621
end