postgresql_adapter.rb 26.9 KB
Newer Older
D
Initial  
David Heinemeier Hansson 已提交
1
require 'active_record/connection_adapters/abstract_adapter'
2
require 'active_record/connection_adapters/statement_pool'
3

4
require 'active_record/connection_adapters/postgresql/utils'
5
require 'active_record/connection_adapters/postgresql/column'
6
require 'active_record/connection_adapters/postgresql/oid'
7
require 'active_record/connection_adapters/postgresql/quoting'
8
require 'active_record/connection_adapters/postgresql/referential_integrity'
9
require 'active_record/connection_adapters/postgresql/schema_definitions'
10 11
require 'active_record/connection_adapters/postgresql/schema_statements'
require 'active_record/connection_adapters/postgresql/database_statements'
12

13
require 'arel/visitors/bind_visitor'
14 15 16

# Make sure we're using pg high enough for PGResult#values
gem 'pg', '~> 0.11'
17
require 'pg'
D
Initial  
David Heinemeier Hansson 已提交
18

D
Dan McClain 已提交
19 20
require 'ipaddr'

D
Initial  
David Heinemeier Hansson 已提交
21
module ActiveRecord
22
  module ConnectionHandling # :nodoc:
23 24 25
    VALID_CONN_PARAMS = [:host, :hostaddr, :port, :dbname, :user, :password, :connect_timeout,
                         :client_encoding, :options, :application_name, :fallback_application_name,
                         :keepalives, :keepalives_idle, :keepalives_interval, :keepalives_count,
26 27
                         :tty, :sslmode, :requiressl, :sslcompression, :sslcert, :sslkey,
                         :sslrootcert, :sslcrl, :requirepeer, :krbsrvname, :gsslib, :service]
28

D
Initial  
David Heinemeier Hansson 已提交
29
    # Establishes a connection to the database that's used by all Active Record objects
30
    def postgresql_connection(config)
31
      conn_params = config.symbolize_keys
D
Initial  
David Heinemeier Hansson 已提交
32

33
      conn_params.delete_if { |_, v| v.nil? }
34 35 36 37

      # Map ActiveRecords param names to PGs.
      conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
      conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
D
Initial  
David Heinemeier Hansson 已提交
38

39 40 41
      # Forward only valid config params to PGconn.connect.
      conn_params.keep_if { |k, _| VALID_CONN_PARAMS.include?(k) }

42
      # The postgres drivers don't allow the creation of an unconnected PGconn object,
43
      # so just pass a nil connection object for the time being.
44
      ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, conn_params, config)
45 46
    end
  end
47

48
  module ConnectionAdapters
49
    # The PostgreSQL adapter works with the native C (https://bitbucket.org/ged/ruby-pg) driver.
50 51 52
    #
    # Options:
    #
53 54
    # * <tt>:host</tt> - Defaults to a Unix-domain socket in /tmp. On machines without Unix-domain sockets,
    #   the default is to connect to localhost.
P
Pratik Naik 已提交
55
    # * <tt>:port</tt> - Defaults to 5432.
56 57 58
    # * <tt>:username</tt> - Defaults to be the same as the operating system name of the user running the application.
    # * <tt>:password</tt> - Password to be used if the server demands password authentication.
    # * <tt>:database</tt> - Defaults to be the same as the user name.
59
    # * <tt>:schema_search_path</tt> - An optional schema search path for the connection given
60
    #   as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
61
    # * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO
62
    #   <encoding></tt> call on the connection.
63
    # * <tt>:min_messages</tt> - An optional client min messages that is used in a
64
    #   <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
65 66
    # * <tt>:variables</tt> - An optional hash of additional parameters that
    #   will be used in <tt>SET SESSION key = val</tt> calls on the connection.
A
AvnerCohen 已提交
67
    # * <tt>:insert_returning</tt> - An optional boolean to control the use or <tt>RETURNING</tt> for <tt>INSERT</tt> statements
68
    #   defaults to true.
69 70 71 72 73 74 75
    #
    # Any further options are used as connection parameters to libpq. See
    # http://www.postgresql.org/docs/9.1/static/libpq-connect.html for the
    # list of parameters.
    #
    # In addition, default connection parameters of libpq can be set per environment variables.
    # See http://www.postgresql.org/docs/9.1/static/libpq-envars.html .
76
    class PostgreSQLAdapter < AbstractAdapter
77
      ADAPTER_NAME = 'PostgreSQL'
