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
        NOT_NULL_VIOLATION    = "23502"
412 413
        FOREIGN_KEY_VIOLATION = "23503"
        UNIQUE_VIOLATION      = "23505"
414
        SERIALIZATION_FAILURE = "40001"
415
        DEADLOCK_DETECTED     = "40P01"
416

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

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

D
Initial  
David Heinemeier Hansson 已提交
438
      private
439

440
        def get_oid_type(oid, fmod, column_name, sql_type = "") # :nodoc:
441
          if !type_map.key?(oid)
442
            load_additional_types(type_map, [oid])
443 444
          end

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

453
        def initialize_type_map(m) # :nodoc:
454 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 488 489 490
          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)
491 492

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

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

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

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

518 519 520
          load_additional_types(m)
        end

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

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

559
        def extract_default_function(default_value, default) # :nodoc:
560 561 562
          default if has_default_function?(default_value, default)
        end

563
        def has_default_function?(default_value, default) # :nodoc:
564
          !default_value && (%r{\w+\(.*\)|\(.*\)::\w+} === default)
565 566
        end

567
        def load_additional_types(type_map, oids = nil) # :nodoc:
568 569
          initializer = OID::TypeMapInitializer.new(type_map)

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

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

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

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

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

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

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

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

630 631 632 633 634 635
          # 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
636 637
            @statements.delete sql_key(sql)
            retry
638 639 640
          end
        end

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

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

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

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

709
          # Use standard-conforming strings so we don't have to do the E'...' dance.
710 711
          set_standard_conforming_strings

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

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

734
        # Returns the current ID of a table's sequence.
735
        def last_insert_id_result(sequence_name) # :nodoc:
736
          exec_query("SELECT currval('#{sequence_name}')", "SQL")
D
Initial  
David Heinemeier Hansson 已提交
737 738
        end

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

772
        def extract_table_ref_from_insert_sql(sql) # :nodoc:
773
          sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
774 775 776
          $1.strip if $1
        end

777 778
        def create_table_definition(*args) # :nodoc:
          PostgreSQL::TableDefinition.new(*args)
779
        end
S
Sean Griffin 已提交
780 781 782 783 784 785

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

803 804 805 806 807 808 809 810
        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

811 812
        def add_pg_decoders
          coders_by_name = {
813 814 815 816 817 818 819
            "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,
820
          }
821 822
          known_coder_types = coders_by_name.keys.map { |n| quote(n) }
          query = <<-SQL % known_coder_types.join(", ")
823
            SELECT t.oid, t.typname
824
            FROM pg_type as t
825
            WHERE t.typname IN (%s)
826 827 828
          SQL
          coders = execute_and_clear(query, "SCHEMA", []) do |result|
            result
829
              .map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
830 831 832 833 834 835 836 837 838 839
              .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
840
          coder_class.new(oid: row["oid"].to_i, name: row["typname"])
841
        end
842 843 844 845 846 847 848

        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 已提交
849
        ActiveRecord::Type.register(:datetime, OID::DateTime, adapter: :postgresql)
850 851 852 853
        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)
854
        ActiveRecord::Type.register(:json, OID::Json, adapter: :postgresql)
855 856
        ActiveRecord::Type.register(:jsonb, OID::Jsonb, adapter: :postgresql)
        ActiveRecord::Type.register(:money, OID::Money, adapter: :postgresql)
857 858
        ActiveRecord::Type.register(:point, OID::Point, adapter: :postgresql)
        ActiveRecord::Type.register(:legacy_point, OID::LegacyPoint, adapter: :postgresql)
859 860 861
        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 已提交
862 863 864
    end
  end
end