postgresql_adapter.rb 25.7 KB
Newer Older
D
Initial  
David Heinemeier Hansson 已提交
1
require 'active_record/connection_adapters/abstract_adapter'
2
require 'active_record/connection_adapters/statement_pool'
3
require 'active_record/connection_adapters/postgresql/oid'
4
require 'active_record/connection_adapters/postgresql/cast'
5
require 'active_record/connection_adapters/postgresql/array_parser'
6 7 8 9
require 'active_record/connection_adapters/postgresql/quoting'
require 'active_record/connection_adapters/postgresql/schema_statements'
require 'active_record/connection_adapters/postgresql/database_statements'
require 'active_record/connection_adapters/postgresql/referential_integrity'
10
require 'arel/visitors/bind_visitor'
11 12 13

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

D
Dan McClain 已提交
16 17
require 'ipaddr'

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

24
      # Forward any unused config params to PGconn.connect.
25
      [:statement_limit, :encoding, :min_messages, :schema_search_path,
26
       :schema_order, :adapter, :pool, :checkout_timeout, :template,
27
       :reaping_frequency, :insert_returning].each do |key|
28 29
        conn_params.delete key
      end
30
      conn_params.delete_if { |k,v| v.nil? }
31 32 33 34

      # 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 已提交
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 43 44
  module ConnectionAdapters
    # PostgreSQL-specific extensions to column definitions in a table.
    class PostgreSQLColumn < Column #:nodoc:
45
      attr_accessor :array
46
      # Instantiates a new PostgreSQL column definition in a table.
47 48
      def initialize(name, default, oid_type, sql_type = nil, null = true)
        @oid_type = oid_type
49 50 51 52 53 54 55
        if sql_type =~ /\[\]$/
          @array = true
          super(name, self.class.extract_value_from_default(default), sql_type[0..sql_type.length - 3], null)
        else
          @array = false
          super(name, self.class.extract_value_from_default(default), sql_type, null)
        end
56
      end
57

58 59
      # :stopdoc:
      class << self
60
        include ConnectionAdapters::PostgreSQLColumn::Cast
61
        include ConnectionAdapters::PostgreSQLColumn::ArrayParser
62
        attr_accessor :money_precision
63 64 65
      end
      # :startdoc:

66 67 68 69 70 71 72 73 74 75 76 77 78 79
      # Extracts the value from a PostgreSQL column default definition.
      def self.extract_value_from_default(default)
        # This is a performance optimization for Ruby 1.9.2 in development.
        # If the value is nil, we return nil straight away without checking
        # the regular expressions. If we check each regular expression,
        # Regexp#=== will call NilClass#to_str, which will trigger
        # method_missing (defined by whiny nil in ActiveSupport) which
        # makes this method very very slow.
        return default unless default

        case default
          # Numeric types
          when /\A\(?(-?\d+(\.\d*)?\)?)\z/
            $1
80
          # Character types
81 82 83 84 85
          when /\A'(.*)'::(?:character varying|bpchar|text)\z/m
            $1
          # Character types (8.1 formatting)
          when /\AE'(.*)'::(?:character varying|bpchar|text)\z/m
            $1.gsub(/\\(\d\d\d)/) { $1.oct.chr }
86
          # Binary data types
87 88
          when /\A'(.*)'::bytea\z/m
            $1
89
          # Date/time types
90 91 92 93 94 95 96 97 98
          when /\A'(.+)'::(?:time(?:stamp)? with(?:out)? time zone|date)\z/
            $1
          when /\A'(.*)'::interval\z/
            $1
          # Boolean type
          when 'true'
            true
          when 'false'
            false
99
          # Geometric types
100 101
          when /\A'(.*)'::(?:point|line|lseg|box|"?path"?|polygon|circle)\z/
            $1
102
          # Network address types
103 104 105 106 107
          when /\A'(.*)'::(?:cidr|inet|macaddr)\z/
            $1
          # Bit string types
          when /\AB'(.*)'::"?bit(?: varying)?"?\z/
            $1
108
          # XML type
109 110
          when /\A'(.*)'::xml\z/m
            $1