78 79

      NATIVE_DATABASE_TYPES = {
80
        primary_key: "serial primary key",
81
        string:      { name: "character varying" },
82 83 84 85 86 87 88
        text:        { name: "text" },
        integer:     { name: "integer" },
        float:       { name: "float" },
        decimal:     { name: "decimal" },
        datetime:    { name: "timestamp" },
        time:        { name: "time" },
        date:        { name: "date" },
B
bUg 已提交
89 90 91 92 93 94
        daterange:   { name: "daterange" },
        numrange:    { name: "numrange" },
        tsrange:     { name: "tsrange" },
        tstzrange:   { name: "tstzrange" },
        int4range:   { name: "int4range" },
        int8range:   { name: "int8range" },
95 96 97 98 99 100 101 102
        binary:      { name: "bytea" },
        boolean:     { name: "boolean" },
        xml:         { name: "xml" },
        tsvector:    { name: "tsvector" },
        hstore:      { name: "hstore" },
        inet:        { name: "inet" },
        cidr:        { name: "cidr" },
        macaddr:     { name: "macaddr" },
103
        uuid:        { name: "uuid" },
104
        json:        { name: "json" },
105 106
        ltree:       { name: "ltree" },
        citext:      { name: "citext" }
107 108
      }

109 110 111 112
      OID = PostgreSQL::OID #:nodoc:

      include PostgreSQL::Quoting
      include PostgreSQL::ReferentialIntegrity
113
      include PostgreSQL::SchemaStatements
114
      include PostgreSQL::DatabaseStatements
115
      include Savepoints
116

117
      # Returns 'PostgreSQL' as adapter name for identification purposes.
118
      def adapter_name
119
        ADAPTER_NAME
120 121
      end

122 123 124 125
      def schema_creation
        PostgreSQL::SchemaCreation.new self
      end

126 127 128 129 130
      # Adds `:array` option to the default set provided by the
      # AbstractAdapter
      def prepare_column_options(column, types)
        spec = super
        spec[:array] = 'true' if column.respond_to?(:array) && column.array
131
        spec[:default] = "\"#{column.default_function}\"" if column.default_function
132 133 134 135 136 137 138 139
        spec
      end

      # Adds `:array` as a valid migration key
      def migration_keys
        super + [:array]
      end

140 141
      # Returns +true+, since this connection adapter supports prepared statement
      # caching.
142 143 144 145
      def supports_statement_cache?
        true
      end

146 147 148 149
      def supports_index_sort_order?
        true
      end

150 151 152 153
      def supports_partial_index?
        true
      end

154 155 156 157
      def supports_transaction_isolation?
        true
      end

158 159 160 161
      def index_algorithms
        { concurrently: 'CONCURRENTLY' }
      end

162 163 164 165
      class StatementPool < ConnectionAdapters::StatementPool
        def initialize(connection, max)
          super
          @counter = 0
166
          @cache   = Hash.new { |h,pid| h[pid] = {} }
167 168
        end

169 170 171 172
        def each(&block); cache.each(&block); end
        def key?(key);    cache.key?(key); end
        def [](key);      cache[key]; end
        def length;       cache.length; end
173 174 175 176 177 178

        def next_key
          "a#{@counter + 1}"
        end

        def []=(sql, key)
179 180
          while @max <= cache.size
            dealloc(cache.shift.last)
181 182
          end
          @counter += 1
183
          cache[sql] = key
184 185 186
        end

        def clear
187
          cache.each_value do |stmt_key|
188 189
            dealloc stmt_key
          end
190
          cache.clear
191 192
        end

193 194 195 196 197
        def delete(sql_key)
          dealloc cache[sql_key]
          cache.delete sql_key
        end

198
        private
199

200 201 202
          def cache
            @cache[Process.pid]
          end
203

204 205 206 207 208 209 210 211 212
          def dealloc(key)
            @connection.query "DEALLOCATE #{key}" if connection_active?
          end

          def connection_active?
            @connection.status == PGconn::CONNECTION_OK
          rescue PGError
            false
          end
213 214
      end

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

219
        @visitor = Arel::Visitors::PostgreSQL.new self
220
        if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
221
          @prepared_statements = true
222
        else
223
          @prepared_statements = false
224 225
        end

226
        @connection_parameters, @config = connection_parameters, config
227

228 229
        # @local_tz is initialized as nil to avoid warnings when connect tries to use it
        @local_tz = nil
230 231
        @table_alias_length = nil

232
        connect
233
        @statements = StatementPool.new @connection,
234
                                        self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 })
