postgresql_adapter.rb 31.1 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
require "active_record/connection_adapters/postgresql/oid"
require "active_record/connection_adapters/postgresql/quoting"
require "active_record/connection_adapters/postgresql/referential_integrity"
12
require "active_record/connection_adapters/postgresql/schema_creation"
S
Sean Griffin 已提交
13
require "active_record/connection_adapters/postgresql/schema_definitions"
14
require "active_record/connection_adapters/postgresql/schema_dumper"
S
Sean Griffin 已提交
15 16 17 18
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"
19

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

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

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

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

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

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

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

116 117 118 119
      OID = PostgreSQL::OID #:nodoc:

      include PostgreSQL::Quoting
      include PostgreSQL::ReferentialIntegrity
120
      include PostgreSQL::SchemaStatements
121
      include PostgreSQL::DatabaseStatements
122
      include PostgreSQL::ColumnDumper
123

124
      def schema_creation # :nodoc:
125 126 127
        PostgreSQL::SchemaCreation.new self
      end

128 129 130 131
      def arel_visitor # :nodoc:
        Arel::Visitors::PostgreSQL.new(self)
      end

132
      # Returns true, since this connection adapter supports prepared statement
133
      # caching.
134 135 136 137
      def supports_statement_cache?
        true
      end

138 139 140 141
      def supports_index_sort_order?
        true
      end

142 143 144 145
      def supports_partial_index?
        true
      end

146 147 148 149
      def supports_expression_index?
        true
      end

150 151 152 153
      def supports_transaction_isolation?
        true
      end

154 155 156 157
      def supports_foreign_keys?
        true
      end

158 159 160 161
      def supports_views?
        true
      end

162 163 164 165
      def supports_datetime_with_precision?
        true
      end

166 167 168 169
      def supports_json?
        postgresql_version >= 90200
      end

170 171 172 173
      def supports_comments?
        true
      end

174 175 176 177
      def supports_savepoints?
        true
      end

178
      def index_algorithms
179
        { concurrently: "CONCURRENTLY" }
180 181
      end

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

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

        def []=(sql, key)
194
          super.tap { @counter += 1 }
195 196
        end

197
        private
198

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

          def connection_active?
            @connection.status == PGconn::CONNECTION_OK
          rescue PGError
            false
          end
208 209
      end

210 211
      # Initializes and connects a PostgreSQL adapter.
      def initialize(connection, logger, connection_parameters, config)
212
        super(connection, logger, config)
213

214
        @connection_parameters = connection_parameters
215

216 217
        # @local_tz is initialized as nil to avoid warnings when connect tries to use it
        @local_tz = nil
218
        @max_identifier_length = nil
219

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

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

229 230
        add_pg_decoders

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

X
Xavier Noria 已提交
237
      # Clears the prepared statements cache.
238
      def clear_cache!
239 240 241
        @lock.synchronize do
          @statements.clear
        end
242 243
      end

244 245 246 247
      def truncate(table_name, name = nil)
        exec_query "TRUNCATE TABLE #{quote_table_name(table_name)}", name, []
      end

248 249
      # Is this connection alive and ready for queries?
      def active?
250
        @connection.query "SELECT 1"
251
        true
252
      rescue PGError
253
        false
254 255 256 257
      end

      # Close then reopen the connection.
      def reconnect!
258
        super
259 260
        @connection.reset
        configure_connection
261
      end
262

263 264
      def reset!
        clear_cache!
265 266
        reset_transaction
        unless @connection.transaction_status == ::PG::PQTRANS_IDLE
267
          @connection.query "ROLLBACK"
268
        end
269
        @connection.query "DISCARD ALL"
270
        configure_connection
271 272
      end

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

280
      def native_database_types #:nodoc:
281
        NATIVE_DATABASE_TYPES
282
      end
283

284
      def set_standard_conforming_strings
285
        execute("SET standard_conforming_strings = on", "SCHEMA")
286 287
      end

288 289 290
      def supports_ddl_transactions?
        true
      end
291

292 293 294 295
      def supports_advisory_locks?
        true
      end

296 297 298 299
      def supports_explain?
        true
      end

300
      def supports_extensions?
301
        true
302 303
      end

A
Andrew White 已提交
304 305 306 307 308
      # Range datatypes weren't introduced until PostgreSQL 9.2
      def supports_ranges?
        postgresql_version >= 90200
      end

309 310 311 312
      def supports_materialized_views?
        postgresql_version >= 90300
      end

313 314 315 316
      def supports_pgcrypto_uuid?
        postgresql_version >= 90400
      end

317 318 319
      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")
320
        end
321
        select_value("SELECT pg_try_advisory_lock(#{lock_id});")
322 323
      end

324 325 326
      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")
327
        end
328
        select_value("SELECT pg_advisory_unlock(#{lock_id})")
329 330
      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.cast_values.first
348
        end
349 350
      end

351 352
      def extensions
        if supports_extensions?
353
          exec_query("SELECT extname from pg_extension", "SCHEMA").cast_values
354
        else
355
          super
356 357 358
        end
      end

