postgresql_adapter.rb 30.8 KB
Newer Older
1
# Make sure we're using pg high enough for type casts and Ruby 2.2+ compatibility
2 3
gem "pg", "~> 0.18"
require "pg"
4

S
Sean Griffin 已提交
5 6 7
require "active_record/connection_adapters/abstract_adapter"
require "active_record/connection_adapters/postgresql/column"
require "active_record/connection_adapters/postgresql/database_statements"
8
require "active_record/connection_adapters/postgresql/explain_pretty_printer"
S
Sean Griffin 已提交
9 10 11 12
require "active_record/connection_adapters/postgresql/oid"
require "active_record/connection_adapters/postgresql/quoting"
require "active_record/connection_adapters/postgresql/referential_integrity"
require "active_record/connection_adapters/postgresql/schema_definitions"
13
require "active_record/connection_adapters/postgresql/schema_dumper"
S
Sean Griffin 已提交
14 15 16 17
require "active_record/connection_adapters/postgresql/schema_statements"
require "active_record/connection_adapters/postgresql/type_metadata"
require "active_record/connection_adapters/postgresql/utils"
require "active_record/connection_adapters/statement_pool"
18

D
Initial  
David Heinemeier Hansson 已提交
19
module ActiveRecord
20
  module ConnectionHandling # :nodoc:
D
Initial  
David Heinemeier Hansson 已提交
21
    # Establishes a connection to the database that's used by all Active Record objects
22
    def postgresql_connection(config)
23
      conn_params = config.symbolize_keys
D
Initial  
David Heinemeier Hansson 已提交
24

25
      conn_params.delete_if { |_, v| v.nil? }
26 27 28 29

      # 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 已提交
30

31
      # Forward only valid config params to PGconn.connect.
32 33
      valid_conn_param_keys = PGconn.conndefaults_hash.keys + [:requiressl]
      conn_params.slice!(*valid_conn_param_keys)
34

35
      # The postgres drivers don't allow the creation of an unconnected PGconn object,
36
      # so just pass a nil connection object for the time being.
37
      ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, conn_params, config)
38 39
    end
  end
40

41
  module ConnectionAdapters
42
    # The PostgreSQL adapter works with the native C (https://bitbucket.org/ged/ruby-pg) driver.
43 44 45
    #
    # Options:
    #
46 47
    # * <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 已提交
48
    # * <tt>:port</tt> - Defaults to 5432.
49 50 51
    # * <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.
52
    # * <tt>:schema_search_path</tt> - An optional schema search path for the connection given
53
    #   as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
54
    # * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO
55
    #   <encoding></tt> call on the connection.
56
    # * <tt>:min_messages</tt> - An optional client min messages that is used in a
57
    #   <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
58 59
    # * <tt>:variables</tt> - An optional hash of additional parameters that
    #   will be used in <tt>SET SESSION key = val</tt> calls on the connection.
60
    # * <tt>:insert_returning</tt> - An optional boolean to control the use of <tt>RETURNING</tt> for <tt>INSERT</tt> statements
61
    #   defaults to true.
62 63
    #
    # Any further options are used as connection parameters to libpq. See
64
    # http://www.postgresql.org/docs/current/static/libpq-connect.html for the
65 66 67
    # list of parameters.
    #
    # In addition, default connection parameters of libpq can be set per environment variables.
68
    # See http://www.postgresql.org/docs/current/static/libpq-envars.html .
69
    class PostgreSQLAdapter < AbstractAdapter
70
      ADAPTER_NAME = "PostgreSQL".freeze