235 236 237 238 239

        if postgresql_version < 80200
          raise "Your version of PostgreSQL (#{postgresql_version}) is too old, please upgrade!"
        end

240
        @type_map = Type::HashLookupTypeMap.new
241
        initialize_type_map(type_map)
242
        @local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
243
        @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
244 245
      end

X
Xavier Noria 已提交
246
      # Clears the prepared statements cache.
247 248 249 250
      def clear_cache!
        @statements.clear
      end

251 252
      # Is this connection alive and ready for queries?
      def active?
253 254
        @connection.query 'SELECT 1'
        true
255
      rescue PGError
256
        false
257 258 259 260
      end

      # Close then reopen the connection.
      def reconnect!
261
        super
262 263
        @connection.reset
        configure_connection
264
      end
265

266 267
      def reset!
        clear_cache!
268 269 270 271 272 273
        reset_transaction
        unless @connection.transaction_status == ::PG::PQTRANS_IDLE
          @connection.query 'ROLLBACK'
        end
        @connection.query 'DISCARD ALL'
        configure_connection
274 275
      end

276 277
      # Disconnects from the database if already connected. Otherwise, this
      # method does nothing.
278
      def disconnect!
279
        super
280 281
        @connection.close rescue nil
      end
282

283
      def native_database_types #:nodoc:
284
        NATIVE_DATABASE_TYPES
285
      end
286

287
      # Returns true, since this connection adapter supports migrations.
288 289
      def supports_migrations?
        true
290 291
      end

292
      # Does PostgreSQL support finding primary key on non-Active Record tables?
293 294 295 296
      def supports_primary_key? #:nodoc:
        true
      end

297 298 299
      # Enable standard-conforming strings if available.
      def set_standard_conforming_strings
        old, self.client_min_messages = client_min_messages, 'panic'
300
        execute('SET standard_conforming_strings = on', 'SCHEMA') rescue nil
301 302
      ensure
        self.client_min_messages = old
303 304
      end

305
      def supports_insert_with_returning?
306
        true
307 308
      end

309 310 311
      def supports_ddl_transactions?
        true
      end
312

313 314 315 316
      def supports_explain?
        true
      end

317
      # Returns true if pg > 9.1
318
      def supports_extensions?
319
        postgresql_version >= 90100
320 321
      end

A
Andrew White 已提交
322 323 324 325 326
      # Range datatypes weren't introduced until PostgreSQL 9.2
      def supports_ranges?
        postgresql_version >= 90200
      end

327 328 329 330
      def supports_materialized_views?
        postgresql_version >= 90300
      end

331
      def enable_extension(name)
332
        exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
333 334
          reload_type_map
        }
335 336 337
      end

      def disable_extension(name)
338
        exec_query("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE").tap {
339 340
          reload_type_map
        }
341 342 343
      end

      def extension_enabled?(name)
R
Rafael Mendonça França 已提交
344
        if supports_extensions?
345
          res = exec_query "SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled",
346
            'SCHEMA'
347
          res.column_types['enabled'].type_cast res.rows.first.first
348
        end
349 350
      end

351 352 353 354 355
      def extensions
        if supports_extensions?
          res = exec_query "SELECT extname from pg_extension", "SCHEMA"
          res.rows.map { |r| res.column_types['extname'].type_cast r.first }
        else
356
          super
357 358 359
        end
      end

360
      # Returns the configured supported identifier length supported by PostgreSQL
361
      def table_alias_length
K
kennyj 已提交
362
        @table_alias_length ||= query('SHOW max_identifier_length', 'SCHEMA')[0][0].to_i
363
      end
364

365 366
      # Set the authorized user for this session
      def session_auth=(user)
367
        clear_cache!
A
Aaron Patterson 已提交
368
        exec_query "SET SESSION AUTHORIZATION #{user}"
369 370
      end

371 372
      def use_insert_returning?
        @use_insert_returning
373 374
      end

375 376 377 378
      def valid_type?(type)
        !native_database_types[type].nil?
      end

379
      def update_table_definition(table_name, base) #:nodoc:
380
        PostgreSQL::Table.new(table_name, base)
381 382
      end

383
      protected
384

385
        # Returns the version of the connected PostgreSQL server.
386
        def postgresql_version
387
          @connection.server_version
388 389
        end

390 391 392 393
        # See http://www.postgresql.org/docs/9.1/static/errcodes-appendix.html
        FOREIGN_KEY_VIOLATION = "23503"
        UNIQUE_VIOLATION      = "23505"