359
      # Returns the configured supported identifier length supported by PostgreSQL
360
      def table_alias_length
361
        @max_identifier_length ||= select_value("SHOW max_identifier_length", "SCHEMA").to_i
362
      end
363
      alias index_name_length table_alias_length
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
      def update_table_definition(table_name, base) #:nodoc:
376
        PostgreSQL::Table.new(table_name, base)
377 378
      end

379
      def lookup_cast_type(sql_type) # :nodoc:
380
        oid = execute("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").first["oid"].to_i
381 382 383
        super(oid)
      end

384 385 386 387 388 389 390 391 392 393
      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 已提交
394 395 396 397
      # Returns the version of the connected PostgreSQL server.
      def postgresql_version
        @connection.server_version
      end
398

399 400 401 402
      def default_index_type?(index) # :nodoc:
        index.using == :btree || super
      end

403
      private
404

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

414
        def translate_exception(exception, message)
415 416
          return exception unless exception.respond_to?(:result)

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

437
        def get_oid_type(oid, fmod, column_name, sql_type = "".freeze)
438
          if !type_map.key?(oid)
439
            load_additional_types(type_map, [oid])
440 441
          end

442 443
          type_map.fetch(oid, fmod, sql_type) {
            warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
444
            Type.default_value.tap do |cast_type|
445 446 447
              type_map.register_type(oid, cast_type)
            end
          }
448 449
        end

A
Akira Matsuda 已提交
450
        def initialize_type_map(m)
451 452 453
          register_class_with_limit m, "int2", Type::Integer
          register_class_with_limit m, "int4", Type::Integer
          register_class_with_limit m, "int8", Type::Integer
454
          m.register_type "oid", OID::Oid.new
455 456 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
          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)
488

489 490 491 492
          m.register_type "interval" do |_, _, sql_type|
            precision = extract_precision(sql_type)
            OID::SpecializedString.new(:interval, precision: precision)
          end
493

494 495
          register_class_with_precision m, "time", Type::Time
          register_class_with_precision m, "timestamp", OID::DateTime
S
Sean Griffin 已提交
496

497
          m.register_type "numeric" do |_, fmod, sql_type|
S
Sean Griffin 已提交
498
            precision = extract_precision(sql_type)
S
Sean Griffin 已提交
499
            scale = extract_scale(sql_type)
S
Sean Griffin 已提交
500

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

517 518 519
          load_additional_types(m)
        end

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

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

A
Akira Matsuda 已提交
558
        def extract_default_function(default_value, default)
559 560 561
          default if has_default_function?(default_value, default)
        end

A
Akira Matsuda 已提交
562
        def has_default_function?(default_value, default)
563
          !default_value && (%r{\w+\(.*\)|\(.*\)::\w+} === default)
564 565
        end

A
Akira Matsuda 已提交
566
        def load_additional_types(type_map, oids = nil)
567 568
          initializer = OID::TypeMapInitializer.new(type_map)

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

          if oids
            query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
584 585
          else
            query += initializer.query_conditions_for_initial_load(type_map)
586 587
          end

588
          execute_and_clear(query, "SCHEMA", []) do |records|
589 590
            initializer.run(records)
          end
591 592
        end

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

595 596 597 598 599 600 601 602
        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
603 604 605 606 607
          ret = yield result
          result.clear
          ret
        end

608
        def exec_no_cache(sql, name, binds)
609
          type_casted_binds = type_casted_binds(binds)
610 611 612 613 614
          log(sql, name, binds, type_casted_binds) do
            ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
              @connection.async_exec(sql, type_casted_binds)
            end
          end
615
        end
616

617
        def exec_cache(sql, name, binds)
618
          stmt_key = prepare_statement(sql)
619
          type_casted_binds = type_casted_binds(binds)
620

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

629 630 631 632 633
          # 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
634 635 636 637
            @lock.synchronize do
              # outside of transactions we can simply flush this query and retry
              @statements.delete sql_key(sql)
            end
638
            retry
639 640 641
          end
        end

642 643 644 645 646 647 648 649 650
        # 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
651
        CACHED_PLAN_HEURISTIC = "cached plan must not change result type".freeze
652 653 654 655 656 657 658 659 660 661 662 663
        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

664 665 666 667 668 669 670 671 672
        # 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)
673 674 675 676 677 678 679 680 681 682 683 684
          @lock.synchronize do
            sql_key = sql_key(sql)
            unless @statements.key? sql_key
              nextkey = @statements.next_key
              begin
                @connection.prepare nextkey, sql
              rescue => e
                raise translate_exception_class(e, sql)
              end
              # Clear the queue
              @connection.get_last_result
              @statements[sql_key] = nextkey
685
            end
686
            @statements[sql_key]
687 688
          end
        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
                     pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
764 765 766 767 768
                     c.collname, col_description(a.attrelid, a.attnum) AS comment
                FROM pg_attribute a
                LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
                LEFT JOIN pg_type t ON a.atttypid = t.oid
                LEFT JOIN pg_collation c ON a.attcollation = c.oid AND a.attcollation <> t.typcollation
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