71 72

      NATIVE_DATABASE_TYPES = {
73
        primary_key: "bigserial primary key",
74
        string:      { name: "character varying" },
75 76 77 78 79 80 81
        text:        { name: "text" },
        integer:     { name: "integer" },
        float:       { name: "float" },
        decimal:     { name: "decimal" },
        datetime:    { name: "timestamp" },
        time:        { name: "time" },
        date:        { name: "date" },
B
bUg 已提交
82 83 84 85 86 87
        daterange:   { name: "daterange" },
        numrange:    { name: "numrange" },
        tsrange:     { name: "tsrange" },
        tstzrange:   { name: "tstzrange" },
        int4range:   { name: "int4range" },
        int8range:   { name: "int8range" },
88 89 90 91 92 93 94 95
        binary:      { name: "bytea" },
        boolean:     { name: "boolean" },
        xml:         { name: "xml" },
        tsvector:    { name: "tsvector" },
        hstore:      { name: "hstore" },
        inet:        { name: "inet" },
        cidr:        { name: "cidr" },
        macaddr:     { name: "macaddr" },
96
        uuid:        { name: "uuid" },
97
        json:        { name: "json" },
98
        jsonb:       { name: "jsonb" },
99
        ltree:       { name: "ltree" },
100
        citext:      { name: "citext" },
S
Sean Griffin 已提交
101
        point:       { name: "point" },
102 103 104 105 106 107
        line:        { name: "line" },
        lseg:        { name: "lseg" },
        box:         { name: "box" },
        path:        { name: "path" },
        polygon:     { name: "polygon" },
        circle:      { name: "circle" },
S
Sean Griffin 已提交
108 109
        bit:         { name: "bit" },
        bit_varying: { name: "bit varying" },
110
        money:       { name: "money" },
111 112
      }

113 114 115 116
      OID = PostgreSQL::OID #:nodoc:

      include PostgreSQL::Quoting
      include PostgreSQL::ReferentialIntegrity
117
      include PostgreSQL::SchemaStatements
118
      include PostgreSQL::DatabaseStatements
119
      include PostgreSQL::ColumnDumper
120

121
      def schema_creation # :nodoc:
122 123 124
        PostgreSQL::SchemaCreation.new self
      end

125 126 127 128
      def arel_visitor # :nodoc:
        Arel::Visitors::PostgreSQL.new(self)
      end

129
      # Returns true, since this connection adapter supports prepared statement
130
      # caching.
131 132 133 134
      def supports_statement_cache?
        true
      end

135 136 137 138
      def supports_index_sort_order?
        true
      end

139 140 141 142
      def supports_partial_index?
        true
      end

143 144 145 146
      def supports_expression_index?
        true
      end

147 148 149 150
      def supports_transaction_isolation?
        true
      end

151 152 153 154
      def supports_foreign_keys?
        true
      end

155 156 157 158
      def supports_views?
        true
      end

159 160 161 162
      def supports_datetime_with_precision?
        true
      end

163 164 165 166
      def supports_json?
        postgresql_version >= 90200
      end

167 168 169 170
      def supports_comments?
        true
      end

171 172 173 174
      def supports_savepoints?
        true
      end

175
      def index_algorithms
176
        { concurrently: "CONCURRENTLY" }
177 178
      end

179 180
      class StatementPool < ConnectionAdapters::StatementPool
        def initialize(connection, max)
181 182
          super(max)
          @connection = connection
183 184 185 186 187 188 189 190
          @counter = 0
        end

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

        def []=(sql, key)
191
          super.tap { @counter += 1 }
192 193
        end

194
        private
195

196 197 198 199 200 201 202 203 204
          def dealloc(key)
            @connection.query "DEALLOCATE #{key}" if connection_active?
          end

          def connection_active?
            @connection.status == PGconn::CONNECTION_OK
          rescue PGError
            false
          end
205 206
      end

207 208
      # Initializes and connects a PostgreSQL adapter.
      def initialize(connection, logger, connection_parameters, config)
209
        super(connection, logger, config)
210

211
        @connection_parameters = connection_parameters
212

213 214
        # @local_tz is initialized as nil to avoid warnings when connect tries to use it
        @local_tz = nil
215 216
        @table_alias_length = nil

217
        connect
