postgresql_adapter.rb 23.9 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 5 6 7 8
require 'active_record/connection_adapters/postgresql/cast'
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'
9
require 'arel/visitors/bind_visitor'
10 11 12

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

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

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

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

      # 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 已提交
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 42 43 44
  module ConnectionAdapters
    # PostgreSQL-specific extensions to column definitions in a table.
    class PostgreSQLColumn < Column #:nodoc:
      # Instantiates a new PostgreSQL column definition in a table.
45 46
      def initialize(name, default, oid_type, sql_type = nil, null = true)
        @oid_type = oid_type
47 48
        super(name, self.class.extract_value_from_default(default), sql_type, null)
      end
49

50 51
      # :stopdoc:
      class << self
52
        include ConnectionAdapters::PostgreSQLColumn::Cast
53

54
        attr_accessor :money_precision
55 56 57
      end
      # :startdoc:

58 59 60 61 62 63 64 65 66 67 68 69 70 71
      # 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
72
          # Character types
73 74 75 76 77
          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 }
78
          # Binary data types
79 80
          when /\A'(.*)'::bytea\z/m
            $1
81
          # Date/time types
82 83 84 85 86 87 88 89 90
          when /\A'(.+)'::(?:time(?:stamp)? with(?:out)? time zone|date)\z/
            $1
          when /\A'(.*)'::interval\z/
            $1
          # Boolean type
          when 'true'
            true
          when 'false'
            false
91
          # Geometric types
92 93
          when /\A'(.*)'::(?:point|line|lseg|box|"?path"?|polygon|circle)\z/
            $1
94
          # Network address types
95 96 97 98 99
          when /\A'(.*)'::(?:cidr|inet|macaddr)\z/
            $1
          # Bit string types
          when /\AB'(.*)'::"?bit(?: varying)?"?\z/
            $1
100
          # XML type
101 102
          when /\A'(.*)'::xml\z/m
            $1
103
          # Arrays
104 105
          when /\A'(.*)'::"?\D+"?\[\]\z/
            $1
106 107 108
          # Hstore
          when /\A'(.*)'::hstore\z/
            $1
109 110 111
          # JSON
          when /\A'(.*)'::json\z/
            $1
112
          # Object identifier types
113 114
          when /\A-?\d+\z/
            $1
115
          else
116 117 118
            # Anything else is blank, some user type, or some function
            # and we can't know the value of that, so return nil.
            nil
119
        end
120
      end
121

122 123 124 125 126 127 128
      def type_cast(value)
        return if value.nil?
        return super if encoded?

        @oid_type.type_cast value
      end

129
      private
130 131 132 133 134 135 136 137

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

140 141 142 143 144
        # 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
145

146 147 148 149 150 151 152 153 154
        # 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
155 156
        end

157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
        # 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
184
          when /^interval(?:|\(\d+\))$/
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
            :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
207 208 209
        # JSON type
        when 'json'
          :json
210 211 212 213 214 215 216
          # Small and big integer types
          when /^(?:small|big)int$/
            :integer
          # Pass through all types that are not specific to PostgreSQL.
          else
            super
          end
217
        end
D
Initial  
David Heinemeier Hansson 已提交
218 219
    end

220
    # The PostgreSQL adapter works with the native C (https://bitbucket.org/ged/ruby-pg) driver.
221 222 223
    #
    # Options:
    #
224 225
    # * <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 已提交
226
    # * <tt>:port</tt> - Defaults to 5432.
227 228 229
    # * <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.
230
    # * <tt>:schema_search_path</tt> - An optional schema search path for the connection given
231
    #   as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
232
    # * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO
233
    #   <encoding></tt> call on the connection.
234
    # * <tt>:min_messages</tt> - An optional client min messages that is used in a
235
    #   <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
236 237
    # * <tt>:insert_returning</tt> - An optional boolean to control the use or <tt>RETURNING</tt> for <tt>INSERT<tt> statements
    #   defaults to true.
238 239 240 241 242 243 244
    #
    # 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 .
