Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Changeset 2339

Show
Ignore:
Timestamp:
09/25/05 17:56:03 (3 years ago)
Author:
david
Message:

Refactored the AbstractAdapter to be a lot less scary. Cleaned up the docs and style for the OSS adapters

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb

    r2335 r2339  
    11require 'benchmark' 
    22require 'date' 
     3 
     4require 'active_record/connection_adapters/abstract/schema_definitions' 
     5require 'active_record/connection_adapters/abstract/schema_statements' 
     6require 'active_record/connection_adapters/abstract/database_statements' 
     7require 'active_record/connection_adapters/abstract/quoting' 
     8require 'active_record/connection_adapters/abstract/connection_specification' 
    39 
    410# Method that requires a library, ensuring that rubygems is loaded 
     
    2632 
    2733module ActiveRecord 
    28   class Base 
    29     class ConnectionSpecification #:nodoc: 
    30       attr_reader :config, :adapter_method 
    31       def initialize (config, adapter_method) 
    32         @config, @adapter_method = config, adapter_method 
    33       end 
    34     end 
    35  
    36     # The class -> [adapter_method, config] map 
    37     @@defined_connections = {} 
    38  
    39     # Establishes the connection to the database. Accepts a hash as input where 
    40     # the :adapter key must be specified with the name of a database adapter (in lower-case) 
    41     # example for regular databases (MySQL, Postgresql, etc): 
    42     # 
    43     #   ActiveRecord::Base.establish_connection( 
    44     #     :adapter  => "mysql", 
    45     #     :host     => "localhost", 
    46     #     :username => "myuser", 
    47     #     :password => "mypass", 
    48     #     :database => "somedatabase" 
    49     #   ) 
    50     # 
    51     # Example for SQLite database: 
    52     # 
    53     #   ActiveRecord::Base.establish_connection( 
    54     #     :adapter => "sqlite", 
    55     #     :dbfile  => "path/to/dbfile" 
    56     #   ) 
    57     # 
    58     # Also accepts keys as strings (for parsing from yaml for example): 
    59     #   ActiveRecord::Base.establish_connection( 
    60     #     "adapter" => "sqlite", 
    61     #     "dbfile"  => "path/to/dbfile" 
    62     #   ) 
    63     # 
    64     # The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError 
    65     # may be returned on an error. 
    66     def self.establish_connection(spec = nil) 
    67       case spec 
    68         when nil 
    69           raise AdapterNotSpecified unless defined? RAILS_ENV 
    70           establish_connection(RAILS_ENV) 
    71         when ConnectionSpecification 
    72           @@defined_connections[self] = spec 
    73         when Symbol, String 
    74           if configuration = configurations[spec.to_s] 
    75             establish_connection(configuration) 
    76           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           remove_connection 
    85           establish_connection(ConnectionSpecification.new(spec, adapter_method)) 
    86       end 
    87     end 
    88  
    89     def self.active_connections #:nodoc: 
    90       if threaded_connections 
    91         Thread.current['active_connections'] ||= {} 
    92       else 
    93         @@active_connections ||= {} 
    94       end 
    95     end 
    96  
    97     # Locate the connection of the nearest super class. This can be an 
    98     # active or defined connections: if it is the latter, it will be 
    99     # opened and set as the active connection for the class it was defined 
    100     # for (not necessarily the current class). 
    101     def self.retrieve_connection #:nodoc: 
    102       klass = self 
    103       ar_super = ActiveRecord::Base.superclass 
    104       until klass == ar_super 
    105         if conn = active_connections[klass] 
    106           return conn 
    107         elsif conn = @@defined_connections[klass] 
    108           klass.connection = conn 
    109           return self.connection 
    110         end 
    111         klass = klass.superclass 
    112       end 
    113       raise ConnectionNotEstablished 
    114     end 
    115  
    116     # Returns true if a connection that's accessible to this class have already been opened. 
    117     def self.connected? 
    118       klass = self 
    119       until klass == ActiveRecord::Base.superclass 
    120         if active_connections[klass] 
    121           return true 
    122         else 
    123           klass = klass.superclass 
    124         end 
    125       end 
    126       return false 
    127     end 
    128  
    129     # Remove the connection for this class. This will close the active 
    130     # connection and the defined connection (if they exist). The result 
    131     # can be used as argument for establish_connection, for easy 
    132     # re-establishing of the connection. 
    133     def self.remove_connection(klass=self) 
    134       conn = @@defined_connections[klass] 
    135       @@defined_connections.delete(klass) 
    136       active_connections[klass] = nil 
    137       conn.config if conn 
    138     end 
    139  
    140     # Set the connection for the class. 
    141     def self.connection=(spec) 
    142       raise ConnectionNotEstablished unless spec 
    143       conn = self.send(spec.adapter_method, spec.config) 
    144       active_connections[self] = conn 
    145     end 
    146  
    147     # Converts all strings in a hash to symbols. 
    148     def self.symbolize_strings_in_hash(hash) #:nodoc: 
    149       hash.symbolize_keys 
    150     end 
    151   end 
    152  
    15334  module ConnectionAdapters # :nodoc: 
    154     class Column # :nodoc: 
    155       attr_reader :name, :default, :type, :limit, :null 
    156       # The name should contain the name of the column, such as "name" in "name varchar(250)" 
    157       # The default should contain the type-casted default of the column, such as 1 in "count int(11) DEFAULT 1" 
    158       # The type parameter should either contain :integer, :float, :datetime, :date, :text, or :string 
    159       # 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         @name, @type, @null = name, simplified_type(sql_type), null 
    162         # have to do this one separately because type_cast depends on #type 
    163         @default = type_cast(default) 
    164         @limit = extract_limit(sql_type) unless sql_type.nil? 
    165       end 
    166  
    167       def klass 
    168         case type 
    169           when :integer       then Fixnum 
    170           when :float         then Float 
    171           when :datetime      then Time 
    172           when :date          then Date 
    173           when :timestamp     then Time 
    174           when :time          then Time 
    175           when :text, :string then String 
    176           when :binary        then String 
    177           when :boolean       then Object 
    178         end 
    179       end 
    180  
    181       def type_cast(value) 
    182         if value.nil? then return nil end 
    183         case type 
    184           when :string    then value 
    185           when :text      then value 
    186           when :integer   then value.to_i rescue value ? 1 : 0 
    187           when :float     then value.to_f 
    188           when :datetime  then string_to_time(value) 
    189           when :timestamp then string_to_time(value) 
    190           when :time      then string_to_dummy_time(value) 
    191           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' 
    194           else value 
    195         end 
    196       end 
    197  
    198       def human_name 
    199         Base.human_attribute_name(@name) 
    200       end 
    201  
    202       def string_to_binary(value) 
    203         value 
    204       end 
    205  
    206       def binary_to_string(value) 
    207         value 
    208       end 
    209  
    210       private 
    211         def string_to_date(string) 
    212           return string unless string.is_a?(String) 
    213           date_array = ParseDate.parsedate(string.to_s) 
    214           # treat 0000-00-00 as nil 
    215           Date.new(date_array[0], date_array[1], date_array[2]) rescue nil 
    216         end 
    217  
    218         def string_to_time(string) 
    219           return string unless string.is_a?(String) 
    220           time_array = ParseDate.parsedate(string.to_s).compact 
    221           # treat 0000-00-00 00:00:00 as nil 
    222           Time.send(Base.default_timezone, *time_array) rescue nil 
    223         end 
    224  
    225         def string_to_dummy_time(string) 
    226           return string unless string.is_a?(String) 
    227           time_array = ParseDate.parsedate(string.to_s) 
    228           # pad the resulting array with dummy date information 
    229           time_array[0] = 2000; time_array[1] = 1; time_array[2] = 1; 
    230           Time.send(Base.default_timezone, *time_array) rescue nil 
    231         end 
    232  
    233         def extract_limit(sql_type) 
    234           $1.to_i if sql_type =~ /\((.*)\)/ 
    235         end 
    236  
    237         def simplified_type(field_type) 
    238           case field_type 
    239             when /int/i 
    240               :integer 
    241             when /float|double|decimal|numeric/i 
    242               :float 
    243             when /datetime/i 
    244               :datetime 
    245             when /timestamp/i 
    246               :timestamp 
    247             when /time/i 
    248               :time 
    249             when /date/i 
    250               :date 
    251             when /clob/i, /text/i 
    252               :text 
    253             when /blob/i, /binary/i 
    254               :binary 
    255             when /char/i, /string/i 
    256               :string 
    257             when /boolean/i 
    258               :boolean 
    259           end 
    260         end 
    261     end 
    262  
    26335    # All the concrete database adapters follow the interface laid down in this class. 
    26436    # You can use this interface directly by borrowing the database connection from the Base with 
    26537    # Base.connection. 
    26638    class AbstractAdapter 
     39      include Quoting, DatabaseStatements, SchemaStatements 
    26740      @@row_even = true 
    26841 
    269       def initialize(connection, logger = nil) # :nodoc: 
     42      def initialize(connection, logger = nil) #:nodoc: 
    27043        @connection, @logger = connection, logger 
    27144        @runtime = 0 
    27245      end 
    27346 
    274       # Returns an array of record hashes with the column names as a keys and fields as values. 
    275       def select_all(sql, name = nil) end 
    276  
    277       # Returns a record hash with the column names as a keys and fields as values. 
    278       def select_one(sql, name = nil) end 
    279  
    280       # Returns a single value from a record 
    281       def select_value(sql, name = nil) 
    282         result = select_one(sql, name) 
    283         result.nil? ? nil : result.values.first 
     47      # Returns the human-readable name of the adapter.  Use mixed case - one can always use downcase if needed. 
     48      def adapter_name 
     49        'Abstract' 
     50      end 
     51       
     52      # Returns true for database adapters that has implemented the schema statements. 
     53      def supports_migrations? 
     54        false 
    28455      end 
    28556 
    286       # Returns an array of the values of the first column in a select: 
    287       #   select_values("SELECT id FROM companies LIMIT 3") => [1,2,3] 
    288       def select_values(sql, name = nil) 
    289         result = select_all(sql, name) 
    290         result.map{ |v| v.values.first } 
    291       end 
    292  
    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  
    299       # Returns an array of column objects for the table specified by +table_name+. 
    300       def columns(table_name, name = nil) end 
    301  
    302       # 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 
    304  
    305       # Executes the update statement and returns the number of rows affected. 
    306       def update(sql, name = nil) end 
    307  
    308       # Executes the delete statement and returns the number of rows affected. 
    309       def delete(sql, name = nil) end 
    310  
    311       def reset_runtime # :nodoc: 
     57      def reset_runtime #:nodoc: 
    31258        rt = @runtime 
    31359        @runtime = 0 
    31460        return rt 
    315       end 
    316  
    317       # Wrap a block in a transaction.  Returns result of block. 
    318       def transaction(start_db_transaction = true) 
    319         begin 
    320           if block_given? 
    321             begin_db_transaction if start_db_transaction 
    322             result = yield 
    323             commit_db_transaction if start_db_transaction 
    324             result 
    325           end 
    326         rescue Exception => database_transaction_rollback 
    327           rollback_db_transaction if start_db_transaction 
    328           raise 
    329         end 
    330       end 
    331  
    332       # Begins the transaction (and turns off auto-committing). 
    333       def begin_db_transaction()    end 
    334  
    335       # Commits the transaction (and turns on auto-committing). 
    336       def commit_db_transaction()   end 
    337  
    338       # Rolls back the transaction (and turns on auto-committing). Must be done if the transaction block 
    339       # raises an exception or returns false. 
    340       def rollback_db_transaction() end 
    341  
    342       def quoted_true() "'t'" end 
    343       def quoted_false() "'f'" end 
    344  
    345       def quote(value, column = nil) 
    346         case value 
    347           when String 
    348             if column && column.type == :binary 
    349               "'#{quote_string(column.string_to_binary(value))}'" # ' (for ruby-mode) 
    350             elsif column && [:integer, :float].include?(column.type) 
    351               value.to_s 
    352             else 
    353               "'#{quote_string(value)}'" # ' (for ruby-mode) 
    354             end 
    355           when NilClass              then "NULL" 
    356           when TrueClass             then (column && column.type == :boolean ? quoted_true : "1") 
    357           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           when Time, DateTime        then "'#{value.strftime("%Y-%m-%d %H:%M:%S")}'" 
    361           else                            "'#{quote_string(value.to_yaml)}'" 
    362         end 
    363       end 
    364  
    365       def quote_string(s) 
    366         s.gsub(/\\/, '\&\&').gsub(/'/, "''") # ' (for ruby-mode) 
    367       end 
    368  
    369       def quote_column_name(name) 
    370         name 
    371       end 
    372  
    373       # Returns the human-readable name of the adapter.  Use mixed case - one can always use downcase if needed. 
    374       def adapter_name() 
    375         'Abstract' 
    376       end 
    377  
    378       # Returns a string of the CREATE TABLE SQL statements for recreating the entire structure of the database. 
    379       def structure_dump() end 
    380  
    381       def add_limit!(sql, options) 
    382         return unless options 
    383         add_limit_offset!(sql, options) 
    384       end 
    385  
    386       def add_limit_offset!(sql, options) 
    387         return if options[:limit].nil? 
    388         sql << " LIMIT #{options[:limit]}" 
    389         sql << " OFFSET #{options[:offset]}" if options.has_key?(:offset) and !options[:offset].nil? 
    390       end 
    391  
    392  
    393       def initialize_schema_information 
    394         begin 
    395           execute "CREATE TABLE schema_info (version #{type_to_sql(:integer)})" 
    396           execute "INSERT INTO schema_info (version) VALUES(0)" 
    397         rescue ActiveRecord::StatementInvalid 
    398           # Schema has been intialized 
    399         end 
    400       end 
    401  
    402       def dump_schema_information 
    403         begin 
    404           if (current_schema = ActiveRecord::Migrator.current_version) > 0 
    405             return "INSERT INTO schema_info (version) VALUES (#{current_schema});"  
    406           end 
    407         rescue ActiveRecord::StatementInvalid  
    408           # No Schema Info 
    409         end 
    410       end 
    411  
    412       def create_table(name, options = {}) 
    413         table_definition = TableDefinition.new(self) 
    414         table_definition.primary_key(options[:primary_key] || "id") unless options[:id] == false 
    415  
    416         yield table_definition 
    417         create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE " 
    418         create_sql << "#{name} (" 
    419         create_sql << table_definition.to_sql 
    420         create_sql << ") #{options[:options]}" 
    421         execute create_sql 
    422       end 
    423  
    424       def drop_table(name) 
    425         execute "DROP TABLE #{name}" 
    426       end 
    427  
    428       def add_column(table_name, column_name, type, options = {}) 
    429         add_column_sql = "ALTER TABLE #{table_name} ADD #{column_name} #{type_to_sql(type, options[:limit])}" 
    430         add_column_options!(add_column_sql, options) 
    431         execute(add_column_sql) 
    432       end 
    433        
    434       def remove_column(table_name, column_name) 
    435         execute "ALTER TABLE #{table_name} DROP #{column_name}" 
    436       end       
    437  
    438       def change_column(table_name, column_name, type, options = {}) 
    439         raise NotImplementedError, "change_column is not implemented" 
    440       end 
    441        
    442       def change_column_default(table_name, column_name, default) 
    443         raise NotImplementedError, "change_column_default is not implemented" 
    444       end 
    445        
    446       def rename_column(table_name, column_name, new_column_name) 
    447         raise NotImplementedError, "rename_column is not implemented" 
    448       end 
    449  
    450       # Create a new index on the given table. By default, it will be named 
    451       # <code>"#{table_name}_#{column_name.to_a.first}_index"</code>, but you 
    452       # can explicitly name the index by passing <code>:name => "..."</code> 
    453       # as the last parameter. Unique indexes may be created by passing 
    454       # <code>:unique => true</code>. 
    455       def add_index(table_name, column_name, options = {}) 
    456         index_name = "#{table_name}_#{column_name.to_a.first}_index" 
    457  
    458         if Hash === options # legacy support, since this param was a string 
    459           index_type = options[:unique] ? "UNIQUE" : "" 
    460           index_name = options[:name] || index_name 
    461         else 
    462           index_type = options 
    463         end 
    464  
    465         execute "CREATE #{index_type} INDEX #{index_name} ON #{table_name} (#{column_name.to_a.join(", ")})" 
    466       end 
    467  
    468       # Remove the given index from the table. 
    469       # 
    470       #   remove_index :my_table, :column => :foo 
    471       #   remove_index :my_table, :name => :my_index_on_foo 
    472       # 
    473       # The first version will remove the index named 
    474       # <code>"#{my_table}_#{column}_index"</code> from the table. The 
    475       # second removes the named column from the table. 
    476       def remove_index(table_name, options = {}) 
    477         if Hash === options # legacy support 
    478           if options[:column] 
    479             index_name = "#{table_name}_#{options[:column]}_index" 
    480           elsif options[:name] 
    481             index_name = options[:name] 
    482           else 
    483             raise ArgumentError, "You must specify the index name" 
    484           end 
    485         else 
    486           index_name = "#{table_name}_#{options}_index" 
    487         end 
    488  
    489         execute "DROP INDEX #{index_name} ON #{table_name}" 
    490       end 
    491        
    492       def supports_migrations? 
    493         false 
    494       end            
    495  
    496       def native_database_types 
    497         {} 
    498       end        
    499  
    500       def type_to_sql(type, limit = nil) 
    501         native = native_database_types[type] 
    502         limit ||= native[:limit] 
    503         column_type_sql = native[:name] 
    504         column_type_sql << "(#{limit})" if limit 
    505         column_type_sql 
    506       end             
    507        
    508       def add_column_options!(sql, options) 
    509         sql << " NOT NULL" if options[:null] == false 
    510         sql << " DEFAULT #{quote(options[:default], options[:column])}" unless options[:default].nil? 
    51161      end 
    51262 
     
    562112        end 
    563113    end 
    564  
    565     class IndexDefinition < Struct.new(:table, :name, :unique, :columns) 
    566     end 
    567  
    568     class ColumnDefinition < Struct.new(:base, :name, :type, :limit, :default, :null) 
    569       def to_sql 
    570         column_sql = "#{name} #{type_to_sql(type.to_sym, limit)}" 
    571         add_column_options!(column_sql, :null => null, :default => default) 
    572         column_sql 
    573       end 
    574       alias to_s :to_sql 
    575        
    576     private 
    577       def type_to_sql(name, limit) 
    578         base.type_to_sql(name, limit) rescue name 
    579       end    
    580  
    581       def add_column_options!(sql, options) 
    582         base.add_column_options!(sql, options.merge(:column => self)) 
    583       end 
    584     end 
    585  
    586     class TableDefinition 
    587       attr_accessor :columns 
    588  
    589       def initialize(base) 
    590         @columns = [] 
    591         @base = base 
    592       end 
    593  
    594       def primary_key(name) 
    595         column(name, native[:primary_key]) 
    596       end 
    597        
    598       def [](name) 
    599         @columns.find {|column| column.name == name} 
    600       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         self 
    609       end 
    610              
    611       def to_sql 
    612         @columns * ', ' 
    613       end 
    614        
    615     private 
    616       def native 
    617         @base.native_database_types 
    618       end 
    619     end 
    620114  end 
    621115end 
  • trunk/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb

    r2335 r2339  
    8282        "MySQL server has gone away" 
    8383      ] 
    84        
    85       def supports_migrations? 
     84 
     85      def initialize(connection, logger, connection_options=nil) 
     86        super(connection, logger) 
     87        @connection_options = connection_options 
     88      end 
     89 
     90      def adapter_name #:nodoc: 
     91        'MySQL' 
     92      end 
     93 
     94      def supports_migrations? #:nodoc: 
    8695        true 
    8796      end 
    8897 
    89       def native_database_types 
     98      def native_database_types #:nodoc 
    9099        { 
    91100          :primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY", 
     
    103112      end 
    104113 
    105       def initialize(connection, logger, connection_options=nil) 
    106         super(connection, logger) 
    107         @connection_options = connection_options 
    108       end 
    109  
    110       def adapter_name 
    111         'MySQL' 
    112       end 
    113  
    114       def select_all(sql, name = nil) 
     114 
     115      # QUOTING ================================================== 
     116 
     117      def quote_column_name(name) #:nodoc: 
     118        "`#{name}`" 
     119      end 
     120 
     121      def quote_string(string) #:nodoc: 
     122        Mysql::quote(string) 
     123      end 
     124 
     125      def quoted_true 
     126        "1" 
     127      end 
     128       
     129      def quoted_false 
     130        "0" 
     131      end 
     132 
     133      def quote_column_name(name) 
     134        "`#{name}`" 
     135      end 
     136 
     137 
     138      # DATABASE STATEMENTS ====================================== 
     139 
     140      def select_all(sql, name = nil) #:nodoc: 
    115141        select(sql, name) 
    116142      end 
    117143 
    118       def select_one(sql, name = nil) 
     144      def select_one(sql, name = nil) #:nodoc: 
    119145        result = select(sql, name) 
    120146        result.nil? ? nil : result.first 
    121147      end 
    122148 
    123       def tables(name = nil) 
    124         tables = [] 
    125         execute("SHOW TABLES", name).each { |field| tables << field[0] } 
    126         tables 
    127       end 
    128  
    129       def indexes(table_name, name = nil) 
    130         indexes = [] 
    131         current_index = nil 
    132         execute("SHOW KEYS FROM #{table_name}", name).each do |row| 
    133           if current_index != row[2] 
    134             next if row[2] == "PRIMARY" # skip the primary key 
    135             current_index = row[2] 
    136             indexes << IndexDefinition.new(row[0], row[2], row[1] == "0", []) 
    137           end 
    138  
    139           indexes.last.columns << row[4] 
    140         end 
    141         indexes 
    142       end 
    143  
    144       def columns(table_name, name = nil) 
    145         sql = "SHOW FIELDS FROM #{table_name}" 
    146         columns = [] 
    147         execute(sql, name).each { |field| columns << MysqlColumn.new(field[0], field[4], field[1], field[2] == "YES") } 
    148         columns 
    149       end 
    150  
    151       def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) 
    152         execute(sql, name = nil) 
    153         id_value || @connection.insert_id 
    154       end 
    155  
    156       def execute(sql, name = nil, retries = 2) 
     149      def execute(sql, name = nil, retries = 2) #:nodoc: 
    157150        unless @logger 
    158151          @connection.query(sql) 
     
    176169      end 
    177170 
    178       def update(sql, name = nil) 
     171      def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc: 
     172        execute(sql, name = nil) 
     173        id_value || @connection.insert_id 
     174      end 
     175 
     176      def update(sql, name = nil) #:nodoc: 
    179177        execute(sql, name) 
    180178        @connection.affected_rows 
    181179      end 
    182180 
    183       alias_method :delete, :update 
    184  
    185  
    186       def begin_db_transaction 
     181      alias_method :delete, :update #:nodoc: 
     182 
     183 
     184      def begin_db_transaction #:nodoc: 
    187185        execute "BEGIN" 
    188186      rescue Exception 
     
    190188      end 
    191189 
    192       def commit_db_transaction 
     190      def commit_db_transaction #:nodoc: 
    193191        execute "COMMIT" 
    194192      rescue Exception 
     
    196194      end 
    197195 
    198       def rollback_db_transaction 
     196      def rollback_db_transaction #:nodoc: 
    199197        execute "ROLLBACK" 
    200198      rescue Exception 
     
    203201 
    204202 
    205       def quoted_true() "1" end 
    206       def quoted_false() "0" end 
    207  
    208       def quote_column_name(name) 
    209         "`#{name}`" 
    210       end 
    211  
    212       def quote_string(string) 
    213         Mysql::quote(string) 
    214       end 
    215  
    216  
    217       def structure_dump 
    218         select_all("SHOW TABLES").inject("") do |structure, table| 
    219           structure += select_one("SHOW CREATE TABLE #{table.to_a.first.last}")["Create Table"] + ";\n\n" 
    220         end 
    221       end 
    222  
    223       def add_limit_offset!(sql, options) 
     203      def add_limit_offset!(sql, options) #:nodoc 
    224204        if options[:limit] 
    225205          if options[:offset].blank? 
     
    231211      end 
    232212 
    233       def recreate_database(name) 
     213 
     214      # SCHEMA STATEMENTS ======================================== 
     215 
     216      def structure_dump #:nodoc: 
     217        select_all("SHOW TABLES").inject("") do |structure, table| 
     218          structure += select_one("SHOW CREATE TABLE #{table.to_a.first.last}")["Create Table"] + ";\n\n" 
     219        end 
     220      end 
     221 
     222      def recreate_database(name) #:nodoc: 
    234223        drop_database(name) 
    235224        create_database(name) 
    236225      end 
    237226 
    238       def drop_database(name) 
     227      def create_database(name) #:nodoc: 
     228        execute "CREATE DATABASE #{name}" 
     229      end 
     230       
     231      def drop_database(name) #:nodoc: 
    239232        execute "DROP DATABASE IF EXISTS #{name}" 
    240233      end 
    241234 
    242       def create_database(name) 
    243         execute "CREATE DATABASE #{name}" 
    244       end 
    245        
    246       def change_column_default(table_name, column_name, default) 
     235 
     236      def tables(name = nil) #:nodoc: 
     237        tables = [] 
     238        execute("SHOW TABLES", name).each { |field| tables << field[0] } 
     239        tables 
     240      end 
     241 
     242      def indexes(table_name, name = nil)#:nodoc: 
     243        indexes = [] 
     244        current_index = nil 
     245        execute("SHOW KEYS FROM #{table_name}", name).each do |row| 
     246          if current_index != row[2] 
     247            next if row[2] == "PRIMARY" # skip the primary key 
     248            current_index = row[2] 
     249            indexes << IndexDefinition.new(row[0], row[2], row[1] == "0", []) 
     250          end 
     251 
     252          indexes.last.columns << row[4] 
     253        end 
     254        indexes 
     255      end 
     256 
     257      def columns(table_name, name = nil)#:nodoc: 
     258        sql = "SHOW FIELDS FROM #{table_name}" 
     259        columns = [] 
     260        execute(sql, name).each { |field| columns << MysqlColumn.new(field[0], field[4], field[1], field[2] == "YES") } 
     261        columns 
     262      end 
     263 
     264      def create_table(name, options = {}) #:nodoc: 
     265        super(name, {:options => "ENGINE=InnoDB"}.merge(options)) 
     266      end 
     267 
     268      def change_column_default(table_name, column_name, default) #:nodoc: 
    247269        current_type = select_one("SHOW COLUMNS FROM #{table_name} LIKE '#{column_name}'")["Type"] 
    248270 
     
    250272      end 
    251273 
    252       def change_column(table_name, column_name, type, options = {}) 
     274      def change_column(table_name, column_name, type, options = {}) #:nodoc: 
    253275        options[:default] ||= select_one("SHOW COLUMNS FROM #{table_name} LIKE '#{column_name}'")["Default"] 
    254276         
     
    258280      end 
    259281 
    260       def rename_column(table_name, column_name, new_column_name) 
     282      def rename_column(table_name, column_name, new_column_name) #:nodoc: 
    261283        current_type = select_one("SHOW COLUMNS FROM #{table_name} LIKE '#{column_name}'")["Type"] 
    262284        execute "ALTER TABLE #{table_name} CHANGE #{column_name} #{new_column_name} #{current_type}" 
    263285      end 
    264286 
    265       def create_table(name, options = {}) 
    266         super(name, {:options => "ENGINE=InnoDB"}.merge(options)) 
    267       end 
    268287 
    269288      private 
  • trunk/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb

    r2332 r2339  
    1  
    2 # postgresql_adaptor.rb 
    3 # author: Luke Holden <lholden@cablelan.net> 
    4 # notes: Currently this adaptor does not pass the test_zero_date_fields 
    5 #        and test_zero_datetime_fields unit tests in the BasicsTest test 
    6 #        group. 
    7 
    8 #        This is due to the fact that, in postgresql you can not have a 
    9 #        totally zero timestamp. Instead null/nil should be used to  
    10 #        represent no value. 
    11 
     1# Author: Luke Holden <lholden@cablelan.net> 
    122 
    133require 'active_record/connection_adapters/abstract_adapter' 
     
    6151    # * <tt>:min_messages</tt> -- An optional client min messages that is using in a SET client_min_messages TO <min_messages> call on connection. 
    6252    class PostgreSQLAdapter < AbstractAdapter 
     53      def adapter_name 
     54        'PostgreSQL' 
     55      end 
     56 
    6357      def native_database_types 
    6458        { 
     
    8175      end       
    8276       
    83       def select_all(sql, name = nil) 
     77 
     78      # QUOTING ================================================== 
     79 
     80      def quote(value, column = nil) 
     81        if value.class == String && column && column.type == :binary 
     82          quote_bytea(value) 
     83        else 
     84          super 
     85        end 
     86      end 
     87 
     88      def quote_column_name(name) 
     89        %("#{name}") 
     90      end 
     91 
     92 
     93      # DATABASE STATEMENTS ====================================== 
     94 
     95      def select_all(sql, name = nil) #:nodoc: 
    8496        select(sql, name) 
    8597      end 
    8698 
    87       def select_one(sql, name = nil) 
     99      def select_one(sql, name = nil) #:nodoc: 
    88100        result = select(sql, name) 
    89101        result.nil? ? nil : result.first 
    90102      end 
    91103 
     104      def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc: 
     105        execute(sql, name) 
     106        table = sql.split(" ", 4)[2] 
     107        return id_value || last_insert_id(table, pk) 
     108      end 
     109 
     110      def query(sql, name = nil) #:nodoc: 
     111        log(sql, name) { @connection.query(sql) } 
     112      end 
     113 
     114      def execute(sql, name = nil) #:nodoc: 
     115        log(sql, name) { @connection.exec(sql) } 
     116      end 
     117 
     118      def update(sql, name = nil) #:nodoc: 
     119        execute(sql, name).cmdtuples 
     120      end 
     121 
     122      alias_method :delete, :update #:nodoc: 
     123 
     124 
     125      def begin_db_transaction #:nodoc: 
     126        execute "BEGIN" 
     127      end 
     128 
     129      def commit_db_transaction #:nodoc: 
     130        execute "COMMIT" 
     131      end 
     132       
     133      def rollback_db_transaction #:nodoc: 
     134        execute "ROLLBACK" 
     135      end 
     136 
     137 
     138      # SCHEMA STATEMENTS ======================================== 
     139 
    92140      # Return the list of all tables in the schema search path. 
    93       def tables(name = nil) 
     141      def tables(name = nil) #:nodoc: 
    94142        schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',') 
    95143        query(<<-SQL, name).map { |row| row[0] } 
     
    100148      end 
    101149 
    102       def indexes(table_name, name = nil) 
     150      def indexes(table_name, name = nil) #:nodoc: 
    103151        result = query(<<-SQL, name) 
    104152          SELECT i.relname, d.indisunique, a.attname 
     
    133181      end 
    134182 
    135       def columns(table_name, name = nil) 
     183      def columns(table_name, name = nil) #:nodoc: 
    136184        column_definitions(table_name).collect do |name, type, default, notnull| 
    137185          Column.new(name, default_value(default), translate_field_type(type), 
     
    140188      end 
    141189 
    142       def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) 
    143         execute(sql, name) 
    144         table = sql.split(" ", 4)[2] 
    145         return id_value || last_insert_id(table, pk) 
    146       end 
    147  
    148       def query(sql, name = nil) 
    149         log(sql, name) { @connection.query(sql) } 
    150       end 
    151  
    152       def execute(sql, name = nil) 
    153         log(sql, name) { @connection.exec(sql) } 
    154       end 
    155  
    156       def update(sql, name = nil) 
    157         execute(sql, name).cmdtuples 
    158       end 
    159  
    160       alias_method :delete, :update 
    161  
     190      # Set the schema search path to a string of comma-separated schema names. 
     191      # Names beginning with $ are quoted (e.g. $user => '$user') 
     192      # See http://www.postgresql.org/docs/8.0/interactive/ddl-schemas.html 
     193      def schema_search_path=(schema_csv) #:nodoc: 
     194        if schema_csv 
     195          execute "SET search_path TO #{schema_csv}" 
     196          @schema_search_path = nil 
     197        end 
     198      end 
     199 
     200      def schema_search_path #:nodoc: 
     201        @schema_search_path ||= query('SHOW search_path')[0][0] 
     202      end 
     203             
    162204      def add_column(table_name, column_name, type, options = {}) 
    163205        native_type = native_database_types[type] 
     
    171213        sql_commands.each { |cmd| execute(cmd) } 
    172214      end 
    173        
    174  
    175       def begin_db_transaction()    execute "BEGIN" end 
    176       def commit_db_transaction()   execute "COMMIT" end 
    177       def rollback_db_transaction() execute "ROLLBACK" end 
    178  
    179       def quote(value, column = nil) 
    180         if value.class == String && column && column.type == :binary 
    181           quote_bytea(value) 
    182         else 
    183           super 
    184         end 
    185       end 
    186  
    187       def quote_column_name(name) 
    188         %("#{name}") 
    189       end 
    190  
    191       def adapter_name 
    192         'PostgreSQL' 
    193       end 
    194  
    195  
    196       # Set the schema search path to a string of comma-separated schema names. 
    197       # Names beginning with $ are quoted (e.g. $user => '$user') 
    198       # See http://www.postgresql.org/docs/8.0/inte