111
          # Arrays
112 113
          when /\A'(.*)'::"?\D+"?\[\]\z/
            $1
114 115 116
          # Hstore
          when /\A'(.*)'::hstore\z/
            $1
117 118 119
          # JSON
          when /\A'(.*)'::json\z/
            $1
120
          # Object identifier types
121 122
          when /\A-?\d+\z/
            $1
123
          else
124 125 126
            # Anything else is blank, some user type, or some function
            # and we can't know the value of that, so return nil.
            nil
127
        end
128
      end
129

130 131 132 133 134 135 136
      def type_cast(value)
        return if value.nil?
        return super if encoded?

        @oid_type.type_cast value
      end

137
      private
138 139 140 141 142 143 144 145

        def extract_limit(sql_type)
          case sql_type
          when /^bigint/i;    8
          when /^smallint/i;  2
          when /^timestamp/i; nil
          else super
          end
146 147
        end

148 149 150 151 152
        # Extracts the scale from PostgreSQL-specific data types.
        def extract_scale(sql_type)
          # Money type has a fixed scale of 2.
          sql_type =~ /^money/ ? 2 : super
        end
153

154 155 156 157 158 159 160 161 162
        # Extracts the precision from PostgreSQL-specific data types.
        def extract_precision(sql_type)
          if sql_type == 'money'
            self.class.money_precision
          elsif sql_type =~ /timestamp/i
            $1.to_i if sql_type =~ /\((\d+)\)/
          else
            super
          end
163 164
        end

165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
        # Maps PostgreSQL-specific data types to logical Rails types.
        def simplified_type(field_type)
          case field_type
          # Numeric and monetary types
          when /^(?:real|double precision)$/
            :float
          # Monetary types
          when 'money'
            :decimal
          when 'hstore'
            :hstore
          # Network address types
          when 'inet'
            :inet
          when 'cidr'
            :cidr
          when 'macaddr'
            :macaddr
          # Character types
          when /^(?:character varying|bpchar)(?:\(\d+\))?$/
            :string
          # Binary data types
          when 'bytea'
            :binary
          # Date/time types
          when /^timestamp with(?:out)? time zone$/
            :datetime
192
          when /^interval(?:|\(\d+\))$/
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
            :string
          # Geometric types
          when /^(?:point|line|lseg|box|"?path"?|polygon|circle)$/
            :string
          # Bit strings
          when /^bit(?: varying)?(?:\(\d+\))?$/
            :string
          # XML type
          when 'xml'
            :xml
          # tsvector type
          when 'tsvector'
            :tsvector
          # Arrays
          when /^\D+\[\]$/
            :string
          # Object identifier types
          when 'oid'
            :integer
          # UUID type
          when 'uuid'
            :uuid
215 216 217
        # JSON type
        when 'json'
          :json
218 219 220 221 222 223 224
          # Small and big integer types
          when /^(?:small|big)int$/
            :integer
          # Pass through all types that are not specific to PostgreSQL.
          else
            super
          end
225
        end
D
Initial  
David Heinemeier Hansson 已提交
226 227
    end

228
    # The PostgreSQL adapter works with the native C (https://bitbucket.org/ged/ruby-pg) driver.
229 230 231
    #
    # Options:
    #
232 233
    # * <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 已提交
234
    # * <tt>:port</tt> - Defaults to 5432.
235 236 237
    # * <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.
238
    # * <tt>:schema_search_path</tt> - An optional schema search path for the connection given
239
    #   as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
240
    # * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO
241
    #   <encoding></tt> call on the connection.
242
    # * <tt>:min_messages</tt> - An optional client min messages that is used in a
243
    #   <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
A
AvnerCohen 已提交
244
    # * <tt>:insert_returning</tt> - An optional boolean to control the use or <tt>RETURNING</tt> for <tt>INSERT</tt> statements
245
    #   defaults to true.
246 247 248 249 250 251 252
    #
    # Any further options are used as connection parameters to libpq. See
    # http://www.postgresql.org/docs/9.1/static/libpq-connect.html for the
    # list of parameters.
    #
    # In addition, default connection parameters of libpq can be set per environment variables.
    # See http://www.postgresql.org/docs/9.1/static/libpq-envars.html .