245
    class PostgreSQLAdapter < AbstractAdapter
246 247 248 249 250
      class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
        def xml(*args)
          options = args.extract_options!
          column(args[0], 'xml', options)
        end
251 252 253 254 255

        def tsvector(*args)
          options = args.extract_options!
          column(args[0], 'tsvector', options)
        end
256 257 258 259

        def hstore(name, options = {})
          column(name, 'hstore', options)
        end
260 261 262 263 264 265 266 267 268 269 270 271

        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
272 273 274 275

        def uuid(name, options = {})
          column(name, 'uuid', options)
        end
276 277 278 279

        def json(name, options = {})
          column(name, 'json', options)
        end
280 281
      end

282
      ADAPTER_NAME = 'PostgreSQL'
283 284

      NATIVE_DATABASE_TYPES = {
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
        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" },
303 304
        uuid:        { name: "uuid" },
        json:        { name: "json" }
305 306
      }

307 308 309 310 311
      include Quoting
      include ReferentialIntegrity
      include SchemaStatements
      include DatabaseStatements

312
      # Returns 'PostgreSQL' as adapter name for identification purposes.
313
      def adapter_name
314
        ADAPTER_NAME
315 316
      end

317 318
      # Returns +true+, since this connection adapter supports prepared statement
      # caching.
319 320 321 322
      def supports_statement_cache?
        true
      end

323 324 325 326
      def supports_index_sort_order?
        true
      end

327 328 329 330
      def supports_partial_index?
        true
      end

331 332 333 334
      class StatementPool < ConnectionAdapters::StatementPool
        def initialize(connection, max)
          super
          @counter = 0
335
          @cache   = Hash.new { |h,pid| h[pid] = {} }
336 337
        end

338 339 340 341
        def each(&block); cache.each(&block); end
        def key?(key);    cache.key?(key); end
        def [](key);      cache[key]; end
        def length;       cache.length; end
342 343 344 345 346 347

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

        def []=(sql, key)
348 349
          while @max <= cache.size
            dealloc(cache.shift.last)
350 351
          end
          @counter += 1
352
          cache[sql] = key
353 354 355
        end

        def clear
356
          cache.each_value do |stmt_key|
357 358
            dealloc stmt_key
          end
359
          cache.clear
360 361
        end

362 363 364 365 366
        def delete(sql_key)
          dealloc cache[sql_key]
          cache.delete sql_key
        end

367
        private
368

369 370 371
          def cache
            @cache[Process.pid]
          end
372

373 374 375 376 377 378 379 380 381
          def dealloc(key)
            @connection.query "DEALLOCATE #{key}" if connection_active?
          end

          def connection_active?
            @connection.status == PGconn::CONNECTION_OK
          rescue PGError
            false
          end
382 383
      end

384 385 386 387
      class BindSubstitution < Arel::Visitors::PostgreSQL # :nodoc:
        include Arel::Visitors::BindVisitor
      end

388 389
      # Initializes and connects a PostgreSQL adapter.
      def initialize(connection, logger, connection_parameters, config)
390
        super(connection, logger)
391 392 393 394 395 396 397 398 399

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

        connection_parameters.delete :prepared_statements

400
        @connection_parameters, @config = connection_parameters, config
401

402 403
        # @local_tz is initialized as nil to avoid warnings when connect tries to use it
        @local_tz = nil
404 405
        @table_alias_length = nil

406
        connect
407 408
        @statements = StatementPool.new @connection,
                                        config.fetch(:statement_limit) { 1000 }
409 410 411 412 413

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

414
        initialize_type_map
415
        @local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
416
        @use_insert_returning = @config.key?(:insert_returning) ? @config[:insert_returning] : true
417 418
      end

X
Xavier Noria 已提交
419
      # Clears the prepared statements cache.
420 421 422 423
      def clear_cache!
        @statements.clear
      end

424 425
      # Is this connection alive and ready for queries?
      def active?
426 427
        @connection.query 'SELECT 1'
        true
428
      rescue PGError
429
        false
