postgresql_adapter.rb 36.8 KB
Newer Older
D
Initial  
David Heinemeier Hansson 已提交
1
require 'active_record/connection_adapters/abstract_adapter'
J
Jeremy Kemper 已提交
2
require 'active_support/core_ext/kernel/requires'
3
require 'active_support/core_ext/object/blank'
D
Initial  
David Heinemeier Hansson 已提交
4 5 6 7 8

module ActiveRecord
  class Base
    # Establishes a connection to the database that's used by all Active Record objects
    def self.postgresql_connection(config) # :nodoc:
9 10
      require 'pg'

11
      config = config.symbolize_keys
D
Initial  
David Heinemeier Hansson 已提交
12
      host     = config[:host]
13
      port     = config[:port] || 5432
14 15
      username = config[:username].to_s if config[:username]
      password = config[:password].to_s if config[:password]
D
Initial  
David Heinemeier Hansson 已提交
16 17 18 19 20 21 22

      if config.has_key?(:database)
        database = config[:database]
      else
        raise ArgumentError, "No database specified. Missing argument: database."
      end

23
      # The postgres drivers don't allow the creation of an unconnected PGconn object,
24 25 26 27
      # so just pass a nil connection object for the time being.
      ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, [host, port, nil, nil, database, username, password], config)
    end
  end
28

29
  module ConnectionAdapters
30 31 32 33 34 35
    class TableDefinition
      def xml(*args)
        options = args.extract_options!
        column(args[0], 'xml', options)
      end
    end
36 37 38 39 40 41
    # PostgreSQL-specific extensions to column definitions in a table.
    class PostgreSQLColumn < Column #:nodoc:
      # Instantiates a new PostgreSQL column definition in a table.
      def initialize(name, default, sql_type = nil, null = true)
        super(name, self.class.extract_value_from_default(default), sql_type, null)
      end
42

43 44 45 46 47 48
      # :stopdoc:
      class << self
        attr_accessor :money_precision
      end
      # :startdoc:

49
      private
50
        def extract_limit(sql_type)
51 52 53 54 55
          case sql_type
          when /^bigint/i;    8
          when /^smallint/i;  2
          else super
          end
56 57
        end

58 59 60 61 62
        # Extracts the scale from PostgreSQL-specific data types.
        def extract_scale(sql_type)
          # Money type has a fixed scale of 2.
          sql_type =~ /^money/ ? 2 : super
        end
63

64 65
        # Extracts the precision from PostgreSQL-specific data types.
        def extract_precision(sql_type)
66 67 68 69 70
          if sql_type == 'money'
            self.class.money_precision
          else
            super
          end
71
        end
72

73 74 75 76 77 78 79
        # Maps PostgreSQL-specific data types to logical Rails types.
        def simplified_type(field_type)
          case field_type
            # Numeric and monetary types
            when /^(?:real|double precision)$/
              :float
            # Monetary types
80
            when 'money'
81 82 83 84 85
              :decimal
            # Character types
            when /^(?:character varying|bpchar)(?:\(\d+\))?$/
              :string
            # Binary data types
86
            when 'bytea'
87 88 89 90
              :binary
            # Date/time types
            when /^timestamp with(?:out)? time zone$/
              :datetime
91
            when 'interval'
92 93 94 95 96 97 98 99 100 101 102
              :string
            # Geometric types
            when /^(?:point|line|lseg|box|"?path"?|polygon|circle)$/
              :string
            # Network address types
            when /^(?:cidr|inet|macaddr)$/
              :string
            # Bit strings
            when /^bit(?: varying)?(?:\(\d+\))?$/
              :string
            # XML type
103
            when 'xml'
104
              :xml
105 106
            # Arrays
            when /^\D+\[\]$/
107
              :string
108
            # Object identifier types
109
            when 'oid'
110
              :integer
111
            # UUID type
112
            when 'uuid'
113 114 115 116
              :string
            # Small and big integer types
            when /^(?:small|big)int$/
              :integer
117 118 119 120 121
            # Pass through all types that are not specific to PostgreSQL.
            else
              super
          end
        end
122

123 124 125 126
        # Extracts the value from a PostgreSQL column default definition.
        def self.extract_value_from_default(default)
          case default
            # Numeric types
127 128
            when /\A\(?(-?\d+(\.\d*)?\)?)\z/
              $1
129
            # Character types
130
            when /\A'(.*)'::(?:character varying|bpchar|text)\z/m
131
              $1
132 133 134
            # Character types (8.1 formatting)
            when /\AE'(.*)'::(?:character varying|bpchar|text)\z/m
              $1.gsub(/\\(\d\d\d)/) { $1.oct.chr }
135
            # Binary data types
136
            when /\A'(.*)'::bytea\z/m