394
        def translate_exception(exception, message)
395 396
          return exception unless exception.respond_to?(:result)

397
          case exception.result.try(:error_field, PGresult::PG_DIAG_SQLSTATE)
398
          when UNIQUE_VIOLATION
399
            RecordNotUnique.new(message, exception)
400
          when FOREIGN_KEY_VIOLATION
401
            InvalidForeignKey.new(message, exception)
402 403 404 405 406
          else
            super
          end
        end

D
Initial  
David Heinemeier Hansson 已提交
407
      private
408

409
        def get_oid_type(oid, fmod, column_name, sql_type = '')
410
          if !type_map.key?(oid)
411
            load_additional_types(type_map, [oid])
412 413
          end

414
          type_map.fetch(oid, fmod, sql_type) {
415
            warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
416 417 418
            Type::Value.new.tap do |cast_type|
              type_map.register_type(oid, cast_type)
            end
419 420 421
          }
        end

422
        def initialize_type_map(m)
S
Sean Griffin 已提交
423
          register_class_with_limit m, 'int2', OID::Integer
424 425 426 427 428 429
          m.alias_type 'int4', 'int2'
          m.alias_type 'int8', 'int2'
          m.alias_type 'oid', 'int2'
          m.register_type 'float4', OID::Float.new
          m.alias_type 'float8', 'float4'
          m.register_type 'text', Type::Text.new
S
Sean Griffin 已提交
430
          register_class_with_limit m, 'varchar', Type::String
431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
          m.alias_type 'char', 'varchar'
          m.alias_type 'name', 'varchar'
          m.alias_type 'bpchar', 'varchar'
          m.register_type 'bool', Type::Boolean.new
          m.register_type 'bit', OID::Bit.new
          m.alias_type 'varbit', 'bit'
          m.alias_type 'timestamptz', 'timestamp'
          m.register_type 'date', OID::Date.new
          m.register_type 'time', OID::Time.new

          m.register_type 'money', OID::Money.new
          m.register_type 'bytea', OID::Bytea.new
          m.register_type 'point', OID::Point.new
          m.register_type 'hstore', OID::Hstore.new
          m.register_type 'json', OID::Json.new
          m.register_type 'cidr', OID::Cidr.new
          m.register_type 'inet', OID::Inet.new
          m.register_type 'uuid', OID::Uuid.new
          m.register_type 'xml', OID::SpecializedString.new(:xml)
          m.register_type 'tsvector', OID::SpecializedString.new(:tsvector)
          m.register_type 'macaddr', OID::SpecializedString.new(:macaddr)
          m.register_type 'citext', OID::SpecializedString.new(:citext)
          m.register_type 'ltree', OID::SpecializedString.new(:ltree)

          # FIXME: why are we keeping these types as strings?
          m.alias_type 'interval', 'varchar'
          m.alias_type 'path', 'varchar'
          m.alias_type 'line', 'varchar'
          m.alias_type 'polygon', 'varchar'
          m.alias_type 'circle', 'varchar'
          m.alias_type 'lseg', 'varchar'
          m.alias_type 'box', 'varchar'

464
          m.register_type 'timestamp' do |_, _, sql_type|
S
Sean Griffin 已提交
465 466 467 468
            precision = extract_precision(sql_type)
            OID::DateTime.new(precision: precision)
          end

469
          m.register_type 'numeric' do |_, fmod, sql_type|
S
Sean Griffin 已提交
470
            precision = extract_precision(sql_type)
S
Sean Griffin 已提交
471
            scale = extract_scale(sql_type)
S
Sean Griffin 已提交
472

473 474 475 476 477 478 479 480 481 482 483 484
            # The type for the numeric depends on the width of the field,
            # so we'll do something special here.
            #
            # When dealing with decimal columns:
            #
            # places after decimal  = fmod - 4 & 0xffff
            # places before decimal = (fmod - 4) >> 16 & 0xffff
            if fmod && (fmod - 4 & 0xffff).zero?
              Type::DecimalWithoutScale.new(precision: precision)
            else
              OID::Decimal.new(precision: precision, scale: scale)
            end
S
Sean Griffin 已提交
485 486
          end

487 488 489
          load_additional_types(m)
        end

S
Sean Griffin 已提交
490 491 492 493 494 495 496 497
        def extract_limit(sql_type) # :nodoc:
          case sql_type
          when /^bigint/i;    8
          when /^smallint/i;  2
          else super
          end
        end