218
        add_pg_encoders
219
        @statements = StatementPool.new @connection,
V
Vipul A M 已提交
220
                                        self.class.type_cast_config_to_integer(config[:statement_limit])
221

222
        if postgresql_version < 90100
223
          raise "Your version of PostgreSQL (#{postgresql_version}) is too old. Active Record supports PostgreSQL >= 9.1."
224 225
        end

226 227
        add_pg_decoders

228
        @type_map = Type::HashLookupTypeMap.new
229
        initialize_type_map(type_map)
230
        @local_tz = execute("SHOW TIME ZONE", "SCHEMA").first["TimeZone"]
231
        @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
232 233
      end

X
Xavier Noria 已提交
234
      # Clears the prepared statements cache.
235 236 237 238
      def clear_cache!
        @statements.clear
      end

239 240 241 242
      def truncate(table_name, name = nil)
        exec_query "TRUNCATE TABLE #{quote_table_name(table_name)}", name, []
      end

243 244
      # Is this connection alive and ready for queries?
      def active?
245
        @connection.query "SELECT 1"
246
        true
247
      rescue PGError
248
        false
249 250 251 252
      end

      # Close then reopen the connection.
      def reconnect!
253
        super
254 255
        @connection.reset
        configure_connection
256
      end
257

258 259
      def reset!
        clear_cache!
260 261
        reset_transaction
        unless @connection.transaction_status == ::PG::PQTRANS_IDLE
262
          @connection.query "ROLLBACK"
263
        end
264
        @connection.query "DISCARD ALL"
265
        configure_connection
266 267
      end

268 269
      # Disconnects from the database if already connected. Otherwise, this
      # method does nothing.
270
      def disconnect!
271
        super
272 273
        @connection.close rescue nil
      end
274

275
      def native_database_types #:nodoc:
276
        NATIVE_DATABASE_TYPES
277
      end
278

279
      # Returns true, since this connection adapter supports migrations.
280 281
      def supports_migrations?
        true
282 283
      end

284
      # Does PostgreSQL support finding primary key on non-Active Record tables?
285 286 287 288
      def supports_primary_key? #:nodoc:
        true
      end

289
      def set_standard_conforming_strings
290
        execute("SET standard_conforming_strings = on", "SCHEMA")
291 292
      end

293 294 295
      def supports_ddl_transactions?
        true
      end
296

297 298 299 300
      def supports_advisory_locks?
        true
      end

301 302 303 304
      def supports_explain?
        true
      end

305
      def supports_extensions?
306
        true
307 308
      end

A
Andrew White 已提交
309 310 311 312 313
      # Range datatypes weren't introduced until PostgreSQL 9.2
      def supports_ranges?
        postgresql_version >= 90200
      end

314 315 316 317
      def supports_materialized_views?
        postgresql_version >= 90300
      end

318 319 320 321
      def supports_pgcrypto_uuid?
        postgresql_version >= 90400
      end

322 323 324
      def get_advisory_lock(lock_id) # :nodoc:
        unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
          raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer")
325
        end
326
        select_value("SELECT pg_try_advisory_lock(#{lock_id});")
327 328
      end

329 330 331
      def release_advisory_lock(lock_id) # :nodoc:
        unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
          raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer")
332
        end
333
        select_value("SELECT pg_advisory_unlock(#{lock_id})")
334 335
      end

336
      def enable_extension(name)
337
        exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
338 339
          reload_type_map
        }
340 341 342
      end

      def disable_extension(name)
343
        exec_query("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE").tap {
344 345
          reload_type_map
        }
346 347 348
      end

      def extension_enabled?(name)
R
Rafael Mendonça França 已提交
349
        if supports_extensions?
350
          res = exec_query "SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled",
351
            "SCHEMA"
352
          res.cast_values.first
353
        end
354 355
      end

356 357
      def extensions
        if supports_extensions?
