postgresql_adapter.rb 30.3 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: "serial 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
      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")
321
        end
322
        select_value("SELECT pg_try_advisory_lock(#{lock_id});")
323 324
      end

325 326 327
      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")
328
        end
329
        select_value("SELECT pg_advisory_unlock(#{lock_id})")
330 331
      end

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

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

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

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

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

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

371 372
      def use_insert_returning?
        @use_insert_returning
373 374
      end

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

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

383
      def lookup_cast_type(sql_type) # :nodoc:
384
        oid = execute("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").first["oid"].to_i
385 386 387
        super(oid)
      end

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

D
Derek Prior 已提交
403
      protected
404

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

412
        def translate_exception(exception, message)
413 414
          return exception unless exception.respond_to?(:result)

415
          case exception.result.try(:error_field, PGresult::PG_DIAG_SQLSTATE)
416
          when UNIQUE_VIOLATION
417
            RecordNotUnique.new(message)
418
          when FOREIGN_KEY_VIOLATION
419
            InvalidForeignKey.new(message)
420 421
          when VALUE_LIMIT_VIOLATION
            ValueTooLong.new(message)
422
          when SERIALIZATION_FAILURE
423
            SerializationFailure.new(message)
424
          when DEADLOCK_DETECTED
425
            Deadlocked.new(message)
426 427 428 429 430
          else
            super
          end
        end

D
Initial  
David Heinemeier Hansson 已提交
431
      private
432

433
        def get_oid_type(oid, fmod, column_name, sql_type = "") # :nodoc:
434
          if !type_map.key?(oid)
435
            load_additional_types(type_map, [oid])
436 437
          end

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

446
        def initialize_type_map(m) # :nodoc:
447 448 449 450 451 452 453 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
          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)
484 485

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

488 489
          register_class_with_precision m, "time", Type::Time
          register_class_with_precision m, "timestamp", OID::DateTime
S
Sean Griffin 已提交
490

491
          m.register_type "numeric" do |_, fmod, sql_type|
S
Sean Griffin 已提交
492
            precision = extract_precision(sql_type)
S
Sean Griffin 已提交
493
            scale = extract_scale(sql_type)
S
Sean Griffin 已提交
494

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

511 512 513
          load_additional_types(m)
        end

S
Sean Griffin 已提交
514 515
        def extract_limit(sql_type) # :nodoc:
          case sql_type
516 517 518 519 520 521
          when /^bigint/i, /^int8/i
            8
          when /^smallint/i
            2
          else
            super
S
Sean Griffin 已提交
522 523 524
          end
        end

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

552
        def extract_default_function(default_value, default) # :nodoc:
553 554 555
          default if has_default_function?(default_value, default)
        end

556
        def has_default_function?(default_value, default) # :nodoc:
557
          !default_value && (%r{\w+\(.*\)|\(.*\)::\w+} === default)
558 559
        end

560
        def load_additional_types(type_map, oids = nil) # :nodoc:
561 562
          initializer = OID::TypeMapInitializer.new(type_map)

563
          if supports_ranges?
564
            query = <<-SQL
565
              SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
566 567 568 569
              FROM pg_type as t
              LEFT JOIN pg_range as r ON oid = rngtypid
            SQL
          else
570
            query = <<-SQL
571
              SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, t.typtype, t.typbasetype
572 573 574
              FROM pg_type as t
            SQL
          end
575 576 577

          if oids
            query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
578 579
          else
            query += initializer.query_conditions_for_initial_load(type_map)
580 581
          end

582
          execute_and_clear(query, "SCHEMA", []) do |records|
583 584
            initializer.run(records)
          end
585 586
        end

587
        FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
588

589 590 591 592 593 594 595 596
        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
597 598 599 600 601
          ret = yield result
          result.clear
          ret
        end

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

607
        def exec_cache(sql, name, binds)
608
          stmt_key = prepare_statement(sql)
609
          type_casted_binds = type_casted_binds(binds)
610

611
          log(sql, name, binds, type_casted_binds, stmt_key) do
S
Sean Griffin 已提交
612
            @connection.exec_prepared(stmt_key, type_casted_binds)
613 614
          end
        rescue ActiveRecord::StatementInvalid => e
615
          raise unless is_cached_plan_failure?(e)
616

617 618 619 620 621 622
          # 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
623 624
            @statements.delete sql_key(sql)
            retry
625 626 627
          end
        end

628 629 630 631 632 633 634 635 636
        # 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
637
        CACHED_PLAN_HEURISTIC = "cached plan must not change result type".freeze
638 639 640 641 642 643 644 645 646 647 648 649
        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

650 651 652 653 654 655 656 657 658 659
        # 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)
660
          unless @statements.key? sql_key
661
            nextkey = @statements.next_key
662 663 664
            begin
              @connection.prepare nextkey, sql
            rescue => e
A
Aaron Patterson 已提交
665
              raise translate_exception_class(e, sql)
666
            end
667 668
            # Clear the queue
            @connection.get_last_result
669
            @statements[sql_key] = nextkey
670
          end