137 138
              $1
            # Date/time types
139
            when /\A'(.+)'::(?:time(?:stamp)? with(?:out)? time zone|date)\z/
140
              $1
141
            when /\A'(.*)'::interval\z/
142 143
              $1
            # Boolean type
144
            when 'true'
145
              true
146
            when 'false'
147 148
              false
            # Geometric types
149
            when /\A'(.*)'::(?:point|line|lseg|box|"?path"?|polygon|circle)\z/
150 151
              $1
            # Network address types
152
            when /\A'(.*)'::(?:cidr|inet|macaddr)\z/
153 154
              $1
            # Bit string types
155
            when /\AB'(.*)'::"?bit(?: varying)?"?\z/
156 157
              $1
            # XML type
158
            when /\A'(.*)'::xml\z/m
159 160
              $1
            # Arrays
161
            when /\A'(.*)'::"?\D+"?\[\]\z/
162 163
              $1
            # Object identifier types
164
            when /\A-?\d+\z/
165 166 167
              $1
            else
              # Anything else is blank, some user type, or some function
168
              # and we can't know the value of that, so return nil.
169 170 171
              nil
          end
        end
D
Initial  
David Heinemeier Hansson 已提交
172 173 174 175
    end
  end

  module ConnectionAdapters
176 177
    # The PostgreSQL adapter works both with the native C (http://ruby.scripting.ca/postgres/) and the pure
    # Ruby (available both as gem and from http://rubyforge.org/frs/?group_id=234&release_id=1944) drivers.
178 179 180
    #
    # Options:
    #
P
Pratik Naik 已提交
181 182 183 184 185 186 187 188 189
    # * <tt>:host</tt> - Defaults to "localhost".
    # * <tt>:port</tt> - Defaults to 5432.
    # * <tt>:username</tt> - Defaults to nothing.
    # * <tt>:password</tt> - Defaults to nothing.
    # * <tt>:database</tt> - The name of the database. No default, must be provided.
    # * <tt>:schema_search_path</tt> - An optional schema search path for the connection given as a string of comma-separated schema names.  This is backward-compatible with the <tt>:schema_order</tt> option.
    # * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO <encoding></tt> call on the connection.
    # * <tt>:min_messages</tt> - An optional client min messages that is used in a <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
    # * <tt>:allow_concurrency</tt> - If true, use async query methods so Ruby threads don't deadlock; otherwise, use blocking query methods.
190
    class PostgreSQLAdapter < AbstractAdapter
191 192 193 194 195 196 197 198 199 200 201 202 203 204
      ADAPTER_NAME = 'PostgreSQL'.freeze

      NATIVE_DATABASE_TYPES = {
        :primary_key => "serial primary key".freeze,
        :string      => { :name => "character varying", :limit => 255 },
        :text        => { :name => "text" },
        :integer     => { :name => "integer" },
        :float       => { :name => "float" },
        :decimal     => { :name => "decimal" },
        :datetime    => { :name => "timestamp" },
        :timestamp   => { :name => "timestamp" },
        :time        => { :name => "time" },
        :date        => { :name => "date" },
        :binary      => { :name => "bytea" },
205 206
        :boolean     => { :name => "boolean" },
        :xml         => { :name => "xml" }
207 208
      }

209
      # Returns 'PostgreSQL' as adapter name for identification purposes.
210
      def adapter_name
211
        ADAPTER_NAME
212 213
      end

214 215
      # Initializes and connects a PostgreSQL adapter.
      def initialize(connection, logger, connection_parameters, config)
216
        super(connection, logger)
217
        @connection_parameters, @config = connection_parameters, config
218

219
        connect
220 221
      end

222 223 224
      # Is this connection alive and ready for queries?
      def active?
        if @connection.respond_to?(:status)
225
          @connection.status == PGconn::CONNECTION_OK
226
        else
227
          # We're asking the driver, not Active Record, so use @connection.query instead of #query
228
          @connection.query 'SELECT 1'
229 230
          true
        end
231
      # postgres-pr raises a NoMethodError when querying if no connection is available.
232
      rescue PGError, NoMethodError
233
        false
234 235 236 237 238 239
      end

      # Close then reopen the connection.
      def reconnect!
        if @connection.respond_to?(:reset)
          @connection.reset
240
          configure_connection
241 242 243
        else
          disconnect!
          connect
244 245
        end
      end
246

247
      # Close the connection.
248 249 250
      def disconnect!
        @connection.close rescue nil
      end
251

252
      def native_database_types #:nodoc:
253
        NATIVE_DATABASE_TYPES
254
      end
255

256
      # Does PostgreSQL support migrations?
257 258
      def supports_migrations?
        true
259 260
      end