358
          exec_query("SELECT extname from pg_extension", "SCHEMA").cast_values
359
        else
360
          super
361 362 363
        end
      end

364
      # Returns the configured supported identifier length supported by PostgreSQL
365
      def table_alias_length
366
        @table_alias_length ||= query("SHOW max_identifier_length", "SCHEMA")[0][0].to_i
367
      end
368

369 370
      # Set the authorized user for this session
      def session_auth=(user)
371
        clear_cache!
A
Aaron Patterson 已提交
372
        exec_query "SET SESSION AUTHORIZATION #{user}"
373 374
      end

375 376
      def use_insert_returning?
        @use_insert_returning
377 378
      end

379 380 381 382
      def valid_type?(type)
        !native_database_types[type].nil?
      end

383
      def update_table_definition(table_name, base) #:nodoc:
384
        PostgreSQL::Table.new(table_name, base)
385 386
      end

387
      def lookup_cast_type(sql_type) # :nodoc:
388
        oid = execute("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").first["oid"].to_i
389 390 391
        super(oid)
      end

392 393 394 395 396 397 398 399 400 401
      def column_name_for_operation(operation, node) # :nodoc:
        OPERATION_ALIASES.fetch(operation) { operation.downcase }
      end

      OPERATION_ALIASES = { # :nodoc:
        "maximum" => "max",
        "minimum" => "min",
        "average" => "avg",
      }

D
Derek Prior 已提交
402 403 404 405
      # Returns the version of the connected PostgreSQL server.
      def postgresql_version
        @connection.server_version
      end
406

D
Derek Prior 已提交
407
      protected
408

409
        # See http://www.postgresql.org/docs/current/static/errcodes-appendix.html
410
        VALUE_LIMIT_VIOLATION = "22001"
411
        NUMERIC_VALUE_OUT_OF_RANGE = "22003"
412
        NOT_NULL_VIOLATION    = "23502"
413 414
        FOREIGN_KEY_VIOLATION = "23503"
        UNIQUE_VIOLATION      = "23505"
415
        SERIALIZATION_FAILURE = "40001"
416
        DEADLOCK_DETECTED     = "40P01"
417

418
        def translate_exception(exception, message)
419 420
          return exception unless exception.respond_to?(:result)

421
          case exception.result.try(:error_field, PGresult::PG_DIAG_SQLSTATE)
422
          when UNIQUE_VIOLATION
423
            RecordNotUnique.new(message)
424
          when FOREIGN_KEY_VIOLATION
425
            InvalidForeignKey.new(message)
426 427
          when VALUE_LIMIT_VIOLATION
            ValueTooLong.new(message)
428 429
          when NUMERIC_VALUE_OUT_OF_RANGE
            RangeError.new(message)
430 431
          when NOT_NULL_VIOLATION
            NotNullViolation.new(message)
432
          when SERIALIZATION_FAILURE
433
            SerializationFailure.new(message)
434
          when DEADLOCK_DETECTED
435
            Deadlocked.new(message)
436 437 438 439 440
          else
            super
          end
        end

D
Initial  
David Heinemeier Hansson 已提交
441
      private
442

A
Akira Matsuda 已提交
443
        def get_oid_type(oid, fmod, column_name, sql_type = "")
444
          if !type_map.key?(oid)
445
            load_additional_types(type_map, [oid])
446 447
          end

448 449
          type_map.fetch(oid, fmod, sql_type) {
            warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
450
            Type.default_value.tap do |cast_type|
451 452 453
              type_map.register_type(oid, cast_type)
            end
          }
454 455
        end