253
    class PostgreSQLAdapter < AbstractAdapter
254 255 256 257
      class ColumnDefinition < ActiveRecord::ConnectionAdapters::ColumnDefinition
        attr_accessor :array
      end

258 259 260 261 262
      class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
        def xml(*args)
          options = args.extract_options!
          column(args[0], 'xml', options)
        end
263 264 265 266 267

        def tsvector(*args)
          options = args.extract_options!
          column(args[0], 'tsvector', options)
        end
268 269 270 271

        def hstore(name, options = {})
          column(name, 'hstore', options)
        end
272 273 274 275 276 277 278 279 280 281 282 283

        def inet(name, options = {})
          column(name, 'inet', options)
        end

        def cidr(name, options = {})
          column(name, 'cidr', options)
        end

        def macaddr(name, options = {})
          column(name, 'macaddr', options)
        end
284 285 286 287

        def uuid(name, options = {})
          column(name, 'uuid', options)
        end
288 289 290 291

        def json(name, options = {})
          column(name, 'json', options)
        end
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308

        def column(name, type = nil, options = {})
          super
          column = self[name]
          column.array = options[:array]

          self
        end

        private

        def new_column_definition(base, name, type)
          definition = ColumnDefinition.new base, name, type
          @columns << definition
          @columns_hash[name] = definition
          definition
        end
309 310
      end

311
      ADAPTER_NAME = 'PostgreSQL'
312 313

      NATIVE_DATABASE_TYPES = {
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
        primary_key: "serial primary key",
        string:      { name: "character varying", limit: 255 },
        text:        { name: "text" },
        integer:     { name: "integer" },
        float:       { name: "float" },
        decimal:     { name: "decimal" },
        datetime:    { name: "timestamp" },
        timestamp:   { name: "timestamp" },
        time:        { name: "time" },
        date:        { name: "date" },
        binary:      { name: "bytea" },
        boolean:     { name: "boolean" },
        xml:         { name: "xml" },
        tsvector:    { name: "tsvector" },
        hstore:      { name: "hstore" },
        inet:        { name: "inet" },
        cidr:        { name: "cidr" },
        macaddr:     { name: "macaddr" },
332 333
        uuid:        { name: "uuid" },
        json:        { name: "json" }
334 335
      }

336 337 338 339 340
      include Quoting
      include ReferentialIntegrity
      include SchemaStatements
      include DatabaseStatements

341
      # Returns 'PostgreSQL' as adapter name for identification purposes.
342
      def adapter_name
343
        ADAPTER_NAME
344 345
      end

346 347 348 349 350 351 352 353 354 355 356 357 358
      # Adds `:array` option to the default set provided by the
      # AbstractAdapter
      def prepare_column_options(column, types)
        spec = super
        spec[:array] = 'true' if column.respond_to?(:array) && column.array
        spec
      end

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

359 360
      # Returns +true+, since this connection adapter supports prepared statement
      # caching.
361 362 363 364
      def supports_statement_cache?
        true
      end

365 366 367 368
      def supports_index_sort_order?
        true
      end

369 370 371 372
      def supports_partial_index?
        true
      end

373 374 375 376
      def supports_transaction_isolation?
        true
      end

377 378 379 380
      class StatementPool < ConnectionAdapters::StatementPool
        def initialize(connection, max)
          super
          @counter = 0
381
          @cache   = Hash.new { |h,pid| h[pid] = {} }
382 383
        end

384 385 386 387
        def each(&block); cache.each(&block); end
        def key?(key);    cache.key?(key); end
        def [](key);      cache[key]; end
        def length;       cache.length; end
388 389 390 391 392 393

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

        def []=(sql, key)
394 395
          while @max <= cache.size
            dealloc(cache.shift.last)
396 397
          end
          @counter += 1
398
          cache[sql] = key
399 400 401
        end

        def clear
402
          cache.each_value do |stmt_key|
403 404
            dealloc stmt_key
          end
405
          cache.clear
406 407
        end