261
      # Does PostgreSQL support finding primary key on non-Active Record tables?
262 263 264 265
      def supports_primary_key? #:nodoc:
        true
      end

266 267 268 269 270 271
      # Enable standard-conforming strings if available.
      def set_standard_conforming_strings
        old, self.client_min_messages = client_min_messages, 'panic'
        execute('SET standard_conforming_strings = on') rescue nil
      ensure
        self.client_min_messages = old
272 273
      end

274
      def supports_insert_with_returning?
275
        postgresql_version >= 80200
276 277
      end

278 279 280
      def supports_ddl_transactions?
        true
      end
281

282 283 284
      def supports_savepoints?
        true
      end
285

286 287
      # Returns the configured supported identifier length supported by PostgreSQL,
      # or report the default of 63 on PostgreSQL 7.x.
288
      def table_alias_length
289
        @table_alias_length ||= (postgresql_version >= 80000 ? query('SHOW max_identifier_length')[0][0].to_i : 63)
290
      end
291

292 293
      # QUOTING ==================================================

294
      # Escapes binary strings for bytea input to the database.
295 296
      def escape_bytea(value)
        @connection.escape_bytea(value) if value
297 298 299 300 301
      end

      # Unescapes bytea output from a database to the binary string it represents.
      # NOTE: This is NOT an inverse of escape_bytea! This is only to be used
      #       on escaped binary output from database drive.
302 303
      def unescape_bytea(value)
        @connection.unescape_bytea(value) if value
304 305
      end

306 307
      # Quotes PostgreSQL-specific data types for SQL input.
      def quote(value, column = nil) #:nodoc:
308
        if value.kind_of?(String) && column && column.type == :binary
309
          "'#{escape_bytea(value)}'"
310
        elsif value.kind_of?(String) && column && column.sql_type == 'xml'
311
          "xml '#{quote_string(value)}'"
312
        elsif value.kind_of?(Numeric) && column && column.sql_type == 'money'
313 314 315 316 317 318 319 320 321
          # Not truly string input, so doesn't require (or allow) escape string syntax.
          "'#{value.to_s}'"
        elsif value.kind_of?(String) && column && column.sql_type =~ /^bit/
          case value
            when /^[01]*$/
              "B'#{value}'" # Bit-string notation
            when /^[0-9A-F]*$/i
              "X'#{value}'" # Hexadecimal notation
          end
322 323 324 325 326
        else
          super
        end
      end

327 328 329
      # Quotes strings for use in SQL input.
      def quote_string(s) #:nodoc:
        @connection.escape(s)
330 331
      end

332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
      # Checks the following cases:
      #
      # - table_name
      # - "table.name"
      # - schema_name.table_name
      # - schema_name."table.name"
      # - "schema.name".table_name
      # - "schema.name"."table.name"
      def quote_table_name(name)
        schema, name_part = extract_pg_identifier_from_name(name.to_s)

        unless name_part
          quote_column_name(schema)
        else
          table_name, name_part = extract_pg_identifier_from_name(name_part)
          "#{quote_column_name(schema)}.#{quote_column_name(table_name)}"
        end
      end

351 352
      # Quotes column names for use in SQL queries.
      def quote_column_name(name) #:nodoc:
353
        PGconn.quote_ident(name.to_s)
354 355
      end

356 357 358
      # Quote date/time values for use in SQL input. Includes microseconds
      # if the value is a Time responding to usec.
      def quoted_date(value) #:nodoc:
359 360 361 362 363
        if value.acts_like?(:time) && value.respond_to?(:usec)
          "#{super}.#{sprintf("%06d", value.usec)}"
        else
          super
        end
364 365
      end

366 367
      # REFERENTIAL INTEGRITY ====================================

368 369 370 371 372 373 374
      def supports_disable_referential_integrity?() #:nodoc:
        version = query("SHOW server_version")[0][0].split('.')
        (version[0].to_i >= 8 && version[1].to_i >= 1) ? true : false
      rescue
        return false
      end

375
      def disable_referential_integrity(&block) #:nodoc:
376 377 378
        if supports_disable_referential_integrity?() then
          execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
        end
379 380
        yield
      ensure
381 382 383
        if supports_disable_referential_integrity?() then
          execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";"))
        end
384
      end
385 386 387

      # DATABASE STATEMENTS ======================================

388 389 390 391 392 393
      # Executes a SELECT query and returns an array of rows. Each row is an
      # array of field values.
      def select_rows(sql, name = nil)
        select_raw(sql, name).last
      end

394
      # Executes an INSERT query and returns the new record's ID
395
      def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