A
Akira Matsuda 已提交
456
        def initialize_type_map(m)
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
          register_class_with_limit m, "int2", Type::Integer
          register_class_with_limit m, "int4", Type::Integer
          register_class_with_limit m, "int8", Type::Integer
          m.alias_type "oid", "int2"
          m.register_type "float4", Type::Float.new
          m.alias_type "float8", "float4"
          m.register_type "text", Type::Text.new
          register_class_with_limit m, "varchar", Type::String
          m.alias_type "char", "varchar"
          m.alias_type "name", "varchar"
          m.alias_type "bpchar", "varchar"
          m.register_type "bool", Type::Boolean.new
          register_class_with_limit m, "bit", OID::Bit
          register_class_with_limit m, "varbit", OID::BitVarying
          m.alias_type "timestamptz", "timestamp"
          m.register_type "date", Type::Date.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 "jsonb", OID::Jsonb.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::Xml.new
          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)
          m.register_type "line", OID::SpecializedString.new(:line)
          m.register_type "lseg", OID::SpecializedString.new(:lseg)
          m.register_type "box", OID::SpecializedString.new(:box)
          m.register_type "path", OID::SpecializedString.new(:path)
          m.register_type "polygon", OID::SpecializedString.new(:polygon)
          m.register_type "circle", OID::SpecializedString.new(:circle)
494 495

          # FIXME: why are we keeping these types as strings?
496
          m.alias_type "interval", "varchar"
497

498 499
          register_class_with_precision m, "time", Type::Time
          register_class_with_precision m, "timestamp", OID::DateTime
S
Sean Griffin 已提交
500

501
          m.register_type "numeric" do |_, fmod, sql_type|
S
Sean Griffin 已提交
502
            precision = extract_precision(sql_type)
S
Sean Griffin 已提交
503
            scale = extract_scale(sql_type)
S
Sean Griffin 已提交
504

505 506 507 508 509 510 511 512
            # 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?
513 514
              # FIXME: Remove this class, and the second argument to
              # lookups on PG
515 516 517 518
              Type::DecimalWithoutScale.new(precision: precision)
            else
              OID::Decimal.new(precision: precision, scale: scale)
            end
S
Sean Griffin 已提交
519 520
          end

521 522 523
          load_additional_types(m)
        end

A
Akira Matsuda 已提交
524
        def extract_limit(sql_type)
S
Sean Griffin 已提交
525
          case sql_type
526 527 528 529 530 531
          when /^bigint/i, /^int8/i
            8
          when /^smallint/i
            2
          else
            super
S
Sean Griffin 已提交
532 533 534
          end
        end

535
        # Extracts the value from a PostgreSQL column default definition.
A
Akira Matsuda 已提交
536
        def extract_value_from_default(default)
537
          case default
538
            # Quoted types