408 409 410 411 412
        def delete(sql_key)
          dealloc cache[sql_key]
          cache.delete sql_key
        end

413
        private
414

415 416 417
          def cache
            @cache[Process.pid]
          end
418

419 420 421 422 423 424 425 426 427
          def dealloc(key)
            @connection.query "DEALLOCATE #{key}" if connection_active?
          end

          def connection_active?
            @connection.status == PGconn::CONNECTION_OK
          rescue PGError
            false
          end
428 429
      end

430 431 432 433
      class BindSubstitution < Arel::Visitors::PostgreSQL # :nodoc:
        include Arel::Visitors::BindVisitor
      end

434 435
      # Initializes and connects a PostgreSQL adapter.
      def initialize(connection, logger, connection_parameters, config)
436
        super(connection, logger)
437 438 439 440 441 442 443 444 445

        if config.fetch(:prepared_statements) { true }
          @visitor = Arel::Visitors::PostgreSQL.new self
        else
          @visitor = BindSubstitution.new self
        end

        connection_parameters.delete :prepared_statements

446
        @connection_parameters, @config = connection_parameters, config
447

448 449
        # @local_tz is initialized as nil to avoid warnings when connect tries to use it
        @local_tz = nil
450 451
        @table_alias_length = nil

452
        connect
453 454
        @statements = StatementPool.new @connection,
                                        config.fetch(:statement_limit) { 1000 }
455 456 457 458 459

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

460
        initialize_type_map
461
        @local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
462
        @use_insert_returning = @config.key?(:insert_returning) ? @config[:insert_returning] : true
463 464
      end

X
Xavier Noria 已提交
465
      # Clears the prepared statements cache.
466 467 468 469
      def clear_cache!
        @statements.clear
      end

470 471
      # Is this connection alive and ready for queries?
      def active?
472 473
        @connection.query 'SELECT 1'
        true
474
      rescue PGError
475
        false
476 477 478 479
      end

      # Close then reopen the connection.
      def reconnect!
480
        super
481 482
        @connection.reset
        configure_connection
483
      end
484

485 486 487 488 489
      def reset!
        clear_cache!
        super
      end

490 491
      # Disconnects from the database if already connected. Otherwise, this
      # method does nothing.
492
      def disconnect!
493
        super
494 495
        @connection.close rescue nil
      end
496

497
      def native_database_types #:nodoc:
498
        NATIVE_DATABASE_TYPES
499
      end
500

501
      # Returns true, since this connection adapter supports migrations.
502 503
      def supports_migrations?
        true
504 505
      end

506
      # Does PostgreSQL support finding primary key on non-Active Record tables?
507 508 509 510
      def supports_primary_key? #:nodoc:
        true
      end

511 512 513
      # Enable standard-conforming strings if available.
      def set_standard_conforming_strings
        old, self.client_min_messages = client_min_messages, 'panic'
514
        execute('SET standard_conforming_strings = on', 'SCHEMA') rescue nil
515 516
      ensure
        self.client_min_messages = old
517 518
      end

519
      def supports_insert_with_returning?
520
        true
521 522
      end

523 524 525
      def supports_ddl_transactions?
        true
      end
526

527
      # Returns true, since this connection adapter supports savepoints.
528 529 530
      def supports_savepoints?
        true
      end
531

532 533 534 535 536
      # Returns true.
      def supports_explain?
        true
      end

537
      # Returns the configured supported identifier length supported by PostgreSQL
538
      def table_alias_length
K
kennyj 已提交
539
        @table_alias_length ||= query('SHOW max_identifier_length', 'SCHEMA')[0][0].to_i
540
      end
541

542 543 544 545 546 547 548
      def add_column_options!(sql, options)
        if options[:array] || options[:column].try(:array)
          sql << '[]'
        end
        super
      end

549 550
      # Set the authorized user for this session
      def session_auth=(user)
551
        clear_cache!
A
Aaron Patterson 已提交
552
        exec_query "SET SESSION AUTHORIZATION #{user}"
553 554
      end

555
      module Utils
556 557
        extend self