430 431 432 433
      end

      # Close then reopen the connection.
      def reconnect!
434 435 436
        clear_cache!
        @connection.reset
        configure_connection
437
      end
438

439 440 441 442 443
      def reset!
        clear_cache!
        super
      end

444 445
      # Disconnects from the database if already connected. Otherwise, this
      # method does nothing.
446
      def disconnect!
447
        clear_cache!
448 449
        @connection.close rescue nil
      end
450

451
      def native_database_types #:nodoc:
452
        NATIVE_DATABASE_TYPES
453
      end
454

455
      # Returns true, since this connection adapter supports migrations.
456 457
      def supports_migrations?
        true
458 459
      end

460
      # Does PostgreSQL support finding primary key on non-Active Record tables?
461 462 463 464
      def supports_primary_key? #:nodoc:
        true
      end

465 466 467
      # Enable standard-conforming strings if available.
      def set_standard_conforming_strings
        old, self.client_min_messages = client_min_messages, 'panic'
468
        execute('SET standard_conforming_strings = on', 'SCHEMA') rescue nil
469 470
      ensure
        self.client_min_messages = old
471 472
      end

473
      def supports_insert_with_returning?
474
        true
475 476
      end

477 478 479
      def supports_ddl_transactions?
        true
      end
480

481
      # Returns true, since this connection adapter supports savepoints.
482 483 484
      def supports_savepoints?
        true
      end
485

486 487 488 489 490
      # Returns true.
      def supports_explain?
        true
      end

491
      # Returns the configured supported identifier length supported by PostgreSQL
492
      def table_alias_length
K
kennyj 已提交
493
        @table_alias_length ||= query('SHOW max_identifier_length', 'SCHEMA')[0][0].to_i
494
      end
495

496 497
      # Set the authorized user for this session
      def session_auth=(user)
498
        clear_cache!
A
Aaron Patterson 已提交
499
        exec_query "SET SESSION AUTHORIZATION #{user}"
500 501
      end

502
      module Utils
503 504
        extend self

505 506 507 508 509 510 511 512 513 514
        # 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>
515
        def extract_schema_and_table(name)