539
          when /\A[\(B]?'(.*)'.*::"?([\w. ]+)"?(?:\[\])?\z/m
540
            # The default 'now'::date is CURRENT_DATE
541 542 543 544 545
            if $1 == "now".freeze && $2 == "date".freeze
              nil
            else
              $1.gsub("''".freeze, "'".freeze)
            end
546
            # Boolean types
547 548
          when "true".freeze, "false".freeze
            default
549
            # Numeric types
550 551
          when /\A\(?(-?\d+(\.\d*)?)\)?(::bigint)?\z/
            $1
552
            # Object identifier types
553 554
          when /\A-?\d+\z/
            $1
555 556 557
          else
            # Anything else is blank, some user type, or some function
            # and we can't know the value of that, so return nil.
558
            nil
559 560 561
          end
        end

A
Akira Matsuda 已提交
562
        def extract_default_function(default_value, default)
563 564 565
          default if has_default_function?(default_value, default)
        end

A
Akira Matsuda 已提交
566
        def has_default_function?(default_value, default)
567
          !default_value && (%r{\w+\(.*\)|\(.*\)::\w+} === default)
568 569
        end

A
Akira Matsuda 已提交
570
        def load_additional_types(type_map, oids = nil)
571 572
          initializer = OID::TypeMapInitializer.new(type_map)

573
          if supports_ranges?
574
            query = <<-SQL
575
              SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
576 577 578 579
              FROM pg_type as t
              LEFT JOIN pg_range as r ON oid = rngtypid
            SQL
          else
580
            query = <<-SQL
581
              SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype
582 583 584
              FROM pg_type as t
            SQL
          end
585 586 587

          if oids
            query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
588 589
          else
            query += initializer.query_conditions_for_initial_load(type_map)
590 591
          end

592
          execute_and_clear(query, "SCHEMA", []) do |records|
593 594
            initializer.run(records)
          end
595 596
        end

597
        FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
598

599 600 601 602 603 604 605 606
        def execute_and_clear(sql, name, binds, prepare: false)
          if without_prepared_statement?(binds)
            result = exec_no_cache(sql, name, [])
          elsif !prepare
            result = exec_no_cache(sql, name, binds)
          else
            result = exec_cache(sql, name, binds)
          end
607 608 609 610 611
          ret = yield result
          result.clear
          ret
        end

612
        def exec_no_cache(sql, name, binds)
613
          type_casted_binds = type_casted_binds(binds)
614 615 616 617 618
          log(sql, name, binds, type_casted_binds) do
            ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
              @connection.async_exec(sql, type_casted_binds)
            end
          end
619
        end
620

621
        def exec_cache(sql, name, binds)
622
          stmt_key = prepare_statement(sql)
623
          type_casted_binds = type_casted_binds(binds)
624

625
          log(sql, name, binds, type_casted_binds, stmt_key) do
626 627 628
            ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
              @connection.exec_prepared(stmt_key, type_casted_binds)
            end
629 630
          end
        rescue ActiveRecord::StatementInvalid => e
631
          raise unless is_cached_plan_failure?(e)
632

633 634 635 636 637 638
          # Nothing we can do if we are in a transaction because all commands
          # will raise InFailedSQLTransaction
          if in_transaction?
            raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message)
          else
            # outside of transactions we can simply flush this query and retry
639 640
            @statements.delete sql_key(sql)
            retry
641 642 643
          end
        end

644 645 646 647 648 649 650 651 652
        # Annoyingly, the code for prepared statements whose return value may
        # have changed is FEATURE_NOT_SUPPORTED.
        #
        # This covers various different error types so we need to do additional
        # work to classify the exception definitively as a
        # ActiveRecord::PreparedStatementCacheExpired
        #
        # Check here for more details:
        # http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
653
        CACHED_PLAN_HEURISTIC = "cached plan must not change result type".freeze
654 655 656 657 658 659 660 661 662 663 664 665
        def is_cached_plan_failure?(e)
          pgerror = e.cause
          code = pgerror.result.result_error_field(PGresult::PG_DIAG_SQLSTATE)
          code == FEATURE_NOT_SUPPORTED && pgerror.message.include?(CACHED_PLAN_HEURISTIC)
        rescue
          false
        end

        def in_transaction?
          open_transactions > 0
        end

666 667 668 669 670 671 672 673 674 675
        # 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)
676
          unless @statements.key? sql_key
677
            nextkey = @statements.next_key
678 679 680
            begin
              @connection.prepare nextkey, sql
            rescue => e
A
Aaron Patterson 已提交
681
              raise translate_exception_class(e, sql)
682
            end
683 684
            # Clear the queue
            @connection.get_last_result
685
            @statements[sql_key] = nextkey
686
          end
687
          @statements[sql_key]
688
        end
689

690 691 692
        # Connects to a PostgreSQL server and sets up the adapter depending on the
        # connected server's characteristics.
        def connect
693
          @connection = PGconn.connect(@connection_parameters)
694
          configure_connection
695 696
        rescue ::PG::Error => error
          if error.message.include?("does not exist")
697
            raise ActiveRecord::NoDatabaseError
698
          else
699
            raise
700
          end
701 702
        end

703
        # Configures the encoding, verbosity, schema search path, and time zone of the connection.
704
        # This is called by #connect and should not be called manually.