558 559 560 561 562 563 564 565 566 567
        # Returns an array of <tt>[schema_name, table_name]</tt> extracted from +name+.
        # +schema_name+ is nil if not specified in +name+.
        # +schema_name+ and +table_name+ exclude surrounding quotes (regardless of whether provided in +name+)
        # +name+ supports the range of schema/table references understood by PostgreSQL, for example:
        #
        # * <tt>table_name</tt>
        # * <tt>"table.name"</tt>
        # * <tt>schema_name.table_name</tt>
        # * <tt>schema_name."table.name"</tt>
        # * <tt>"schema.name"."table name"</tt>
568
        def extract_schema_and_table(name)
569 570 571 572 573
          table, schema = name.scan(/[^".\s]+|"[^"]*"/)[0..1].collect{|m| m.gsub(/(^"|"$)/,'') }.reverse
          [schema, table]
        end
      end

574 575
      def use_insert_returning?
        @use_insert_returning
576 577
      end

578
      protected
579

580
        # Returns the version of the connected PostgreSQL server.
581
        def postgresql_version
582
          @connection.server_version
583 584
        end

585 586 587 588
        # See http://www.postgresql.org/docs/9.1/static/errcodes-appendix.html
        FOREIGN_KEY_VIOLATION = "23503"
        UNIQUE_VIOLATION      = "23505"

589
        def translate_exception(exception, message)
590
          case exception.result.try(:error_field, PGresult::PG_DIAG_SQLSTATE)
591
          when UNIQUE_VIOLATION
592
            RecordNotUnique.new(message, exception)
593
          when FOREIGN_KEY_VIOLATION
594
            InvalidForeignKey.new(message, exception)
595 596 597 598 599
          else
            super
          end
        end

D
Initial  
David Heinemeier Hansson 已提交
600
      private
601

602
        def initialize_type_map
603
          result = execute('SELECT oid, typname, typelem, typdelim, typinput FROM pg_type', 'SCHEMA')
604
          leaves, nodes = result.partition { |row| row['typelem'] == '0' }
605

606 607 608 609 610
          # populate the leaf nodes
          leaves.find_all { |row| OID.registered_type? row['typname'] }.each do |row|
            OID::TYPE_MAP[row['oid'].to_i] = OID::NAMES[row['typname']]
          end

611 612
          arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in' }

613 614 615 616 617
          # populate composite types
          nodes.find_all { |row| OID::TYPE_MAP.key? row['typelem'].to_i }.each do |row|
            vector = OID::Vector.new row['typdelim'], OID::TYPE_MAP[row['typelem'].to_i]
            OID::TYPE_MAP[row['oid'].to_i] = vector
          end
618 619 620 621 622 623

          # populate array types
          arrays.find_all { |row| OID::TYPE_MAP.key? row['typelem'].to_i }.each do |row|
            array = OID::Array.new  OID::TYPE_MAP[row['typelem'].to_i]
            OID::TYPE_MAP[row['oid'].to_i] = array
          end
624 625
        end

626 627
        FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:

628 629
        def exec_no_cache(sql, binds)
          @connection.async_exec(sql)
630
        end
631

632
        def exec_cache(sql, binds)
633 634 635 636 637 638 639 640 641 642 643 644 645 646 647
          begin
            stmt_key = prepare_statement sql

            # Clear the queue
            @connection.get_last_result
            @connection.send_query_prepared(stmt_key, binds.map { |col, val|
              type_cast(val, col)
            })
            @connection.block
            @connection.get_last_result
          rescue PGError => e
            # Get the PG code for the failure.  Annoyingly, the code for
            # prepared statements whose return value may have changed is
            # FEATURE_NOT_SUPPORTED.  Check here for more details:
            # http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
648 649 650 651 652
            begin
              code = e.result.result_error_field(PGresult::PG_DIAG_SQLSTATE)
            rescue
              raise e
            end
653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671
            if FEATURE_NOT_SUPPORTED == code
              @statements.delete sql_key(sql)
              retry
            else
              raise e
            end
          end
        end

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

        # Prepare the statement if it hasn't been prepared, return
        # the statement key.
        def prepare_statement(sql)
          sql_key = sql_key(sql)
672
          unless @statements.key? sql_key
673
            nextkey = @statements.next_key
674
            @connection.prepare nextkey, sql
675
            @statements[sql_key] = nextkey
676
          end
677
          @statements[sql_key]
678
        end
679

P
Pratik Naik 已提交
680
        # The internal PostgreSQL identifier of the money data type.
681
        MONEY_COLUMN_TYPE_OID = 790 #:nodoc:
682 683
        # The internal PostgreSQL identifier of the BYTEA data type.
        BYTEA_COLUMN_TYPE_OID = 17 #:nodoc:
684 685 686 687

        # Connects to a PostgreSQL server and sets up the adapter depending on the
        # connected server's characteristics.
        def connect
688
          @connection = PGconn.connect(@connection_parameters)
689 690 691 692

          # Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of
          # PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision
          # should know about this but can't detect it there, so deal with it here.
693 694
          PostgreSQLColumn.money_precision = (postgresql_version >= 80300) ? 19 : 10

695 696 697
          configure_connection
        end

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

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

710
          # If using Active Record's time zone support configure the connection to return
711
          # TIMESTAMP WITH ZONE types in UTC.
712
          if ActiveRecord::Base.default_timezone == :utc
713
            execute("SET time zone 'UTC'", 'SCHEMA')
714
          elsif @local_tz
715
            execute("SET time zone '#{@local_tz}'", 'SCHEMA')
716
          end
717 718
        end

719
        # Returns the current ID of a table's sequence.
720
        def last_insert_id(sequence_name) #:nodoc:
721 722 723
          Integer(last_insert_id_value(sequence_name))
        end

D
Doug Cole 已提交
724 725 726 727 728
        def last_insert_id_value(sequence_name)
          last_insert_id_result(sequence_name).rows.first.first
        end

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

732
        # Executes a SELECT query and returns the results, performing any data type
733
        # conversions that are required to be performed here instead of in PostgreSQLColumn.
734
        def select(sql, name = nil, binds = [])
735
          exec_query(sql, name, binds)
736 737 738
        end

        def select_raw(sql, name = nil)
739
          res = execute(sql, name)
740
          results = result_as_array(res)
741
          fields = res.fields
742
          res.clear
743
          return fields, results
M
Marcel Molina 已提交
744 745
        end

746
        # Returns the list of a table's column names, data types, and default values.
747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763
        #
        # The underlying query is roughly:
        #  SELECT column.name, column.type, default.value
        #    FROM column LEFT JOIN default
        #      ON column.table_id = default.table_id
        #     AND column.num = default.column_num
        #   WHERE column.table_id = get_table_id('table_name')
        #     AND column.num > 0
        #     AND NOT column.is_dropped
        #   ORDER BY column.num
        #
        # If the table name is not prefixed with a schema, the database will
        # take the first match from the schema search path.
        #
        # Query implementation notes:
        #  - format_type includes the column size constraint, e.g. varchar(50)
        #  - ::regclass is a function that gives the id for a table name
764
        def column_definitions(table_name) #:nodoc:
765
          exec_query(<<-end_sql, 'SCHEMA').rows
766 767 768 769 770 771
              SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull, a.atttypid, a.atttypmod
                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
772
          end_sql
D
Initial  
David Heinemeier Hansson 已提交
773
        end
774 775

        def extract_pg_identifier_from_name(name)
776
          match_data = name.start_with?('"') ? name.match(/\"([^\"]+)\"/) : name.match(/([^\.]+)/)
777 778

          if match_data
779 780
            rest = name[match_data[0].length, name.length]
            rest = rest[1, rest.length] if rest.start_with? "."
J
José Valim 已提交
781
            [match_data[1], (rest.length > 0 ? rest : nil)]
782 783
          end
        end
784

785 786 787 788 789
        def extract_table_ref_from_insert_sql(sql)
          sql[/into\s+([^\(]*).*values\s*\(/i]
          $1.strip if $1
        end

790 791 792
        def table_definition
          TableDefinition.new(self)
        end
D
Initial  
David Heinemeier Hansson 已提交
793 794 795
    end
  end
end