516 517 518 519 520
          table, schema = name.scan(/[^".\s]+|"[^"]*"/)[0..1].collect{|m| m.gsub(/(^"|"$)/,'') }.reverse
          [schema, table]
        end
      end

521 522
      def use_insert_returning?
        @use_insert_returning
523 524
      end

525
      protected
526

527
        # Returns the version of the connected PostgreSQL server.
528
        def postgresql_version
529
          @connection.server_version
530 531
        end

532 533 534 535
        # See http://www.postgresql.org/docs/9.1/static/errcodes-appendix.html
        FOREIGN_KEY_VIOLATION = "23503"
        UNIQUE_VIOLATION      = "23505"

536
        def translate_exception(exception, message)
537
          case exception.result.try(:error_field, PGresult::PG_DIAG_SQLSTATE)
538
          when UNIQUE_VIOLATION
539
            RecordNotUnique.new(message, exception)
540
          when FOREIGN_KEY_VIOLATION
541
            InvalidForeignKey.new(message, exception)
542 543 544 545 546
          else
            super
          end
        end

D
Initial  
David Heinemeier Hansson 已提交
547
      private
548

549 550 551
        def initialize_type_map
          result = execute('SELECT oid, typname, typelem, typdelim FROM pg_type', 'SCHEMA')
          leaves, nodes = result.partition { |row| row['typelem'] == '0' }
552

553 554 555 556 557 558 559 560 561 562
          # 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

          # 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
563 564
        end

565 566
        FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:

567 568
        def exec_no_cache(sql, binds)
          @connection.async_exec(sql)
569
        end
570

571
        def exec_cache(sql, binds)
572 573 574 575 576 577 578 579 580 581 582 583 584 585 586
          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
587 588 589 590 591
            begin
              code = e.result.result_error_field(PGresult::PG_DIAG_SQLSTATE)
            rescue
              raise e
            end
592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610
            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)
611
          unless @statements.key? sql_key
612
            nextkey = @statements.next_key
613
            @connection.prepare nextkey, sql
614
            @statements[sql_key] = nextkey
615
          end
616
          @statements[sql_key]
617
        end
618

P
Pratik Naik 已提交
619
        # The internal PostgreSQL identifier of the money data type.
620
        MONEY_COLUMN_TYPE_OID = 790 #:nodoc:
621 622
        # The internal PostgreSQL identifier of the BYTEA data type.
        BYTEA_COLUMN_TYPE_OID = 17 #:nodoc:
623 624 625 626

        # Connects to a PostgreSQL server and sets up the adapter depending on the
        # connected server's characteristics.
        def connect
627
          @connection = PGconn.connect(@connection_parameters)
628 629 630 631

          # 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.
632 633
          PostgreSQLColumn.money_precision = (postgresql_version >= 80300) ? 19 : 10

634 635 636
          configure_connection
        end

637
        # Configures the encoding, verbosity, schema search path, and time zone of the connection.
638
        # This is called by #connect and should not be called manually.
639 640
        def configure_connection
          if @config[:encoding]
641
            @connection.set_client_encoding(@config[:encoding])
642
          end
643
          self.client_min_messages = @config[:min_messages] || 'warning'
644
          self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
645 646 647 648

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

649
          # If using Active Record's time zone support configure the connection to return
650
          # TIMESTAMP WITH ZONE types in UTC.
651
          if ActiveRecord::Base.default_timezone == :utc
652
            execute("SET time zone 'UTC'", 'SCHEMA')
653
          elsif @local_tz
654
            execute("SET time zone '#{@local_tz}'", 'SCHEMA')
655
          end
656 657
        end

658
        # Returns the current ID of a table's sequence.
659
        def last_insert_id(sequence_name) #:nodoc:
660 661 662
          Integer(last_insert_id_value(sequence_name))
        end

D
Doug Cole 已提交
663 664 665 666 667
        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:
668
          exec_query("SELECT currval('#{sequence_name}')", 'SQL')
D
Initial  
David Heinemeier Hansson 已提交
669 670
        end

671
        # Executes a SELECT query and returns the results, performing any data type
672
        # conversions that are required to be performed here instead of in PostgreSQLColumn.
673
        def select(sql, name = nil, binds = [])
674
          exec_query(sql, name, binds)
675 676 677
        end

        def select_raw(sql, name = nil)
678
          res = execute(sql, name)
679
          results = result_as_array(res)
680
          fields = res.fields
681
          res.clear
682
          return fields, results
M
Marcel Molina 已提交
683 684
        end

685
        # Returns the list of a table's column names, data types, and default values.
686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702
        #
        # 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
703
        def column_definitions(table_name) #:nodoc:
704
          exec_query(<<-end_sql, 'SCHEMA').rows
705
            SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull, a.atttypid, a.atttypmod
706 707
              FROM pg_attribute a LEFT JOIN pg_attrdef d
                ON a.attrelid = d.adrelid AND a.attnum = d.adnum
708
             WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
709 710 711
               AND a.attnum > 0 AND NOT a.attisdropped
             ORDER BY a.attnum
          end_sql
D
Initial  
David Heinemeier Hansson 已提交
712
        end
713 714

        def extract_pg_identifier_from_name(name)
715
          match_data = name.start_with?('"') ? name.match(/\"([^\"]+)\"/) : name.match(/([^\.]+)/)
716 717

          if match_data
718 719
            rest = name[match_data[0].length, name.length]
            rest = rest[1, rest.length] if rest.start_with? "."
J
José Valim 已提交
720
            [match_data[1], (rest.length > 0 ? rest : nil)]
721 722
          end
        end
723

724 725 726 727 728
        def extract_table_ref_from_insert_sql(sql)
          sql[/into\s+([^\(]*).*values\s*\(/i]
          $1.strip if $1
        end

729 730 731
        def table_definition
          TableDefinition.new(self)
        end
D
Initial  
David Heinemeier Hansson 已提交
732 733 734
    end
  end
end