705 706
        def configure_connection
          if @config[:encoding]
707
            @connection.set_client_encoding(@config[:encoding])
708
          end
709
          self.client_min_messages = @config[:min_messages] || "warning"
710
          self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
711

712
          # Use standard-conforming strings so we don't have to do the E'...' dance.
713 714
          set_standard_conforming_strings

715
          # If using Active Record's time zone support configure the connection to return
716
          # TIMESTAMP WITH ZONE types in UTC.
717
          # (SET TIME ZONE does not use an equals sign like other SET variables)
718
          if ActiveRecord::Base.default_timezone == :utc
719
            execute("SET time zone 'UTC'", "SCHEMA")
720
          elsif @local_tz
721
            execute("SET time zone '#{@local_tz}'", "SCHEMA")
722
          end
723 724

          # SET statements from :variables config hash
725
          # http://www.postgresql.org/docs/current/static/sql-set.html
726 727
          variables = @config[:variables] || {}
          variables.map do |k, v|
728
            if v == ":default" || v == :default
729
              # Sets the value to the global or compile default
730
              execute("SET SESSION #{k} TO DEFAULT", "SCHEMA")
731
            elsif !v.nil?
732
              execute("SET SESSION #{k} TO #{quote(v)}", "SCHEMA")
733 734
            end
          end
735 736
        end

737
        # Returns the current ID of a table's sequence.
A
Akira Matsuda 已提交
738
        def last_insert_id_result(sequence_name)
739
          exec_query("SELECT currval('#{sequence_name}')", "SQL")
D
Initial  
David Heinemeier Hansson 已提交
740 741
        end

742
        # Returns the list of a table's column names, data types, and default values.
743 744
        #
        # The underlying query is roughly:
745
        #  SELECT column.name, column.type, default.value, column.comment
746 747 748 749 750 751 752 753 754 755 756 757 758 759
        #    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
A
Akira Matsuda 已提交
760
        def column_definitions(table_name)
761
          query(<<-end_sql, "SCHEMA")
762
              SELECT a.attname, format_type(a.atttypid, a.atttypmod),
763 764
                     pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
             (SELECT c.collname FROM pg_collation c, pg_type t
765 766
               WHERE c.oid = a.attcollation AND t.oid = a.atttypid AND a.attcollation <> t.typcollation),
                     col_description(a.attrelid, a.attnum) AS comment
767 768
                FROM pg_attribute a LEFT JOIN pg_attrdef d
                  ON a.attrelid = d.adrelid AND a.attnum = d.adnum
R
Ryuta Kamizono 已提交
769
               WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass
770 771
                 AND a.attnum > 0 AND NOT a.attisdropped
               ORDER BY a.attnum
772
          end_sql
D
Initial  
David Heinemeier Hansson 已提交
773
        end
774

A
Akira Matsuda 已提交
775
        def extract_table_ref_from_insert_sql(sql)
776
          sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
777 778 779
          $1.strip if $1
        end

A
Akira Matsuda 已提交
780
        def create_table_definition(*args)
781
          PostgreSQL::TableDefinition.new(*args)
782
        end
S
Sean Griffin 已提交
783 784 785 786 787 788

        def can_perform_case_insensitive_comparison_for?(column)
          @case_insensitive_cache ||= {}
          @case_insensitive_cache[column.sql_type] ||= begin
            sql = <<-end_sql
              SELECT exists(
789 790 791 792
                SELECT * FROM pg_proc
                WHERE proname = 'lower'
                  AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector
              ) OR exists(
S
Sean Griffin 已提交
793 794
                SELECT * FROM pg_proc
                INNER JOIN pg_cast
N
nanaya 已提交
795
                  ON ARRAY[casttarget]::oidvector = proargtypes
S
Sean Griffin 已提交
796
                WHERE proname = 'lower'
N
nanaya 已提交
797
                  AND castsource = #{quote column.sql_type}::regtype
S
Sean Griffin 已提交
798 799 800
              )
            end_sql
            execute_and_clear(sql, "SCHEMA", []) do |result|