671
          @statements[sql_key]
672
        end
673

674 675 676
        # Connects to a PostgreSQL server and sets up the adapter depending on the
        # connected server's characteristics.
        def connect
677
          @connection = PGconn.connect(@connection_parameters)
678
          configure_connection
679 680
        rescue ::PG::Error => error
          if error.message.include?("does not exist")
681
            raise ActiveRecord::NoDatabaseError
682
          else
683
            raise
684
          end
685 686
        end

687
        # Configures the encoding, verbosity, schema search path, and time zone of the connection.
688
        # This is called by #connect and should not be called manually.
689 690
        def configure_connection
          if @config[:encoding]
691
            @connection.set_client_encoding(@config[:encoding])
692
          end
693
          self.client_min_messages = @config[:min_messages] || "warning"
694
          self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
695

696
          # Use standard-conforming strings so we don't have to do the E'...' dance.
697 698
          set_standard_conforming_strings

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

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

721
        # Returns the current ID of a table's sequence.
722
        def last_insert_id_result(sequence_name) # :nodoc:
723
          exec_query("SELECT currval('#{sequence_name}')", "SQL")
D
Initial  
David Heinemeier Hansson 已提交
724 725
        end

726
        # Returns the list of a table's column names, data types, and default values.
727 728
        #
        # The underlying query is roughly:
729
        #  SELECT column.name, column.type, default.value, column.comment
730 731 732 733 734 735 736 737 738 739 740 741 742 743
        #    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
744
        def column_definitions(table_name) # :nodoc:
745
          query(<<-end_sql, "SCHEMA")
746
              SELECT a.attname, format_type(a.atttypid, a.atttypmod),
747 748
                     pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
             (SELECT c.collname FROM pg_collation c, pg_type t
749 750
               WHERE c.oid = a.attcollation AND t.oid = a.atttypid AND a.attcollation <> t.typcollation),
                     col_description(a.attrelid, a.attnum) AS comment
751 752 753 754 755
                FROM pg_attribute a LEFT JOIN pg_attrdef d
                  ON a.attrelid = d.adrelid AND a.attnum = d.adnum
               WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
                 AND a.attnum > 0 AND NOT a.attisdropped
               ORDER BY a.attnum
756
          end_sql
D
Initial  
David Heinemeier Hansson 已提交
757
        end
758

759
        def extract_table_ref_from_insert_sql(sql) # :nodoc:
760
          sql[/into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im]
761 762 763
          $1.strip if $1
        end

764 765
        def create_table_definition(*args) # :nodoc:
          PostgreSQL::TableDefinition.new(*args)
766
        end
S
Sean Griffin 已提交
767 768 769 770 771 772 773 774

        def can_perform_case_insensitive_comparison_for?(column)
          @case_insensitive_cache ||= {}
          @case_insensitive_cache[column.sql_type] ||= begin
            sql = <<-end_sql
              SELECT exists(
                SELECT * FROM pg_proc
                INNER JOIN pg_cast
N
nanaya 已提交
775
                  ON ARRAY[casttarget]::oidvector = proargtypes
S
Sean Griffin 已提交
776
                WHERE proname = 'lower'
N
nanaya 已提交
777
                  AND castsource = #{quote column.sql_type}::regtype
S
Sean Griffin 已提交
778 779 780
              )
            end_sql
            execute_and_clear(sql, "SCHEMA", []) do |result|
781
              result.getvalue(0, 0)
S
Sean Griffin 已提交
782 783 784
            end
          end
        end
785

786 787 788 789 790 791 792 793 794
        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
          map[Float] = PG::TextEncoder::Float.new
          @connection.type_map_for_queries = map
        end

795 796
        def add_pg_decoders
          coders_by_name = {
797 798 799 800 801 802 803
            "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,
804
          }
805 806
          known_coder_types = coders_by_name.keys.map { |n| quote(n) }
          query = <<-SQL % known_coder_types.join(", ")
807
            SELECT t.oid, t.typname
808
            FROM pg_type as t
809
            WHERE t.typname IN (%s)
810 811 812
          SQL
          coders = execute_and_clear(query, "SCHEMA", []) do |result|
            result
813
              .map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
814 815 816 817 818 819 820 821 822 823
              .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
824
          coder_class.new(oid: row["oid"].to_i, name: row["typname"])
825
        end
826 827 828 829 830 831 832

        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 已提交
833
        ActiveRecord::Type.register(:datetime, OID::DateTime, adapter: :postgresql)
834 835 836 837
        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)
838
        ActiveRecord::Type.register(:json, OID::Json, adapter: :postgresql)
839 840
        ActiveRecord::Type.register(:jsonb, OID::Jsonb, adapter: :postgresql)
        ActiveRecord::Type.register(:money, OID::Money, adapter: :postgresql)
841 842
        ActiveRecord::Type.register(:point, OID::Point, adapter: :postgresql)
        ActiveRecord::Type.register(:legacy_point, OID::LegacyPoint, adapter: :postgresql)
843 844 845
        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 已提交
846 847 848
    end
  end
end