498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568
        # Extracts the value from a PostgreSQL column default definition.
        def extract_value_from_default(default)
          # This is a performance optimization for Ruby 1.9.2 in development.
          # If the value is nil, we return nil straight away without checking
          # the regular expressions. If we check each regular expression,
          # Regexp#=== will call NilClass#to_str, which will trigger
          # method_missing (defined by whiny nil in ActiveSupport) which
          # makes this method very very slow.
          return default unless default

          case default
            when /\A'(.*)'::(num|date|tstz|ts|int4|int8)range\z/m
              $1
            # Numeric types
            when /\A\(?(-?\d+(\.\d*)?\)?(::bigint)?)\z/
              $1
            # Character types
            when /\A\(?'(.*)'::.*\b(?:character varying|bpchar|text)\z/m
              $1.gsub(/''/, "'")
            # Binary data types
            when /\A'(.*)'::bytea\z/m
              $1
            # Date/time types
            when /\A'(.+)'::(?:time(?:stamp)? with(?:out)? time zone|date)\z/
              $1
            when /\A'(.*)'::interval\z/
              $1
            # Boolean type
            when 'true'
              true
            when 'false'
              false
            # Geometric types
            when /\A'(.*)'::(?:point|line|lseg|box|"?path"?|polygon|circle)\z/
              $1
            # Network address types
            when /\A'(.*)'::(?:cidr|inet|macaddr)\z/
              $1
            # Bit string types
            when /\AB'(.*)'::"?bit(?: varying)?"?\z/
              $1
            # XML type
            when /\A'(.*)'::xml\z/m
              $1
            # Arrays
            when /\A'(.*)'::"?\D+"?\[\]\z/
              $1
            # Hstore
            when /\A'(.*)'::hstore\z/
              $1
            # JSON
            when /\A'(.*)'::json\z/
              $1
            # Object identifier types
            when /\A-?\d+\z/
              $1
            else
              # Anything else is blank, some user type, or some function
              # and we can't know the value of that, so return nil.
              nil
          end
        end

        def extract_default_function(default_value, default)
          default if has_default_function?(default_value, default)
        end

        def has_default_function?(default_value, default)
          !default_value && (%r{\w+\(.*\)} === default)
        end

569
        def load_additional_types(type_map, oids = nil)
570
          if supports_ranges?
571
            query = <<-SQL
572
              SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
573 574 575 576
              FROM pg_type as t
              LEFT JOIN pg_range as r ON oid = rngtypid
            SQL
          else
577
            query = <<-SQL
578
              SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype
579 580 581
              FROM pg_type as t
            SQL
          end
582 583 584 585 586

          if oids
            query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
          end

587 588 589
          initializer = OID::TypeMapInitializer.new(type_map)
          records = execute(query, 'SCHEMA')
          initializer.run(records)
590 591
        end

592
        FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
593

594 595 596 597 598 599 600 601
        def execute_and_clear(sql, name, binds)
          result = without_prepared_statement?(binds) ? exec_no_cache(sql, name, binds) :
                                                        exec_cache(sql, name, binds)
          ret = yield result
          result.clear
          ret
        end

602 603
        def exec_no_cache(sql, name, binds)
          log(sql, name, binds) { @connection.async_exec(sql) }
604
        end
605

606
        def exec_cache(sql, name, binds)
607
          stmt_key = prepare_statement(sql)
608 609 610
          type_casted_binds = binds.map { |col, val|
            [col, type_cast(val, col)]
          }
611

612
          log(sql, name, type_casted_binds, stmt_key) do
613
            @connection.send_query_prepared(stmt_key, type_casted_binds.map { |_, val| val })
614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633
            @connection.block
            @connection.get_last_result
          end
        rescue ActiveRecord::StatementInvalid => e
          pgerror = e.original_exception

          # Get the PG code for the failure.  Annoyingly, the code for
          # prepared statements whose return value may have changed is
          # FEATURE_NOT_SUPPORTED.  Check here for more details:
          # http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
          begin
            code = pgerror.result.result_error_field(PGresult::PG_DIAG_SQLSTATE)
          rescue
            raise e
          end
          if FEATURE_NOT_SUPPORTED == code
            @statements.delete sql_key(sql)
            retry
          else
            raise e
634 635 636 637 638 639 640 641 642 643 644 645 646
          end
        end

        # Returns the statement identifier for the client side cache
        # of statements
        def sql_key(sql)
          "#{schema_search_path}-#{sql}"
        end

        # Prepare the statement if it hasn't been prepared, return
        # the statement key.
        def prepare_statement(sql)
          sql_key = sql_key(sql)
647
          unless @statements.key? sql_key
648
            nextkey = @statements.next_key
649 650 651
            begin
              @connection.prepare nextkey, sql
            rescue => e
A
Aaron Patterson 已提交
652
              raise translate_exception_class(e, sql)
653
            end
654 655
            # Clear the queue
            @connection.get_last_result
656
            @statements[sql_key] = nextkey
657
          end
658
          @statements[sql_key]
659
        end
660

661 662 663
        # Connects to a PostgreSQL server and sets up the adapter depending on the
        # connected server's characteristics.
        def connect
664
          @connection = PGconn.connect(@connection_parameters)
665 666 667 668

          # 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.
669
          OID::Money.precision = (postgresql_version >= 80300) ? 19 : 10
670

671
          configure_connection
672 673
        rescue ::PG::Error => error
          if error.message.include?("does not exist")
674
            raise ActiveRecord::NoDatabaseError.new(error.message, error)
675
          else
676
            raise
677
          end
678 679
        end

680
        # Configures the encoding, verbosity, schema search path, and time zone of the connection.
681
        # This is called by #connect and should not be called manually.
682 683
        def configure_connection
          if @config[:encoding]
684
            @connection.set_client_encoding(@config[:encoding])
685
          end
686
          self.client_min_messages = @config[:min_messages] || 'warning'
687
          self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
688 689 690 691

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

692
          # If using Active Record's time zone support configure the connection to return
693
          # TIMESTAMP WITH ZONE types in UTC.
694
          # (SET TIME ZONE does not use an equals sign like other SET variables)
695
          if ActiveRecord::Base.default_timezone == :utc
696
            execute("SET time zone 'UTC'", 'SCHEMA')
697
          elsif @local_tz
698
            execute("SET time zone '#{@local_tz}'", 'SCHEMA')
699
          end
700 701 702 703 704 705 706 707 708 709 710 711

          # SET statements from :variables config hash
          # http://www.postgresql.org/docs/8.3/static/sql-set.html
          variables = @config[:variables] || {}
          variables.map do |k, v|
            if v == ':default' || v == :default
              # Sets the value to the global or compile default
              execute("SET SESSION #{k.to_s} TO DEFAULT", 'SCHEMA')
            elsif !v.nil?
              execute("SET SESSION #{k.to_s} TO #{quote(v)}", 'SCHEMA')
            end
          end
712 713
        end

714
        # Returns the current ID of a table's sequence.
715
        def last_insert_id(sequence_name) #:nodoc:
716 717 718
          Integer(last_insert_id_value(sequence_name))
        end

D
Doug Cole 已提交
719 720 721 722 723
        def last_insert_id_value(sequence_name)
          last_insert_id_result(sequence_name).rows.first.first
        end

        def last_insert_id_result(sequence_name) #:nodoc:
724
          exec_query("SELECT currval('#{sequence_name}')", 'SQL')
D
Initial  
David Heinemeier Hansson 已提交
725 726
        end

727
        # Executes a SELECT query and returns the results, performing any data type
728
        # conversions that are required to be performed here instead of in PostgreSQLColumn.
729
        def select(sql, name = nil, binds = [])
730
          exec_query(sql, name, binds)
731 732
        end

733
        # Returns the list of a table's column names, data types, and default values.
734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750
        #
        # 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
751
        def column_definitions(table_name) # :nodoc:
752
          exec_query(<<-end_sql, 'SCHEMA').rows
753 754
              SELECT a.attname, format_type(a.atttypid, a.atttypmod),
                     pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod
755 756 757 758 759
                FROM pg_attribute a LEFT JOIN pg_attrdef d
                  ON a.attrelid = d.adrelid AND a.attnum = d.adnum
               WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
                 AND a.attnum > 0 AND NOT a.attisdropped
               ORDER BY a.attnum
760
          end_sql
D
Initial  
David Heinemeier Hansson 已提交
761
        end
762

763
        def extract_table_ref_from_insert_sql(sql) # :nodoc:
764
          sql[/into\s+([^\(]*).*values\s*\(/im]
765 766 767
          $1.strip if $1
        end

768
        def create_table_definition(name, temporary, options, as = nil) # :nodoc:
769
          PostgreSQL::TableDefinition.new native_database_types, name, temporary, options, as
770
        end
D
Initial  
David Heinemeier Hansson 已提交
771 772 773
    end
  end
end