801
              result.getvalue(0, 0)
S
Sean Griffin 已提交
802 803 804
            end
          end
        end
805

806 807 808 809 810 811 812 813
        def add_pg_encoders
          map = PG::TypeMapByClass.new
          map[Integer] = PG::TextEncoder::Integer.new
          map[TrueClass] = PG::TextEncoder::Boolean.new
          map[FalseClass] = PG::TextEncoder::Boolean.new
          @connection.type_map_for_queries = map
        end

814 815
        def add_pg_decoders
          coders_by_name = {
816 817 818 819 820 821 822
            "int2" => PG::TextDecoder::Integer,
            "int4" => PG::TextDecoder::Integer,
            "int8" => PG::TextDecoder::Integer,
            "oid" => PG::TextDecoder::Integer,
            "float4" => PG::TextDecoder::Float,
            "float8" => PG::TextDecoder::Float,
            "bool" => PG::TextDecoder::Boolean,
823
          }
824 825
          known_coder_types = coders_by_name.keys.map { |n| quote(n) }
          query = <<-SQL % known_coder_types.join(", ")
826
            SELECT t.oid, t.typname
827
            FROM pg_type as t
828
            WHERE t.typname IN (%s)
829 830 831
          SQL
          coders = execute_and_clear(query, "SCHEMA", []) do |result|
            result
832
              .map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
833 834 835 836 837 838 839 840 841 842
              .compact
          end

          map = PG::TypeMapByOid.new
          coders.each { |coder| map.add_coder(coder) }
          @connection.type_map_for_results = map
        end

        def construct_coder(row, coder_class)
          return unless coder_class
843
          coder_class.new(oid: row["oid"].to_i, name: row["typname"])
844
        end
845 846 847 848 849 850 851

        ActiveRecord::Type.add_modifier({ array: true }, OID::Array, adapter: :postgresql)
        ActiveRecord::Type.add_modifier({ range: true }, OID::Range, adapter: :postgresql)
        ActiveRecord::Type.register(:bit, OID::Bit, adapter: :postgresql)
        ActiveRecord::Type.register(:bit_varying, OID::BitVarying, adapter: :postgresql)
        ActiveRecord::Type.register(:binary, OID::Bytea, adapter: :postgresql)
        ActiveRecord::Type.register(:cidr, OID::Cidr, adapter: :postgresql)
Y
yuuji.yaginuma 已提交
852
        ActiveRecord::Type.register(:datetime, OID::DateTime, adapter: :postgresql)
853 854 855 856
        ActiveRecord::Type.register(:decimal, OID::Decimal, adapter: :postgresql)
        ActiveRecord::Type.register(:enum, OID::Enum, adapter: :postgresql)
        ActiveRecord::Type.register(:hstore, OID::Hstore, adapter: :postgresql)
        ActiveRecord::Type.register(:inet, OID::Inet, adapter: :postgresql)
857
        ActiveRecord::Type.register(:json, OID::Json, adapter: :postgresql)
858 859
        ActiveRecord::Type.register(:jsonb, OID::Jsonb, adapter: :postgresql)
        ActiveRecord::Type.register(:money, OID::Money, adapter: :postgresql)
860 861
        ActiveRecord::Type.register(:point, OID::Point, adapter: :postgresql)
        ActiveRecord::Type.register(:legacy_point, OID::LegacyPoint, adapter: :postgresql)
862 863 864
        ActiveRecord::Type.register(:uuid, OID::Uuid, adapter: :postgresql)
        ActiveRecord::Type.register(:vector, OID::Vector, adapter: :postgresql)
        ActiveRecord::Type.register(:xml, OID::Xml, adapter: :postgresql)
D
Initial  
David Heinemeier Hansson 已提交
865 866 867
    end
  end
end