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

Ticket #8049: postgresql_adapter.diff

File postgresql_adapter.diff, 38.6 kB (added by FooBarWidget, 1 year ago)

Changes to the PostgreSQLAdapter class

  • activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb

    old new  
    22 
    33module ActiveRecord 
    44  class Base 
    5     # Establishes a connection to the database that's used by all Active Record objects 
    6     def self.postgresql_connection(config) # :nodoc: 
    7       require_library_or_gem 'postgres' unless self.class.const_defined?(:PGconn) 
     5    # Establishes a connection to the database that's used by all Active Record objects. 
     6    def self.postgresql_connection(config) 
     7      require_library_or_gem('postgres') unless self.class.const_defined?(:PGconn) 
    88 
    99      config = config.symbolize_keys 
    1010      host     = config[:host] 
     
    1212      username = config[:username].to_s 
    1313      password = config[:password].to_s 
    1414 
    15       min_messages = config[:min_messages] 
    16  
    1715      if config.has_key?(:database) 
    1816        database = config[:database] 
    1917      else 
    20         raise ArgumentError, "No database specified. Missing argument: database." 
     18        raise(ArgumentError, 'No database specified. Missing argument: database.') 
    2119      end 
    2220 
    23       pga = ConnectionAdapters::PostgreSQLAdapter.new( 
    24         PGconn.connect(host, port, "", "", database, username, password), logger, config 
    25       ) 
     21      # The postgres drivers don't allow to create an unconnected PGconn object, 
     22      # so just pass a nil connection object for the time being. 
     23      ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, [host, port, nil, nil, database, username, password], config) 
     24    end 
     25  end 
    2626 
    27       PGconn.translate_results = false if PGconn.respond_to? :translate_results= 
     27  module ConnectionAdapters 
     28    # PostgreSQL-specific extensions to column definitions in a table. 
     29    class PostgreSQLColumn < Column #:nodoc: 
     30      # Instantiates a new PostgreSQL column definition in a table. 
     31      def initialize(name, default, sql_type = nil, null = true) 
     32        super(name, self.class.extract_value_from_default(default), sql_type, null) 
     33      end 
    2834 
    29       pga.schema_search_path = config[:schema_search_path] || config[:schema_order] 
     35      private 
     36        # Extracts the scale from PostgreSQL-specific data types. 
     37        def extract_scale(sql_type) 
     38          # Money type has a fixed scale of 2. 
     39          if sql_type =~ /^money/ 
     40            2 
     41          else 
     42            super 
     43          end 
     44        end 
    3045 
    31       pga 
     46        # Extracts the precision from PostgreSQL-specific data types. 
     47        def extract_precision(sql_type) 
     48          # Actual code is defined dynamically in PostgreSQLAdapter.connect 
     49          super 
     50        end 
     51 
     52        # Escapes binary strings for bytea input to the database. 
     53        def self.string_to_binary(value) 
     54          if PGconn.respond_to?(:escape_bytea) 
     55            self.class.module_eval do 
     56              define_method(:string_to_binary) do |value| 
     57                PGconn.escape_bytea(value) if value 
     58              end 
     59            end 
     60          else 
     61            self.class.module_eval do 
     62              define_method(:string_to_binary) do |value| 
     63                if value 
     64                  result = '' 
     65                  value.each_byte { |c| result << sprintf('\\\\%03o', c) } 
     66                  result 
     67                end 
     68              end 
     69            end 
     70          end 
     71          self.class.string_to_binary(value) 
     72        end 
     73 
     74        # Unescapes bytea output from a database to the binary string it represents. 
     75        def self.binary_to_string(value) 
     76          # In each case, check if the value actually is escaped PostgresSQL bytea output 
     77          # or an unescaped Active Record attribute that was just written. 
     78          if PGconn.respond_to?(:unescape_bytea) 
     79            self.class.module_eval do 
     80              define_method(:binary_to_string) do |value| 
     81                if value =~ /\\\\\d{3}/ 
     82                  PGconn.unescape_bytea(value) 
     83                else 
     84                  value 
     85                end 
     86              end 
     87            end 
     88          else 
     89            self.class.module_eval do 
     90              define_method(:binary_to_string) do |value| 
     91                if value =~ /\\\\\d{3}/ 
     92                  result = '' 
     93                  i, max = 0, value.size 
     94                  while i < max 
     95                    char = value[i] 
     96                    if char == ?\\ 
     97                      if value[i+1] == ?\\ 
     98                        char = ?\\ 
     99                        i += 1 
     100                      else 
     101                        char = value[i+1..i+3].oct 
     102                        i += 3 
     103                      end 
     104                    end 
     105                    result << char 
     106                    i += 1 
     107                  end 
     108                  result 
     109                else 
     110                  value 
     111                end 
     112              end 
     113            end 
     114          end 
     115          self.class.binary_to_string(value) 
     116        end 
     117 
     118        # Maps PostgreSQL-specific data types to logical Rails types. 
     119        def simplified_type(field_type) 
     120          case field_type 
     121            # Numeric and monetary types 
     122            when /^(?:real|double precision)$/ 
     123              :float 
     124            # Monetary types 
     125            when /^money$/ 
     126              :decimal 
     127            # Character types 
     128            when /^(?:character varying|bpchar)(?:\(\d+\))?$/ 
     129              :string 
     130            # Binary data types 
     131            when /^bytea$/ 
     132              :binary 
     133            # Date/time types 
     134            when /^timestamp with(?:out)? time zone$/ 
     135              :datetime 
     136            when /^interval$/ 
     137              :string 
     138            # Geometric types 
     139            when /^(?:point|line|lseg|box|"?path"?|polygon|circle)$/ 
     140              :string 
     141            # Network address types 
     142            when /^(?:cidr|inet|macaddr)$/ 
     143              :string 
     144            # Bit strings 
     145            when /^bit(?: varying)?(?:\(\d+\))?$/ 
     146              :string 
     147            # XML type 
     148            when /^xml$/ 
     149              :string 
     150            # Arrays 
     151            when /^\D+\[\]$/ 
     152              :string               
     153            # Object identifier types 
     154            when /^oid$/ 
     155              :integer 
     156            # Pass through all types that are not specific to PostgreSQL. 
     157            else 
     158              super 
     159          end 
     160        end 
     161 
     162        # Extracts the value from a PostgreSQL column default definition. 
     163        def self.extract_value_from_default(default) 
     164          case default 
     165            # Numeric types 
     166            when /^-?\d+(\.\d*)?$/ 
     167              default 
     168            # Character types 
     169            when /^'(.*)'::(?:character varying|bpchar|text)$/ 
     170              $1 
     171            # Binary data types 
     172            when /^'(.*)'::bytea$/ 
     173              $1 
     174            # Date/time types 
     175            when /^'(.+)'::(?:time(?:stamp)? with(?:out)? time zone|date)$/ 
     176              $1 
     177            when /^'(.*)'::interval$/ 
     178              $1 
     179            # Boolean type 
     180            when /^true$/ 
     181              true 
     182            when /^false$/ 
     183              false 
     184            # Geometric types 
     185            when /^'(.*)'::(?:point|line|lseg|box|"?path"?|polygon|circle)$/ 
     186              $1 
     187            # Network address types 
     188            when /^'(.*)'::(?:cidr|inet|macaddr)$/ 
     189              $1 
     190            # Bit string types 
     191            when /^B'(.*)'::"?bit(?: varying)?"?$/ 
     192              $1 
     193            # XML type 
     194            when /^'(.*)'::xml$/ 
     195              $1 
     196            # Arrays 
     197            when /^'(.*)'::"?\D+"?\[\]$/ 
     198              $1 
     199            # Object identifier types 
     200            when /^-?\d+$/ 
     201              $1 
     202            else 
     203              # Anything else is blank, some user type, or some function 
     204              # and we can't know the value of that, so return nil.             
     205              nil 
     206          end 
     207        end 
    32208    end 
    33   end 
    34209 
    35   module ConnectionAdapters 
    36     # The PostgreSQL adapter works both with the C-based (http://www.postgresql.jp/interfaces/ruby/) and the Ruby-base 
    37     # (available both as gem and from http://rubyforge.org/frs/?group_id=234&release_id=1145) drivers. 
     210    # The PostgreSQL adapter works both with the native C (http://ruby.scripting.ca/postgres/) and the pure 
     211    # Ruby (available both as gem and from http://rubyforge.org/frs/?group_id=234&release_id=1944) drivers. 
    38212    # 
    39213    # Options: 
    40214    # 
     
    48222    # * <tt>:min_messages</tt> -- An optional client min messages that is using in a SET client_min_messages TO <min_messages> call on connection. 
    49223    # * <tt>:allow_concurrency</tt> -- If true, use async query methods so Ruby threads don't deadlock; otherwise, use blocking query methods. 
    50224    class PostgreSQLAdapter < AbstractAdapter 
     225      # Returns 'PostgreSQL' as adapter name for identification purposes. 
    51226      def adapter_name 
    52227        'PostgreSQL' 
    53228      end 
    54229 
    55       def initialize(connection, logger, config = {}) 
     230      # Initializes and connects a PostgreSQL adapter. 
     231      def initialize(connection, logger, connection_parameters, config) 
    56232        super(connection, logger) 
    57         @config = config 
     233        @connection_parameters, @config = connection_parameters, config 
    58234 
    59         # Ignore async_exec and async_query with the postgres-pr client lib. 
    60         @async = config[:allow_concurrency] && @connection.respond_to?(:async_exec) 
    61  
    62         configure_connection 
     235        connect 
    63236      end 
    64237 
    65       # Is this connection alive and ready for queries? 
     238      # Returns if this connection is alive and ready for queries. 
    66239      def active? 
    67240        if @connection.respond_to?(:status) 
    68241          @connection.status == PGconn::CONNECTION_OK 
    69242        else 
    70           @connection.query 'SELECT 1' 
     243          # We're asking the driver, not ActiveRecord, so use @connection.query instead of #query 
     244          @connection.query('SELECT 1') 
    71245          true 
    72246        end 
    73       # postgres-pr raises a NoMethodError when querying if no conn is available 
     247      # postgres-pr raises a NoMethodError when querying if no connection is available. 
    74248      rescue PGError, NoMethodError 
    75249        false 
    76250      end 
    77251 
    78       # Close then reopen the connection. 
     252      # Closes and then reopens the connection. 
    79253      def reconnect! 
    80         # TODO: postgres-pr doesn't have PGconn#reset. 
    81254        if @connection.respond_to?(:reset) 
    82255          @connection.reset 
    83256          configure_connection 
     257        else 
     258          disconnect! 
     259          connect 
    84260        end 
    85261      end 
    86262 
     263      # Closes the connection. 
    87264      def disconnect! 
    88         # Both postgres and postgres-pr respond to :close 
    89265        @connection.close rescue nil 
    90266      end 
    91267 
    92       def native_database_types 
     268      def native_database_types #:nodoc: 
    93269        { 
    94           :primary_key => "serial primary key"
    95           :string      => { :name => "character varying", :limit => 255 }, 
    96           :text        => { :name => "text" }, 
    97           :integer     => { :name => "integer" }, 
    98           :float       => { :name => "float" }, 
    99           :decimal     => { :name => "decimal" }, 
    100           :datetime    => { :name => "timestamp" }, 
    101           :timestamp   => { :name => "timestamp" }, 
    102           :time        => { :name => "time" }, 
    103           :date        => { :name => "date" }, 
    104           :binary      => { :name => "bytea" }, 
    105           :boolean     => { :name => "boolean"
     270          :primary_key => 'serial PRIMARY KEY'
     271          :string      => { :name => 'character varying', :limit => 255 }, 
     272          :text        => { :name => 'text' }, 
     273          :integer     => { :name => 'integer' }, 
     274          :float       => { :name => 'float' }, 
     275          :decimal     => { :name => 'decimal' }, 
     276          :datetime    => { :name => 'timestamp' }, 
     277          :timestamp   => { :name => 'timestamp' }, 
     278          :time        => { :name => 'time' }, 
     279          :date        => { :name => 'date' }, 
     280          :binary      => { :name => 'bytea' }, 
     281          :boolean     => { :name => 'boolean'
    106282        } 
    107283      end 
    108284 
     285      # Returns true to indicate that PostgreSQL supports migrations. 
    109286      def supports_migrations? 
    110287        true 
    111288      end 
    112289 
     290      # Returns the configured supported identifier length supported by PostgreSQL, 
     291      # or report the default of 63 on PostgreSQL 7. 
    113292      def table_alias_length 
    114         63 
     293        @table_alias_length ||= (postgresql_version >= 80000 ? query('SHOW max_identifier_length')[0][0].to_i : 63) 
    115294      end 
    116295 
    117296      # QUOTING ================================================== 
    118297 
    119       def quote(value, column = nil) 
     298      # Quotes PostgreSQL-specific data types for SQL input. 
     299      def quote(value, column = nil) #:nodoc: 
    120300        if value.kind_of?(String) && column && column.type == :binary 
    121           "'#{escape_bytea(value)}'" 
     301          "#{quoted_string_prefix}'#{column.class.string_to_binary(value)}'" 
     302        elsif value.kind_of?(String) && column && column.sql_type =~ /^xml$/ 
     303          "xml '#{quote_string(value)}'" 
     304        elsif value.kind_of?(Numeric) && column && column.sql_type =~ /^money$/ 
     305          # Not truly string input, so doesn't require (or allow) escape string syntax. 
     306          "'#{value.to_s}'" 
     307        elsif value.kind_of?(String) && column && column.sql_type =~ /^bit/ 
     308          case value 
     309            when /^[01]*$/ 
     310              "B'#{value}'" # Bit-string notation 
     311            when /^[0-9A-F]*$/i 
     312              "X'#{value}'" # Hexadecimal notation 
     313          end 
    122314        else 
    123315          super 
    124316        end 
    125317      end 
    126318 
    127       def quote_column_name(name) 
     319      # Quotes strings for use in SQL input in the postgres driver for better performance. 
     320      def quote_string(s) #:nodoc: 
     321        if PGconn.respond_to?(:escape) 
     322          self.class.instance_eval do 
     323            define_method(:quote_string) do |s| 
     324              PGconn.escape(s) 
     325            end 
     326          end 
     327        else 
     328          # There are some incorrectly compiled postgres drivers out there 
     329          # that don't define PGconn.escape. 
     330          self.class.instance_eval do 
     331            undef_method(:quote_string) 
     332          end 
     333        end 
     334        quote_string(s) 
     335      end 
     336 
     337      # Quotes column names for use in SQL queries. 
     338      def quote_column_name(name) #:nodoc: 
    128339        %("#{name}") 
    129340      end 
    130341 
    131       # Include microseconds if the value is a Time responding to usec. 
    132       def quoted_date(value) 
     342      # Quote date/time values for use in SQL input. Includes microseconds 
     343      # if the value is a Time responding to usec. 
     344      def quoted_date(value) #:nodoc: 
    133345        if value.acts_like?(:time) && value.respond_to?(:usec) 
    134346          "#{super}.#{sprintf("%06d", value.usec)}" 
    135347        else 
     
    137349        end 
    138350      end 
    139351 
    140  
    141352      # DATABASE STATEMENTS ====================================== 
    142353 
    143       def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc: 
     354      # Executes an INSERT query and returns the new record's ID 
     355      def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) 
    144356        execute(sql, name) 
    145         table = sql.split(" ", 4)[2] 
     357        table = sql.split(' ', 4)[2] 
    146358        id_value || last_insert_id(table, sequence_name || default_sequence_name(table, pk)) 
    147359      end 
    148360 
     361      # Queries the database and returns the results in an Array or nil otherwise. 
    149362      def query(sql, name = nil) #:nodoc: 
    150363        log(sql, name) do 
    151364          if @async 
     
    156369        end 
    157370      end 
    158371 
    159       def execute(sql, name = nil) #:nodoc: 
     372      # Executes a SQL statement, returning a PGresult object on success 
     373      # or raising a PGError exception otherwise. 
     374      def execute(sql, name = nil) 
    160375        log(sql, name) do 
    161376          if @async 
    162377            @connection.async_exec(sql) 
     
    166381        end 
    167382      end 
    168383 
    169       def update(sql, name = nil) #:nodoc: 
     384      # Executes an UPDATE query and returns the number of affected tuples. 
     385      def update(sql, name = nil) 
    170386        execute(sql, name).cmdtuples 
    171387      end 
    172388 
    173       def begin_db_transaction #:nodoc: 
    174         execute "BEGIN" 
     389      # Begins a transaction. 
     390      def begin_db_transaction 
     391        execute('BEGIN') 
    175392      end 
    176393 
    177       def commit_db_transaction #:nodoc: 
    178         execute "COMMIT" 
     394      # Commits a transaction. 
     395      def commit_db_transaction 
     396        execute('COMMIT') 
    179397      end 
    180398 
    181       def rollback_db_transaction #:nodoc: 
    182         execute "ROLLBACK" 
     399      # Aborts a transaction. 
     400      def rollback_db_transaction 
     401        execute('ROLLBACK') 
    183402      end 
    184403 
    185404      # SCHEMA STATEMENTS ======================================== 
    186405 
    187       # Return the list of all tables in the schema search path
    188       def tables(name = nil) #:nodoc: 
     406      # Returns the list of all tables in the schema search path or a specified schema
     407      def tables(name = nil) 
    189408        schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',') 
    190409        query(<<-SQL, name).map { |row| row[0] } 
    191410          SELECT tablename 
     
    194413        SQL 
    195414      end 
    196415 
    197       def indexes(table_name, name = nil) #:nodoc: 
     416      # Returns the list of all indexes for a table. 
     417      def indexes(table_name, name = nil) 
    198418        result = query(<<-SQL, name) 
    199419          SELECT i.relname, d.indisunique, a.attname 
    200420            FROM pg_class t, pg_class i, pg_index d, pg_attribute a 
     
    217437 
    218438        result.each do |row| 
    219439          if current_index != row[0] 
    220             indexes << IndexDefinition.new(table_name, row[0], row[1] == "t", []) 
     440            indexes << IndexDefinition.new(table_name, row[0], row[1] == 't', []) 
    221441            current_index = row[0] 
    222442          end 
    223443 
     
    227447        indexes 
    228448      end 
    229449 
    230       def columns(table_name, name = nil) #:nodoc: 
    231         column_definitions(table_name).collect do |name, type, default, notnull, typmod| 
    232           # typmod now unused as limit, precision, scale all handled by superclass 
    233           Column.new(name, default_value(default), translate_field_type(type), notnull == "f") 
     450      # Returns the list of all column definitions for a table. 
     451      def columns(table_name, name = nil) 
     452        # Limit, precision, and scale are all handled by superclass. 
     453        column_definitions(table_name).collect do |name, type, default, notnull| 
     454          PostgreSQLColumn.new(name, default, type, notnull == 'f') 
    234455        end 
    235456      end 
    236457 
    237       # Set the schema search path to a string of comma-separated schema names. 
    238       # Names beginning with $ are quoted (e.g. $user => '$user') 
    239       # See http://www.postgresql.org/docs/8.0/interactive/ddl-schemas.html 
    240       def schema_search_path=(schema_csv) #:nodoc: 
     458      # Sets the schema search path to a string of comma-separated schema names. 
     459      # Names beginning with $ have to be quoted (e.g. $user => '$user'). 
     460      # See: http://www.postgresql.org/docs/current/static/ddl-schemas.html 
     461      # 
     462      # This should be not be called manually but set in database.yml. 
     463      def schema_search_path=(schema_csv) 
    241464        if schema_csv 
    242           execute "SET search_path TO #{schema_csv}" 
    243           @schema_search_path = nil 
     465          execute("SET search_path TO #{schema_csv}") 
     466          @schema_search_path = schema_csv 
    244467        end 
    245468      end 
    246469 
    247       def schema_search_path #:nodoc: 
     470      # Returns the active schema search path. 
     471      def schema_search_path 
    248472        @schema_search_path ||= query('SHOW search_path')[0][0] 
    249473      end 
    250474 
    251       def default_sequence_name(table_name, pk = nil) 
     475      # Returns the sequence name for a table's primary key or some other specified key. 
     476      def default_sequence_name(table_name, pk = nil) #:nodoc: 
    252477        default_pk, default_seq = pk_and_sequence_for(table_name) 
    253478        default_seq || "#{table_name}_#{pk || default_pk || 'id'}_seq" 
    254479      end 
    255480 
    256       # Resets sequence to the max value of the table's pk if present
    257       def reset_pk_sequence!(table, pk = nil, sequence = nil) 
    258         unless pk and sequence 
     481      # Resets the sequence of a table's primary key to the maximum value
     482      def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc: 
     483        unless pk && sequence 
    259484          default_pk, default_sequence = pk_and_sequence_for(table) 
    260485          pk ||= default_pk 
    261486          sequence ||= default_sequence 
    262487        end 
    263488        if pk 
    264489          if sequence 
    265             select_value <<-end_sql, 'Reset sequence' 
     490            select_value(<<-end_sql, 'Reset sequence') 
    266491              SELECT setval('#{sequence}', (SELECT COALESCE(MAX(#{pk})+(SELECT increment_by FROM #{sequence}), (SELECT min_value FROM #{sequence})) FROM #{table}), false) 
    267492            end_sql 
    268493          else 
    269             @logger.warn "#{table} has primary key #{pk} with no default sequence" if @logger 
     494            @logger.warn("#{table} has primary key #{pk} with no default sequence") if @logger 
    270495          end 
    271496        end 
    272497      end 
    273498 
    274       # Find a table's primary key and sequence. 
    275       def pk_and_sequence_for(table) 
     499      # Returns a table's primary key and belonging sequence. 
     500      def pk_and_sequence_for(table) #:nodoc: 
    276501        # First try looking for a sequence with a dependency on the 
    277502        # given table's primary key. 
    278         result = query(<<-end_sql, 'PK and serial sequence')[0] 
    279           SELECT attr.attname, name.nspname, seq.relname 
     503        result = query(<<-end_sql, 'Primary key and serial sequence')[0] 
     504          SELECT attr.attname, seq.relname 
    280505          FROM pg_class      seq, 
    281506               pg_attribute  attr, 
    282507               pg_depend     dep, 
    283508               pg_namespace  name, 
    284509               pg_constraint cons 
    285510          WHERE seq.oid           = dep.objid 
    286             AND seq.relnamespace  = name.oid 
    287511            AND seq.relkind       = 'S' 
    288512            AND attr.attrelid     = dep.refobjid 
    289513            AND attr.attnum       = dep.refobjsubid 
     
    293517            AND dep.refobjid      = '#{table}'::regclass 
    294518        end_sql 
    295519 
    296         if result.nil? or result.empty? 
     520        if result.nil? || result.empty? 
    297521          # If that fails, try parsing the primary key's default value. 
    298522          # Support the 7.x and 8.0 nextval('foo'::text) as well as 
    299523          # the 8.1+ nextval('foo'::regclass). 
    300           # TODO: assumes sequence is in same schema as table. 
    301           result = query(<<-end_sql, 'PK and custom sequence')[0] 
    302             SELECT attr.attname, name.nspname, split_part(def.adsrc, '''', 2) 
     524          result = query(<<-end_sql, 'Primary key and custom sequence')[0] 
     525            SELECT attr.attname, split_part(def.adsrc, '''', 2) 
    303526            FROM pg_class       t 
    304             JOIN pg_namespace   name ON (t.relnamespace = name.oid) 
    305527            JOIN pg_attribute   attr ON (t.oid = attrelid) 
    306528            JOIN pg_attrdef     def  ON (adrelid = attrelid AND adnum = attnum) 
    307529            JOIN pg_constraint  cons ON (conrelid = adrelid AND adnum = conkey[1]) 
     
    310532              AND def.adsrc ~* 'nextval' 
    311533          end_sql 
    312534        end 
    313         # check for existence of . in sequence name as in public.foo_sequence.  if it does not exist, return unqualified sequence 
    314         # We cannot qualify unqualified sequences, as rails doesn't qualify any table access, using the search path 
     535 
     536        # [primary_key, sequence] 
    315537        [result.first, result.last] 
    316538      rescue 
    317539        nil 
    318540      end 
    319541 
     542      # Renames a table. 
    320543      def rename_table(name, new_name) 
    321         execute "ALTER TABLE #{name} RENAME TO #{new_name}" 
     544        execute("ALTER TABLE #{name} RENAME TO #{new_name}") 
    322545      end 
    323546 
     547      # Adds a column to a table. 
    324548      def add_column(table_name, column_name, type, options = {}) 
    325549        default = options[:default] 
    326550        notnull = options[:null] == false 
     
    341565        end 
    342566      end 
    343567 
    344       def change_column(table_name, column_name, type, options = {}) #:nodoc: 
     568      # Changes the column of a table. 
     569      def change_column(table_name, column_name, type, options = {}) 
    345570        begin 
    346           execute "ALTER TABLE #{table_name} ALTER COLUMN #{column_name} TYPE #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" 
     571          execute("ALTER TABLE #{table_name} ALTER COLUMN #{column_name} TYPE #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}") 
    347572        rescue ActiveRecord::StatementInvalid 
    348           # This is PG7, so we use a more arcane way of doing it. 
     573          # This is PostgreSQL 7, so we have to use a more arcane way of doing it. 
    349574          begin_db_transaction 
    350575          add_column(table_name, "#{column_name}_ar_tmp", type, options) 
    351           execute "UPDATE #{table_name} SET #{column_name}_ar_tmp = CAST(#{column_name} AS #{type_to_sql(type, options[:limit], options[:precision], options[:scale])})" 
     576          execute("UPDATE #{table_name} SET #{column_name}_ar_tmp = CAST(#{column_name} AS #{type_to_sql(type, options[:limit], options[:precision], options[:scale])})") 
    352577          remove_column(table_name, column_name) 
    353578          rename_column(table_name, "#{column_name}_ar_tmp", column_name) 
    354579          commit_db_transaction 
     
    359584        end 
    360585      end 
    361586 
    362       def change_column_default(table_name, column_name, default) #:nodoc: 
    363         execute "ALTER TABLE #{table_name} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote(default)}" 
     587      # Changes the default value of a table column. 
     588      def change_column_default(table_name, column_name, default) 
     589        execute("ALTER TABLE #{table_name} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote(default)}") 
    364590      end 
    365591 
    366       def rename_column(table_name, column_name, new_column_name) #:nodoc: 
    367         execute "ALTER TABLE #{table_name} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}" 
     592      # Renames a column in a table. 
     593      def rename_column(table_name, column_name, new_column_name) 
     594        execute("ALTER TABLE #{table_name} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}") 
    368595      end 
    369596 
    370       def remove_index(table_name, options) #:nodoc: 
    371         execute "DROP INDEX #{index_name(table_name, options)}" 
     597      # Drops an index from a table. 
     598      def remove_index(table_name, options = {}) 
     599        execute("DROP INDEX #{index_name(table_name, options)}") 
    372600      end 
    373601 
    374       def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc: 
     602      # Maps logical Rails types to PostgreSQL-specific data types. 
     603      def type_to_sql(type, limit = nil, precision = nil, scale = nil) 
    375604        return super unless type.to_s == 'integer' 
    376605 
    377606        if limit.nil? || limit == 4 
     
    382611          'bigint' 
    383612        end 
    384613      end 
    385        
    386       # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause. 
     614 
     615      # Returns a SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause. 
    387616      # 
    388617      # PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and 
    389618      # requires that the ORDER BY include the distinct column. 
    390619      # 
    391620      #   distinct("posts.id", "posts.created_at desc") 
    392       def distinct(columns, order_by) 
     621      def distinct(columns, order_by) #:nodoc: 
    393622        return "DISTINCT #{columns}" if order_by.blank? 
    394623 
    395         # construct a clean list of column names from the ORDER BY clause, removing 
    396         # any asc/desc modifiers 
     624        # Construct a clean list of column names from the ORDER BY clause, removing 
     625        # any ASC/DESC modifiers. 
    397626        order_columns = order_by.split(',').collect { |s| s.split.first } 
    398         order_columns.delete_if &:blank? 
     627        order_columns.delete_if(&:blank?) 
    399628        order_columns = order_columns.zip((0...order_columns.size).to_a).map { |s,i| "#{s} AS alias_#{i}" } 
    400629 
    401         # return a DISTINCT ON() clause that's distinct on the columns we want but includes 
    402         # all the required columns for the ORDER BY to work properly 
     630        # Return a DISTINCT ON() clause that's distinct on the columns we want but includes 
     631        # all the required columns for the ORDER BY to work properly. 
    403632        sql = "DISTINCT ON (#{columns}) #{columns}, " 
    404633        sql << order_columns * ', ' 
    405634      end 
    406        
    407       # ORDER BY clause for the passed order option. 
    408       #  
     635 
     636      # Returns a ORDER BY clause for the passed order option. 
     637      # 
    409638      # PostgreSQL does not allow arbitrary ordering when using DISTINCT ON, so we work around this 
    410639      # by wrapping the sql as a sub-select and ordering in that query. 
    411       def add_order_by_for_association_limiting!(sql, options) 
     640      def add_order_by_for_association_limiting!(sql, options) #:nodoc: 
    412641        return sql if options[:order].blank? 
    413          
     642 
    414643        order = options[:order].split(',').collect { |s| s.strip }.reject(&:blank?) 
    415644        order.map! { |s| 'DESC' if s =~ /\bdesc$/i } 
    416645        order = order.zip((0...order.size).to_a).map { |s,i| "id_list.alias_#{i} #{s}" }.join(', ') 
    417          
    418         sql.replace "SELECT * FROM (#{sql}) AS id_list ORDER BY #{order}" 
     646 
     647        sql.replace("SELECT * FROM (#{sql}) AS id_list ORDER BY #{order}") 
    419648      end 
    420649 
     650      protected 
     651        # Returns the version of the connected PostgreSQL version. 
     652        def postgresql_version 
     653          @postgresql_version ||= 
     654            if @connection.respond_to?(:server_version) 
     655              @connection.server_version 
     656            else 
     657              # Mimic PGconn.server_version behavior 
     658              begin 
     659                query('SELECT version()')[0][0] =~ /PostgreSQL (\d+)\.(\d+)\.(\d+)/ 
     660                ($1.to_i * 10000) + ($2.to_i * 100) + $3.to_i 
     661              rescue 
     662                0 
     663              end 
     664            end 
     665        end 
     666 
    421667      private 
    422         BYTEA_COLUMN_TYPE_OID = 17 
    423         NUMERIC_COLUMN_TYPE_OID = 1700 
    424         TIMESTAMPOID = 1114 
    425         TIMESTAMPTZOID = 1184 
     668        # The internal PostgreSQL identifer of the money data type. 
     669        MONEY_COLUMN_TYPE_OID = 790 #:nodoc: 
    426670 
     671        # Connects to a PostgreSQL server and sets up the adapter depending on the 
     672        # connected server's characteristics. 
     673        def connect 
     674          @connection = PGconn.connect(*@connection_parameters) 
     675          PGconn.translate_results = false if PGconn.respond_to?(:translate_results=) 
     676 
     677          # Ignore async_exec and async_query when using postgres-pr. 
     678          @async = @config[:allow_concurrency] && @connection.respond_to?(:async_exec) 
     679 
     680          # Use escape string syntax if available. We cannot do this lazily when encountering 
     681          # the first string, because that could then break any transactions in progress. 
     682          # See: http://www.postgresql.org/docs/current/static/runtime-config-compatible.html 
     683          begin 
     684            execute('SHOW standard_conforming_strings') 
     685            self.class.instance_eval do 
     686              define_method(:quoted_string_prefix) { 'E' } 
     687            end 
     688          rescue ActiveRecord::StatementInvalid 
     689            # If PostgreSQL doesn't know the standard_conforming_strings parameter then it doesn't 
     690            # support escape string syntax. Don't override the inherited quoted_string_prefix. 
     691          end 
     692 
     693          # Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of 
     694          # PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision 
     695          # should know about this but can't detect it there, so deal with it here. 
     696          money_precision = (postgresql_version >= 80300) ? 19 : 10 
     697          PostgreSQLColumn.module_eval(<<-end_eval) 
     698            def extract_precision(sql_type) 
     699              if sql_type =~ /^money$/ 
     700                #{money_precision} 
     701              else 
     702                super 
     703              end 
     704            end 
     705          end_eval 
     706 
     707          configure_connection 
     708        end 
     709 
     710        # Configures the encoding, verbosity, and schema search path of the connection. 
     711        # This is called by #connect and should not be called manually. 
    427712        def configure_connection 
    428713          if @config[:encoding] 
    429             execute("SET client_encoding TO '#{@config[:encoding]}'") 
     714            if @connection.respond_to?(:set_client_encoding) 
     715              @connection.set_client_encoding(@config[:encoding]) 
     716            else 
     717              execute("SET client_encoding TO '#{@config[:encoding]}'") 
     718            end 
    430719          end 
    431720          if @config[:min_messages] 
    432721            execute("SET client_min_messages TO '#{@config[:min_messages]}'") 
    433722          end 
     723          self.schema_search_path = @config[:schema_search_path] || @config[:schema_order] 
    434724        end 
    435725 
    436         def last_insert_id(table, sequence_name) 
    437           Integer(select_value("SELECT currval('#{sequence_name}')")) 
     726        # Returns the current ID of a table's sequence. 
     727        def last_insert_id(table, sequence_name) #:nodoc: 
     728          select_value("SELECT currval('#{sequence_name}')").to_i 
    438729        end 
    439730 
     731        # Executes a SELECT query and returns the results, performing any data type 
     732        # conversions that require to be performed here instead of in PostgreSQLColumn. 
    440733        def select(sql, name = nil) 
    441734          res = execute(sql, name) 
    442735          results = res.result 
     
    445738            fields = res.fields 
    446739            results.each do |row| 
    447740              hashed_row = {} 
    448               row.each_index do |cel_index| 
    449                 column = row[cel_index] 
     741              row.each_index do |cell_index| 
     742                column = row[cell_index] 
    450743 
    451                 case res.type(cel_index) 
    452                   when BYTEA_COLUMN_TYPE_OID 
    453                     column = unescape_bytea(column) 
    454                   when NUMERIC_COLUMN_TYPE_OID 
    455                     column = column.to_d if column.respond_to?(:to_d) 
     744                # If this is a money type column and there are any currency symbols, 
     745                # then strip them off. Indeed it would be prettier to do this in 
     746                # PostgresSQLColumn.string_to_decimal but would break form input 
     747                # fields that call value_before_type_cast. 
     748                if res.type(cell_index) == MONEY_COLUMN_TYPE_OID 
     749                  # Because money output is formatted according to the locale, there are two 
     750                  # cases to consider (note the decimal seperators): 
     751                  #  (1) $12,345,678.12         
     752                  #  (2) $12.345.678,12 
     753                  case column 
     754                    when /^-?\D+[\d,]+\.\d{2}$/  # (1) 
     755                      column = column.gsub(/[^-\d\.]/, '') 
     756                    when /^-?\D+[\d\.]+,\d{2}$/  # (2) 
     757                      column = column.gsub(/[^-\d,]/, '').sub(/,/, '.') 
     758                  end 
    456759                end 
    457760 
    458                 hashed_row[fields[cel_index]] = column 
     761                hashed_row[fields[cell_index]] = column 
    459762              end 
    460763              rows << hashed_row 
    461764            end 
     
    464767          return rows 
    465768        end 
    466769 
    467         def escape_bytea(s) 
    468           if PGconn.respond_to? :escape_bytea 
    469             self.class.send(:define_method, :escape_bytea) do |s| 
    470               PGconn.escape_bytea(s) if s 
    471             end 
    472           else 
    473             self.class.send(:define_method, :escape_bytea) do |s| 
    474               if s 
    475                 result = '' 
    476                 s.each_byte { |c| result << sprintf('\\\\%03o', c) } 
    477                 result 
    478               end 
    479             end 
    480           end 
    481           escape_bytea(s) 
    482         end 
    483  
    484         def unescape_bytea(s) 
    485           if PGconn.respond_to? :unescape_bytea 
    486             self.class.send(:define_method, :unescape_bytea) do |s| 
    487               PGconn.unescape_bytea(s) if s 
    488             end 
    489           else 
    490             self.class.send(:define_method, :unescape_bytea) do |s| 
    491               if s 
    492                 result = '' 
    493                 i, max = 0, s.size 
    494                 while i < max 
    495                   char = s[i] 
    496                   if char == ?\\ 
    497                     if s[i+1] == ?\\ 
    498                       char = ?\\ 
    499                       i += 1 
    500                     else 
    501                       char = s[i+1..i+3].oct 
    502                       i += 3 
    503                     end 
    504                   end 
    505                   result << char 
    506                   i += 1 
    507                 end 
    508                 result 
    509               end 
    510             end 
    511           end 
    512           unescape_bytea(s) 
    513         end 
    514  
    515         # Query a table's column names, default values, and types. 
     770        # Returns the list of a table's column names, data types, and default values. 
    516771        # 
    517772        # The underlying query is roughly: 
    518773        #  SELECT column.name, column.type, default.value 
     
    530785        # Query implementation notes: 
    531786        #  - format_type includes the column size constraint, e.g. varchar(50) 
    532787        #  - ::regclass is a function that gives the id for a table name 
    533         def column_definitions(table_name) 
    534           query <<-end_sql 
     788        def column_definitions(table_name) #:nodoc: 
     789          query(<<-end_sql) 
    535790            SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull 
    536791              FROM pg_attribute a LEFT JOIN pg_attrdef d 
    537                 ON a.attrelid = d.adrelid AND a.attnum = d.adnum 
     792                ON a.attrelid = d.adrelid 
     793                AND a.attnum = d.adnum 
    538794             WHERE a.attrelid = '#{table_name}'::regclass 
    539                AND a.attnum > 0 AND NOT a.attisdropped 
     795               AND a.attnum > 0 
     796               AND NOT a.attisdropped 
    540797             ORDER BY a.attnum 
    541798          end_sql 
    542799        end 
    543  
    544         # Translate PostgreSQL-specific types into simplified SQL types. 
    545         # These are special cases; standard types are handled by 
    546         # ConnectionAdapters::Column#simplified_type. 
    547         def translate_field_type(field_type) 
    548           # Match the beginning of field_type since it may have a size constraint on the end. 
    549           case field_type 
    550             # PostgreSQL array data types. 
    551             when /\[\]$/i  then 'string' 
    552             when /^timestamp/i    then 'datetime'