396 397 398 399 400 401 402 403 404 405 406 407 408 409
        # Extract the table from the insert sql. Yuck.
        table = sql.split(" ", 4)[2].gsub('"', '')

        # Try an insert with 'returning id' if available (PG >= 8.2)
        if supports_insert_with_returning?
          pk, sequence_name = *pk_and_sequence_for(table) unless pk
          if pk
            id = select_value("#{sql} RETURNING #{quote_column_name(pk)}")
            clear_query_cache
            return id
          end
        end

        # Otherwise, insert then grab last_insert_id.
410 411 412 413 414 415 416 417 418 419 420 421 422 423
        if insert_id = super
          insert_id
        else
          # If neither pk nor sequence name is given, look them up.
          unless pk || sequence_name
            pk, sequence_name = *pk_and_sequence_for(table)
          end

          # If a pk is given, fallback to default sequence name.
          # Don't fetch last insert id for a table without a pk.
          if pk && sequence_name ||= default_sequence_name(table, pk)
            last_insert_id(table, sequence_name)
          end
        end
424
      end
425
      alias :create :insert
426

427 428
      # create a 2D array representing the result set
      def result_as_array(res) #:nodoc:
429 430 431 432
        # check if we have any binary column and if they need escaping
        unescape_col = []
        for j in 0...res.nfields do
          # unescape string passed BYTEA field (OID == 17)
433
          unescape_col << ( res.ftype(j)==17 )
434 435
        end

436 437 438 439
        ary = []
        for i in 0...res.ntuples do
          ary << []
          for j in 0...res.nfields do
440 441 442
            data = res.getvalue(i,j)
            data = unescape_bytea(data) if unescape_col[j] and data.is_a?(String)
            ary[i] << data
443 444 445 446 447 448 449
          end
        end
        return ary
      end


      # Queries the database and returns the results in an Array-like object
450
      def query(sql, name = nil) #:nodoc:
451 452
        log(sql, name) do
          if @async
453
            res = @connection.async_exec(sql)
454
          else
455
            res = @connection.exec(sql)
456
          end
457
          return result_as_array(res)
458
        end
459 460
      end

461
      # Executes an SQL statement, returning a PGresult object on success
462 463
      # or raising a PGError exception otherwise.
      def execute(sql, name = nil)
464 465 466 467 468 469 470
        log(sql, name) do
          if @async
            @connection.async_exec(sql)
          else
            @connection.exec(sql)
          end
        end
471 472
      end

473
      # Executes an UPDATE query and returns the number of affected tuples.
474
      def update_sql(sql, name = nil)
475
        super.cmd_tuples
476 477
      end

478 479
      # Begins a transaction.
      def begin_db_transaction
480 481 482
        execute "BEGIN"
      end

483 484
      # Commits a transaction.
      def commit_db_transaction
485 486
        execute "COMMIT"
      end
487

488 489
      # Aborts a transaction.
      def rollback_db_transaction
490 491
        execute "ROLLBACK"
      end
492

493 494
      def outside_transaction?
        @connection.transaction_status == PGconn::PQTRANS_IDLE
495
      end
496

J
Jonathan Viney 已提交
497 498 499 500 501 502 503 504
      def create_savepoint
        execute("SAVEPOINT #{current_savepoint_name}")
      end

      def rollback_to_savepoint
        execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
      end

505
      def release_savepoint
J
Jonathan Viney 已提交
506 507
        execute("RELEASE SAVEPOINT #{current_savepoint_name}")
      end
508

509 510
      # SCHEMA STATEMENTS ========================================

511 512 513 514 515
      def recreate_database(name) #:nodoc:
        drop_database(name)
        create_database(name)
      end

516 517 518
      # Create a new PostgreSQL database.  Options include <tt>:owner</tt>, <tt>:template</tt>,
      # <tt>:encoding</tt>, <tt>:tablespace</tt>, and <tt>:connection_limit</tt> (note that MySQL uses
      # <tt>:charset</tt> while PostgreSQL uses <tt>:encoding</tt>).
519 520 521 522 523 524 525 526 527 528
      #
      # Example:
      #   create_database config[:database], config
      #   create_database 'foo_development', :encoding => 'unicode'
      def create_database(name, options = {})
        options = options.reverse_merge(:encoding => "utf8")

        option_string = options.symbolize_keys.sum do |key, value|
          case key
          when :owner
529
            " OWNER = \"#{value}\""
530
          when :template
531
            " TEMPLATE = \"#{value}\""
532 533 534
          when :encoding
            " ENCODING = '#{value}'"
          when :tablespace
535
            " TABLESPACE = \"#{value}\""
536 537 538 539 540 541 542
          when :connection_limit
            " CONNECTION LIMIT = #{value}"
          else
            ""
          end
        end

543
        execute "CREATE DATABASE #{quote_table_name(name)}#{option_string}"
544 545 546 547 548 549 550
      end

      # Drops a PostgreSQL database
      #
      # Example:
      #   drop_database 'matt_development'
      def drop_database(name) #:nodoc:
551 552 553 554 555 556 557 558 559
        if postgresql_version >= 80200
          execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
        else
          begin
            execute "DROP DATABASE #{quote_table_name(name)}"
          rescue ActiveRecord::StatementInvalid
            @logger.warn "#{name} database doesn't exist." if @logger
          end
        end
560 561
      end

562 563
      # Returns the list of all tables in the schema search path or a specified schema.
      def tables(name = nil)
564 565
        query(<<-SQL, name).map { |row| row[0] }
          SELECT tablename
566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586
          FROM pg_tables
          WHERE schemaname = ANY (current_schemas(false))
        SQL
      end

      def table_exists?(name)
        name          = name.to_s
        schema, table = name.split('.', 2)

        unless table # A table was provided without a schema
          table  = schema
          schema = nil
        end

        if name =~ /^"/ # Handle quoted table names
          table  = name
          schema = nil
        end

        query(<<-SQL).first[0].to_i > 0
            SELECT COUNT(*)
587
            FROM pg_tables
588 589
            WHERE tablename = '#{table.gsub(/(^"|"$)/,'')}'
            #{schema ? "AND schemaname = '#{schema}'" : ''}
590 591 592
        SQL
      end

593 594
      # Returns the list of all indexes for a table.
      def indexes(table_name, name = nil)
595 596
         schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',')
         result = query(<<-SQL, name)
597 598
           SELECT distinct i.relname, d.indisunique, d.indkey, t.oid
             FROM pg_class t, pg_class i, pg_index d
599 600 601 602 603
           WHERE i.relkind = 'i'
             AND d.indexrelid = i.oid
             AND d.indisprimary = 'f'
             AND t.oid = d.indrelid
             AND t.relname = '#{table_name}'
604
             AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname IN (#{schemas}) )
605 606 607
          ORDER BY i.relname
        SQL

608

609 610
        indexes = []

611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
        indexes = result.map do |row|
          index_name = row[0]
          unique = row[1] == 't'
          indkey = row[2].split(" ")
          oid = row[3]

          columns = query(<<-SQL, "Columns for index #{row[0]} on #{table_name}").inject({}) {|attlist, r| attlist[r[1]] = r[0]; attlist}
          SELECT a.attname, a.attnum
          FROM pg_attribute a
          WHERE a.attrelid = #{oid}
          AND a.attnum IN (#{indkey.join(",")})
          SQL

          column_names = indkey.map {|attnum| columns[attnum] }
          IndexDefinition.new(table_name, index_name, unique, column_names)
626 627 628 629 630 631

        end

        indexes
      end

632 633
      # Returns the list of all column definitions for a table.
      def columns(table_name, name = nil)
634
        # Limit, precision, and scale are all handled by the superclass.
635 636
        column_definitions(table_name).collect do |name, type, default, notnull|
          PostgreSQLColumn.new(name, default, type, notnull == 'f')
D
Initial  
David Heinemeier Hansson 已提交
637 638 639
        end
      end

640 641 642 643 644 645 646 647 648 649 650 651 652
      # Returns the current database name.
      def current_database
        query('select current_database()')[0][0]
      end

      # Returns the current database encoding format.
      def encoding
        query(<<-end_sql)[0][0]
          SELECT pg_encoding_to_char(pg_database.encoding) FROM pg_database
          WHERE pg_database.datname LIKE '#{current_database}'
        end_sql
      end

653 654 655 656 657 658
      # Sets the schema search path to a string of comma-separated schema names.
      # Names beginning with $ have to be quoted (e.g. $user => '$user').
      # See: http://www.postgresql.org/docs/current/static/ddl-schemas.html
      #
      # This should be not be called manually but set in database.yml.
      def schema_search_path=(schema_csv)
659 660
        if schema_csv
          execute "SET search_path TO #{schema_csv}"
661
          @schema_search_path = schema_csv
662
        end
D
Initial  
David Heinemeier Hansson 已提交
663 664
      end

665 666
      # Returns the active schema search path.
      def schema_search_path
667
        @schema_search_path ||= query('SHOW search_path')[0][0]
668
      end
669

670 671 672 673 674 675 676 677 678 679 680 681
      # Returns the current client message level.
      def client_min_messages
        query('SHOW client_min_messages')[0][0]
      end

      # Set the client message level.
      def client_min_messages=(level)
        execute("SET client_min_messages TO '#{level}'")
      end

      # Returns the sequence name for a table's primary key or some other specified key.
      def default_sequence_name(table_name, pk = nil) #:nodoc:
682
        default_pk, default_seq = pk_and_sequence_for(table_name)
683
        default_seq || "#{table_name}_#{pk || default_pk || 'id'}_seq"
684 685
      end

686 687
      # Resets the sequence of a table's primary key to the maximum value.
      def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc:
688 689 690 691 692 693 694
        unless pk and sequence
          default_pk, default_sequence = pk_and_sequence_for(table)
          pk ||= default_pk
          sequence ||= default_sequence
        end
        if pk
          if sequence
695 696
            quoted_sequence = quote_column_name(sequence)

697
            select_value <<-end_sql, 'Reset sequence'
698
              SELECT setval('#{quoted_sequence}', (SELECT COALESCE(MAX(#{quote_column_name pk})+(SELECT increment_by FROM #{quoted_sequence}), (SELECT min_value FROM #{quoted_sequence})) FROM #{quote_table_name(table)}), false)
699 700 701 702
            end_sql
          else
            @logger.warn "#{table} has primary key #{pk} with no default sequence" if @logger
          end
703 704 705
        end
      end

706 707
      # Returns a table's primary key and belonging sequence.
      def pk_and_sequence_for(table) #:nodoc:
708 709
        # First try looking for a sequence with a dependency on the
        # given table's primary key.
710
        result = query(<<-end_sql, 'PK and serial sequence')[0]
711
          SELECT attr.attname, seq.relname
712 713 714 715 716 717 718 719 720 721 722 723
          FROM pg_class      seq,
               pg_attribute  attr,
               pg_depend     dep,
               pg_namespace  name,
               pg_constraint cons
          WHERE seq.oid           = dep.objid
            AND seq.relkind       = 'S'
            AND attr.attrelid     = dep.refobjid
            AND attr.attnum       = dep.refobjsubid
            AND attr.attrelid     = cons.conrelid
            AND attr.attnum       = cons.conkey[1]
            AND cons.contype      = 'p'
724
            AND dep.refobjid      = '#{quote_table_name(table)}'::regclass
725
        end_sql
726 727 728 729 730

        if result.nil? or result.empty?
          # If that fails, try parsing the primary key's default value.
          # Support the 7.x and 8.0 nextval('foo'::text) as well as
          # the 8.1+ nextval('foo'::regclass).
731
          result = query(<<-end_sql, 'PK and custom sequence')[0]
732 733 734 735 736 737 738
            SELECT attr.attname,
              CASE
                WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN
                  substr(split_part(def.adsrc, '''', 2),
                         strpos(split_part(def.adsrc, '''', 2), '.')+1)
                ELSE split_part(def.adsrc, '''', 2)
              END
739 740 741 742
            FROM pg_class       t
            JOIN pg_attribute   attr ON (t.oid = attrelid)
            JOIN pg_attrdef     def  ON (adrelid = attrelid AND adnum = attnum)
            JOIN pg_constraint  cons ON (conrelid = adrelid AND adnum = conkey[1])
743
            WHERE t.oid = '#{quote_table_name(table)}'::regclass
744
              AND cons.contype = 'p'
745
              AND def.adsrc ~* 'nextval'
746 747
          end_sql
        end
748

749
        # [primary_key, sequence]
750
        [result.first, result.last]
751 752
      rescue
        nil
753 754
      end

755 756 757 758 759 760
      # Returns just a table's primary key
      def primary_key(table)
        pk_and_sequence = pk_and_sequence_for(table)
        pk_and_sequence && pk_and_sequence.first
      end

761
      # Renames a table.
762
      def rename_table(name, new_name)
763
        execute "ALTER TABLE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}"
764
      end
765

766 767
      # Adds a new column to the named table.
      # See TableDefinition#column for details of the options you can use.
S
Scott Barron 已提交
768
      def add_column(table_name, column_name, type, options = {})
769 770 771 772
        default = options[:default]
        notnull = options[:null] == false

        # Add the column.
773
        execute("ALTER TABLE #{quote_table_name(table_name)} ADD COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}")
774

775 776
        change_column_default(table_name, column_name, default) if options_include_default?(options)
        change_column_null(table_name, column_name, false, default) if notnull
S
Scott Barron 已提交
777
      end
D
Initial  
David Heinemeier Hansson 已提交
778

779 780
      # Changes the column of a table.
      def change_column(table_name, column_name, type, options = {})
781 782
        quoted_table_name = quote_table_name(table_name)

783
        begin
784
          execute "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
785 786
        rescue ActiveRecord::StatementInvalid => e
          raise e if postgresql_version > 80000
787
          # This is PostgreSQL 7.x, so we have to use a more arcane way of doing it.
788 789 790 791 792 793 794 795 796 797 798
          begin
            begin_db_transaction
            tmp_column_name = "#{column_name}_ar_tmp"
            add_column(table_name, tmp_column_name, type, options)
            execute "UPDATE #{quoted_table_name} SET #{quote_column_name(tmp_column_name)} = CAST(#{quote_column_name(column_name)} AS #{type_to_sql(type, options[:limit], options[:precision], options[:scale])})"
            remove_column(table_name, column_name)
            rename_column(table_name, tmp_column_name, column_name)
            commit_db_transaction
          rescue
            rollback_db_transaction
          end
799
        end
800

801 802
        change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
        change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
803
      end
804

805 806
      # Changes the default value of a table column.
      def change_column_default(table_name, column_name, default)
807
        execute "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote(default)}"
808
      end
809

810 811
      def change_column_null(table_name, column_name, null, default = nil)
        unless null || default.nil?
812
          execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
813
        end
814
        execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
815 816
      end

817 818
      # Renames a column in a table.
      def rename_column(table_name, column_name, new_column_name)
819
        execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
820
      end
821

822 823 824 825 826 827
      def remove_index!(table_name, index_name) #:nodoc:
        execute "DROP INDEX #{quote_table_name(index_name)}"
      end

      def index_name_length
        63
828
      end
829

830 831
      # Maps logical Rails types to PostgreSQL-specific data types.
      def type_to_sql(type, limit = nil, precision = nil, scale = nil)
832 833
        return super unless type.to_s == 'integer'

834 835 836 837
        case limit
          when 1..2;      'smallint'
          when 3..4, nil; 'integer'
          when 5..8;      'bigint'
838
          else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
839 840
        end
      end
841

842
      # Returns a SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
843 844 845
      #
      # PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
      # requires that the ORDER BY include the distinct column.
846
      #
847
      #   distinct("posts.id", "posts.created_at desc")
848
      def distinct(columns, order_by) #:nodoc:
849 850
        return "DISTINCT #{columns}" if order_by.blank?

851 852
        # Construct a clean list of column names from the ORDER BY clause, removing
        # any ASC/DESC modifiers
853
        order_columns = order_by.split(',').collect { |s| s.split.first }
854
        order_columns.delete_if(&:blank?)
855
        order_columns = order_columns.zip((0...order_columns.size).to_a).map { |s,i| "#{s} AS alias_#{i}" }
856

857 858
        # Return a DISTINCT ON() clause that's distinct on the columns we want but includes
        # all the required columns for the ORDER BY to work properly.
859 860
        sql = "DISTINCT ON (#{columns}) #{columns}, "
        sql << order_columns * ', '
861
      end
862

863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879
      protected
        # Returns the version of the connected PostgreSQL version.
        def postgresql_version
          @postgresql_version ||=
            if @connection.respond_to?(:server_version)
              @connection.server_version
            else
              # Mimic PGconn.server_version behavior
              begin
                query('SELECT version()')[0][0] =~ /PostgreSQL (\d+)\.(\d+)\.(\d+)/
                ($1.to_i * 10000) + ($2.to_i * 100) + $3.to_i
              rescue
                0
              end
            end
        end

880 881 882
        def translate_exception(exception, message)
          case exception.message
          when /duplicate key value violates unique constraint/
883
            RecordNotUnique.new(message, exception)
884
          when /violates foreign key constraint/
885
            InvalidForeignKey.new(message, exception)
886 887 888 889 890
          else
            super
          end
        end

D
Initial  
David Heinemeier Hansson 已提交
891
      private
P
Pratik Naik 已提交
892
        # The internal PostgreSQL identifier of the money data type.
893 894 895 896 897 898 899 900 901 902 903 904 905 906
        MONEY_COLUMN_TYPE_OID = 790 #:nodoc:

        # Connects to a PostgreSQL server and sets up the adapter depending on the
        # connected server's characteristics.
        def connect
          @connection = PGconn.connect(*@connection_parameters)
          PGconn.translate_results = false if PGconn.respond_to?(:translate_results=)

          # Ignore async_exec and async_query when using postgres-pr.
          @async = @config[:allow_concurrency] && @connection.respond_to?(:async_exec)

          # Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of
          # PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision
          # should know about this but can't detect it there, so deal with it here.
907 908
          PostgreSQLColumn.money_precision = (postgresql_version >= 80300) ? 19 : 10

909 910 911
          configure_connection
        end

912
        # Configures the encoding, verbosity, schema search path, and time zone of the connection.
913
        # This is called by #connect and should not be called manually.
914 915
        def configure_connection
          if @config[:encoding]
916 917 918 919 920
            if @connection.respond_to?(:set_client_encoding)
              @connection.set_client_encoding(@config[:encoding])
            else
              execute("SET client_encoding TO '#{@config[:encoding]}'")
            end
921
          end
922 923
          self.client_min_messages = @config[:min_messages] if @config[:min_messages]
          self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
924 925 926 927

          # Use standard-conforming strings if available so we don't have to do the E'...' dance.
          set_standard_conforming_strings

928
          # If using Active Record's time zone support configure the connection to return
929 930
          # TIMESTAMP WITH ZONE types in UTC.
          execute("SET time zone 'UTC'") if ActiveRecord::Base.default_timezone == :utc
931 932
        end

933 934
        # Returns the current ID of a table's sequence.
        def last_insert_id(table, sequence_name) #:nodoc:
935
          Integer(select_value("SELECT currval('#{sequence_name}')"))
D
Initial  
David Heinemeier Hansson 已提交
936 937
        end

938
        # Executes a SELECT query and returns the results, performing any data type
939
        # conversions that are required to be performed here instead of in PostgreSQLColumn.
D
Initial  
David Heinemeier Hansson 已提交
940
        def select(sql, name = nil)
941 942 943 944 945 946 947 948 949 950 951 952 953
          fields, rows = select_raw(sql, name)
          result = []
          for row in rows
            row_hash = {}
            fields.each_with_index do |f, i|
              row_hash[f] = row[i]
            end
            result << row_hash
          end
          result
        end

        def select_raw(sql, name = nil)
954
          res = execute(sql, name)
955
          results = result_as_array(res)
956
          fields = []
M
Marcel Molina 已提交
957
          rows = []
958
          if res.ntuples > 0
M
Marcel Molina 已提交
959 960 961
            fields = res.fields
            results.each do |row|
              hashed_row = {}
962 963 964
              row.each_index do |cell_index|
                # If this is a money type column and there are any currency symbols,
                # then strip them off. Indeed it would be prettier to do this in
965
                # PostgreSQLColumn.string_to_decimal but would break form input
966
                # fields that call value_before_type_cast.
967
                if res.ftype(cell_index) == MONEY_COLUMN_TYPE_OID
968
                  # Because money output is formatted according to the locale, there are two
969
                  # cases to consider (note the decimal separators):
970
                  #  (1) $12,345,678.12
971
                  #  (2) $12.345.678,12
972
                  case column = row[cell_index]
973
                    when /^-?\D+[\d,]+\.\d{2}$/  # (1)
974
                      row[cell_index] = column.gsub(/[^-\d\.]/, '')
975
                    when /^-?\D+[\d\.]+,\d{2}$/  # (2)
976
                      row[cell_index] = column.gsub(/[^-\d,]/, '').sub(/,/, '.')
977
                  end
M
Marcel Molina 已提交
978
                end
979

980
                hashed_row[fields[cell_index]] = column
M
Marcel Molina 已提交
981
              end
982
              rows << row
M
Marcel Molina 已提交
983 984
            end
          end
985
          res.clear
986
          return fields, rows
M
Marcel Molina 已提交
987 988
        end

989
        # Returns the list of a table's column names, data types, and default values.
990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006
        #
        # The underlying query is roughly:
        #  SELECT column.name, column.type, default.value
        #    FROM column LEFT JOIN default
        #      ON column.table_id = default.table_id
        #     AND column.num = default.column_num
        #   WHERE column.table_id = get_table_id('table_name')
        #     AND column.num > 0
        #     AND NOT column.is_dropped
        #   ORDER BY column.num
        #
        # If the table name is not prefixed with a schema, the database will
        # take the first match from the schema search path.
        #
        # Query implementation notes:
        #  - format_type includes the column size constraint, e.g. varchar(50)
        #  - ::regclass is a function that gives the id for a table name
1007
        def column_definitions(table_name) #:nodoc:
1008
          query <<-end_sql
1009
            SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull
1010 1011
              FROM pg_attribute a LEFT JOIN pg_attrdef d
                ON a.attrelid = d.adrelid AND a.attnum = d.adnum
1012
             WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
1013 1014 1015
               AND a.attnum > 0 AND NOT a.attisdropped
             ORDER BY a.attnum
          end_sql
D
Initial  
David Heinemeier Hansson 已提交
1016
        end
1017 1018 1019 1020 1021 1022 1023 1024 1025 1026

        def extract_pg_identifier_from_name(name)
          match_data = name[0,1] == '"' ? name.match(/\"([^\"]+)\"/) : name.match(/([^\.]+)/)

          if match_data
            rest = name[match_data[0].length..-1]
            rest = rest[1..-1] if rest[0,1] == "."
            [match_data[1], (rest.length > 0 ? rest : nil)]
          end
        end
D
Initial  
David Heinemeier Hansson 已提交
1027 1028 1029
    end
  end
end
1030