schema_definitions.rb 21.9 KB
Newer Older
1
require 'date'
2
require 'set'
3 4
require 'bigdecimal'
require 'bigdecimal/util'
5

6 7
module ActiveRecord
  module ConnectionAdapters #:nodoc:
8 9 10
    # Abstract representation of an index definition on a table. Instances of
    # this type are typically created and returned by methods in database
    # adapters. e.g. ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter#indexes
11
    class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths, :orders, :where, :type, :using) #:nodoc:
12 13
    end

P
Pratik Naik 已提交
14 15 16 17
    # Abstract representation of a column definition. Instances of this type
    # are typically created by methods in TableDefinition, and added to the
    # +columns+ attribute of said TableDefinition object, in order to be used
    # for generating a number of table creation or table changing SQL statements.
18
    class ColumnDefinition < Struct.new(:name, :type, :limit, :precision, :scale, :default, :null, :first, :after, :auto_increment, :primary_key, :sql_type) #:nodoc:
19

A
Aaron Patterson 已提交
20
      def primary_key?
21
        primary_key || type.to_sym == :primary_key
A
Aaron Patterson 已提交
22
      end
23 24
    end

25
    class ChangeColumnDefinition < Struct.new(:column, :name) #:nodoc:
26 27
    end

28
    class ForeignKeyDefinition < Struct.new(:from_table, :to_table, :options) #:nodoc:
29 30 31 32 33 34 35 36 37
      def name
        options[:name]
      end

      def column
        options[:column]
      end

      def primary_key
Y
Yves Senn 已提交
38
        options[:primary_key] || default_primary_key
39
      end
40

41 42
      def on_delete
        options[:on_delete]
43
      end
Y
Yves Senn 已提交
44 45 46 47

      def on_update
        options[:on_update]
      end
Y
Yves Senn 已提交
48 49 50 51 52

      def custom_primary_key?
        options[:primary_key] != default_primary_key
      end

53 54
      def defined_for?(options_or_to_table = {})
        if options_or_to_table.is_a?(Hash)
55
          options_or_to_table.all? {|key, value| options[key].to_s == value.to_s }
56 57 58 59 60
        else
          to_table == options_or_to_table.to_s
        end
      end

Y
Yves Senn 已提交
61 62 63 64
      private
      def default_primary_key
        "id"
      end
65 66
    end

67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
    class ReferenceDefinition # :nodoc:
      def initialize(
        name,
        polymorphic: false,
        index: false,
        foreign_key: false,
        type: :integer,
        **options
      )
        @name = name
        @polymorphic = polymorphic
        @index = index
        @foreign_key = foreign_key
        @type = type
        @options = options

        if polymorphic && foreign_key
          raise ArgumentError, "Cannot add a foreign key to a polymorphic relation"
        end
      end

      def add_to(table)
        columns.each do |column_options|
          table.column(*column_options)
        end

        if index
          table.index(column_names, index_options)
        end

        if foreign_key
          table.foreign_key(foreign_table_name, foreign_key_options)
        end
      end

      protected

      attr_reader :name, :polymorphic, :index, :foreign_key, :type, :options

      private

      def as_options(value, default = {})
        if value.is_a?(Hash)
          value
        else
          default
        end
      end

      def polymorphic_options
        as_options(polymorphic, options)
      end

      def index_options
        as_options(index)
      end

      def foreign_key_options
        as_options(foreign_key)
      end

      def columns
        result = [["#{name}_id", type, options]]
        if polymorphic
          result.unshift(["#{name}_type", :string, polymorphic_options])
        end
        result
      end

      def column_names
        columns.map(&:first)
      end

      def foreign_table_name
        name.to_s.pluralize
      end
    end

145 146 147 148 149 150 151 152
    module ColumnMethods
      # Appends a primary key definition to the table definition.
      # Can be called multiple times, but this is probably not a good idea.
      def primary_key(name, type = :primary_key, **options)
        column(name, type, options.merge(primary_key: true))
      end
    end

P
Pratik Naik 已提交
153 154 155
    # Represents the schema of an SQL table in an abstract way. This class
    # provides methods for manipulating the schema representation.
    #
156 157
    # Inside migration files, the +t+ object in +create_table+
    # is actually of this type:
P
Pratik Naik 已提交
158 159
    #
    #   class SomeMigration < ActiveRecord::Migration
A
Akira Matsuda 已提交
160
    #     def up
P
Pratik Naik 已提交
161 162 163 164
    #       create_table :foo do |t|
    #         puts t.class  # => "ActiveRecord::ConnectionAdapters::TableDefinition"
    #       end
    #     end
P
Pratik Naik 已提交
165
    #
A
Akira Matsuda 已提交
166
    #     def down
P
Pratik Naik 已提交
167 168 169 170 171 172
    #       ...
    #     end
    #   end
    #
    # The table definitions
    # The Columns are stored as a ColumnDefinition in the +columns+ attribute.
173
    class TableDefinition
174 175
      include ColumnMethods

P
Pratik Naik 已提交
176 177
      # An array of ColumnDefinition objects, representing the column changes
      # that have been defined.
178
      attr_accessor :indexes
179
      attr_reader :name, :temporary, :options, :as, :foreign_keys
180

181
      def initialize(types, name, temporary, options, as = nil)
182
        @columns_hash = {}
183
        @indexes = {}
184
        @foreign_keys = {}
185
        @native = types
186 187
        @temporary = temporary
        @options = options
188
        @as = as
189
        @name = name
190 191
      end

192 193
      def columns; @columns_hash.values; end

194
      # Returns a ColumnDefinition for the column with name +name+.
195
      def [](name)
196
        @columns_hash[name.to_s]
197 198
      end

199
      # Instantiates a new column for the table.
200 201
      # The +type+ parameter is normally one of the migrations native types,
      # which is one of the following:
202
      # <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
203
      # <tt>:integer</tt>, <tt>:float</tt>, <tt>:decimal</tt>,
204 205
      # <tt>:datetime</tt>, <tt>:time</tt>, <tt>:date</tt>,
      # <tt>:binary</tt>, <tt>:boolean</tt>.
206
      #
207 208 209 210
      # You may use a type not in this list as long as it is supported by your
      # database (for example, "polygon" in MySQL), but this will not be database
      # agnostic and should usually be avoided.
      #
211
      # Available options are (none of these exists by default):
212
      # * <tt>:limit</tt> -
213
      #   Requests a maximum column length. This is number of characters for <tt>:string</tt> and
214
      #   <tt>:text</tt> columns and number of bytes for <tt>:binary</tt> and <tt>:integer</tt> columns.
215
      # * <tt>:default</tt> -
216
      #   The column's default value. Use nil for NULL.
217
      # * <tt>:null</tt> -
218
      #   Allows or disallows +NULL+ values in the column. This option could
219
      #   have been named <tt>:null_allowed</tt>.
220
      # * <tt>:precision</tt> -
221
      #   Specifies the precision for a <tt>:decimal</tt> column.
222
      # * <tt>:scale</tt> -
223
      #   Specifies the scale for a <tt>:decimal</tt> column.
224 225
      # * <tt>:index</tt> -
      #   Create an index for the column. Can be either <tt>true</tt> or an options hash.
226
      #
227 228
      # Note: The precision is the total number of significant digits
      # and the scale is the number of digits that can be stored following
P
Pratik Naik 已提交
229 230 231 232
      # the decimal point. For example, the number 123.45 has a precision of 5
      # and a scale of 2. A decimal with a precision of 5 and a scale of 2 can
      # range from -999.99 to 999.99.
      #
233 234 235 236 237
      # Please be aware of different RDBMS implementations behavior with
      # <tt>:decimal</tt> columns:
      # * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
      #   <tt>:precision</tt>, and makes no comments about the requirements of
      #   <tt>:precision</tt>.
238
      # * MySQL: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..30].
239
      #   Default is (10,0).
240
      # * PostgreSQL: <tt>:precision</tt> [1..infinity],
241
      #   <tt>:scale</tt> [0..infinity]. No default.
242
      # * SQLite2: Any <tt>:precision</tt> and <tt>:scale</tt> may be used.
243
      #   Internal storage as strings. No default.
244
      # * SQLite3: No restrictions on <tt>:precision</tt> and <tt>:scale</tt>,
245
      #   but the maximum supported <tt>:precision</tt> is 16. No default.
246
      # * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
247
      #   Default is (38,0).
248
      # * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
249
      #   Default unknown.
250
      # * SqlServer?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
251
      #   Default (38,0).
252 253 254
      #
      # This method returns <tt>self</tt>.
      #
255
      # == Examples
256
      #  # Assuming +td+ is an instance of TableDefinition
257
      #  td.column(:granted, :boolean)
P
Pratik Naik 已提交
258
      #  # granted BOOLEAN
259
      #
A
AvnerCohen 已提交
260
      #  td.column(:picture, :binary, limit: 2.megabytes)
P
Pratik Naik 已提交
261
      #  # => picture BLOB(2097152)
262
      #
A
AvnerCohen 已提交
263
      #  td.column(:sales_stage, :string, limit: 20, default: 'new', null: false)
P
Pratik Naik 已提交
264
      #  # => sales_stage VARCHAR(20) DEFAULT 'new' NOT NULL
265
      #
A
AvnerCohen 已提交
266
      #  td.column(:bill_gates_money, :decimal, precision: 15, scale: 2)
P
Pratik Naik 已提交
267
      #  # => bill_gates_money DECIMAL(15,2)
268
      #
A
AvnerCohen 已提交
269
      #  td.column(:sensor_reading, :decimal, precision: 30, scale: 20)
P
Pratik Naik 已提交
270
      #  # => sensor_reading DECIMAL(30,20)
271 272 273
      #
      #  # While <tt>:scale</tt> defaults to zero on most databases, it
      #  # probably wouldn't hurt to include it.
A
AvnerCohen 已提交
274
      #  td.column(:huge_integer, :decimal, precision: 30)
P
Pratik Naik 已提交
275
      #  # => huge_integer DECIMAL(30)
276
      #
P
Pratik Naik 已提交
277 278 279 280
      #  # Defines a column with a database-specific type.
      #  td.column(:foo, 'polygon')
      #  # => foo polygon
      #
281 282
      # == Short-hand examples
      #
P
Pratik Naik 已提交
283
      # Instead of calling +column+ directly, you can also work with the short-hand definitions for the default types.
284 285 286 287 288
      # They use the type as the method name instead of as a parameter and allow for multiple columns to be defined
      # in a single statement.
      #
      # What can be written like this with the regular calls to column:
      #
289
      #   create_table :products do |t|
290 291 292 293 294 295 296
      #     t.column :shop_id,     :integer
      #     t.column :creator_id,  :integer
      #     t.column :item_number, :string
      #     t.column :name,        :string, default: "Untitled"
      #     t.column :value,       :string, default: "Untitled"
      #     t.column :created_at,  :datetime
      #     t.column :updated_at,  :datetime
297
      #   end
298
      #   add_index :products, :item_number
299
      #
300
      # can also be written as follows using the short-hand:
301 302 303
      #
      #   create_table :products do |t|
      #     t.integer :shop_id, :creator_id
304
      #     t.string  :item_number, index: true
A
AvnerCohen 已提交
305
      #     t.string  :name, :value, default: "Untitled"
306
      #     t.timestamps null: false
307 308
      #   end
      #
309
      # There's a short-hand method for each of the type values declared at the top. And then there's
310
      # TableDefinition#timestamps that'll add +created_at+ and +updated_at+ as datetimes.
311 312
      #
      # TableDefinition#references will add an appropriately-named _id column, plus a corresponding _type
313
      # column if the <tt>:polymorphic</tt> option is supplied. If <tt>:polymorphic</tt> is a hash of
314 315
      # options, these will be used when creating the <tt>_type</tt> column. The <tt>:index</tt> option
      # will also create an index, similar to calling <tt>add_index</tt>. So what can be written like this:
316 317 318 319
      #
      #   create_table :taggings do |t|
      #     t.integer :tag_id, :tagger_id, :taggable_id
      #     t.string  :tagger_type
A
AvnerCohen 已提交
320
      #     t.string  :taggable_type, default: 'Photo'
321
      #   end
A
AvnerCohen 已提交
322
      #   add_index :taggings, :tag_id, name: 'index_taggings_on_tag_id'
323
      #   add_index :taggings, [:tagger_id, :tagger_type]
324 325 326 327
      #
      # Can also be written as follows using references:
      #
      #   create_table :taggings do |t|
A
AvnerCohen 已提交
328 329 330
      #     t.references :tag, index: { name: 'index_taggings_on_tag_id' }
      #     t.references :tagger, polymorphic: true, index: true
      #     t.references :taggable, polymorphic: { default: 'Photo' }
331
      #   end
332
      def column(name, type, options = {})
333 334 335
        name = name.to_s
        type = type.to_sym

336
        if @columns_hash[name] && @columns_hash[name].primary_key?
337 338 339
          raise ArgumentError, "you can't redefine the primary key column '#{name}'. To define a custom primary key, pass { id: false } to create_table."
        end

340 341
        index_options = options.delete(:index)
        index(name, index_options.is_a?(Hash) ? index_options : {}) if index_options
342
        @columns_hash[name] = new_column_definition(name, type, options)
343 344
        self
      end
345

346 347 348 349
      def remove_column(name)
        @columns_hash.delete name.to_s
      end

A
Aaron Patterson 已提交
350
      [:string, :text, :integer, :bigint, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean].each do |column_type|
351 352 353 354 355
        define_method column_type do |*args|
          options = args.extract_options!
          column_names = args
          column_names.each { |name| column(name, column_type, options) }
        end
356
      end
357

358 359
      # Adds index options to the indexes hash, keyed by column name
      # This is primarily used to track indexes that need to be created after the table
360
      #
A
AvnerCohen 已提交
361
      #   index(:account_id, name: 'index_projects_on_account_id')
362 363 364
      def index(column_name, options = {})
        indexes[column_name] = options
      end
365

366 367 368 369
      def foreign_key(table_name, options = {}) # :nodoc:
        foreign_keys[table_name] = options
      end

370
      # Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
371 372 373
      # <tt>:updated_at</tt> to the table. See SchemaStatements#add_timestamps
      #
      #   t.timestamps null: false
374
      def timestamps(*args)
375
        options = args.extract_options!
376 377 378

        options[:null] = false if options[:null].nil?

379 380
        column(:created_at, :datetime, options)
        column(:updated_at, :datetime, options)
381 382
      end

R
Robin Dupret 已提交
383 384 385 386 387
      # Adds a reference. Optionally adds a +type+ column, if the
      # +:polymorphic+ option is provided. +references+ and +belongs_to+
      # are acceptable. The reference column will be an +integer+ by default,
      # the +:type+ option can be used to specify a different type. A foreign
      # key will be created if the +:foreign_key+ option is passed.
388 389 390 391
      #
      #  t.references(:user)
      #  t.references(:user, type: "string")
      #  t.belongs_to(:supplier, polymorphic: true)
392
      #
393
      # See SchemaStatements#add_reference
394
      def references(*args, **options)
395
        args.each do |col|
396
          ReferenceDefinition.new(col, **options).add_to(self)
397 398 399 400
        end
      end
      alias :belongs_to :references

401
      def new_column_definition(name, type, options) # :nodoc:
402
        type = aliased_types(type.to_s, type)
403 404 405 406 407
        column = create_column_definition name, type
        limit = options.fetch(:limit) do
          native[type][:limit] if native[type].is_a?(Hash)
        end

408 409 410 411 412 413 414
        column.limit       = limit
        column.precision   = options[:precision]
        column.scale       = options[:scale]
        column.default     = options[:default]
        column.null        = options[:null]
        column.first       = options[:first]
        column.after       = options[:after]
415
        column.auto_increment = options[:auto_increment]
416
        column.primary_key = type == :primary_key || options[:primary_key]
417
        column
418 419
      end

420 421 422 423 424
      private
      def create_column_definition(name, type)
        ColumnDefinition.new name, type
      end

425
      def native
426
        @native
427
      end
S
Sean Griffin 已提交
428

429 430
      def aliased_types(name, fallback)
        'timestamp' == name ? :datetime : fallback
S
Sean Griffin 已提交
431
      end
432
    end
433

434
    class AlterTable # :nodoc:
A
Aaron Patterson 已提交
435
      attr_reader :adds
436 437
      attr_reader :foreign_key_adds
      attr_reader :foreign_key_drops
438 439

      def initialize(td)
A
Aaron Patterson 已提交
440 441
        @td   = td
        @adds = []
442 443
        @foreign_key_adds = []
        @foreign_key_drops = []
444 445 446 447
      end

      def name; @td.name; end

448 449 450 451 452 453 454 455
      def add_foreign_key(to_table, options)
        @foreign_key_adds << ForeignKeyDefinition.new(name, to_table, options)
      end

      def drop_foreign_key(name)
        @foreign_key_drops << name
      end

456 457 458
      def add_column(name, type, options)
        name = name.to_s
        type = type.to_sym
A
Aaron Patterson 已提交
459
        @adds << @td.new_column_definition(name, type, options)
460 461 462
      end
    end

463
    # Represents an SQL table in an abstract way for updating a table.
464 465 466 467 468
    # Also see TableDefinition and SchemaStatements#create_table
    #
    # Available transformations are:
    #
    #   change_table :table do |t|
469
    #     t.primary_key
470 471
    #     t.column
    #     t.index
J
Jarek Radosz 已提交
472
    #     t.rename_index
473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497
    #     t.timestamps
    #     t.change
    #     t.change_default
    #     t.rename
    #     t.references
    #     t.belongs_to
    #     t.string
    #     t.text
    #     t.integer
    #     t.float
    #     t.decimal
    #     t.datetime
    #     t.timestamp
    #     t.time
    #     t.date
    #     t.binary
    #     t.boolean
    #     t.remove
    #     t.remove_references
    #     t.remove_belongs_to
    #     t.remove_index
    #     t.remove_timestamps
    #   end
    #
    class Table
498 499
      include ColumnMethods

500 501
      attr_reader :name

502
      def initialize(table_name, base)
503
        @name = table_name
504 505 506 507
        @base = base
      end

      # Adds a new column to the named table.
508
      #
509
      #  t.column(:name, :string)
510 511
      #
      # See TableDefinition#column for details of the options you can use.
512
      def column(column_name, type, options = {})
513
        @base.add_column(name, column_name, type, options)
514 515
      end

516 517 518
      # Checks to see if a column exists.
      #
      # See SchemaStatements#column_exists?
519
      def column_exists?(column_name, type = nil, options = {})
520
        @base.column_exists?(name, column_name, type, options)
521 522
      end

523
      # Adds a new index to the table. +column_name+ can be a single Symbol, or
524
      # an Array of Symbols.
525 526
      #
      #  t.index(:name)
A
AvnerCohen 已提交
527 528
      #  t.index([:branch_id, :party_id], unique: true)
      #  t.index([:branch_id, :party_id], unique: true, name: 'by_branch_party')
529 530
      #
      # See SchemaStatements#add_index for details of the options you can use.
531
      def index(column_name, options = {})
532
        @base.add_index(name, column_name, options)
533 534
      end

535 536 537
      # Checks to see if an index exists.
      #
      # See SchemaStatements#index_exists?
538
      def index_exists?(column_name, options = {})
539
        @base.index_exists?(name, column_name, options)
540 541
      end

J
Jarek Radosz 已提交
542 543 544
      # Renames the given index on the table.
      #
      #  t.rename_index(:user_id, :account_id)
545 546
      #
      # See SchemaStatements#rename_index
J
Jarek Radosz 已提交
547
      def rename_index(index_name, new_index_name)
548
        @base.rename_index(name, index_name, new_index_name)
J
Jarek Radosz 已提交
549 550
      end

551 552 553
      # Adds timestamps (+created_at+ and +updated_at+) columns to the table.
      #
      #  t.timestamps(null: false)
554
      #
555
      # See SchemaStatements#add_timestamps
556
      def timestamps(options = {})
557
        @base.add_timestamps(name, options)
558 559 560
      end

      # Changes the column's definition according to the new options.
561
      #
A
AvnerCohen 已提交
562
      #  t.change(:name, :string, limit: 80)
563
      #  t.change(:description, :text)
564 565
      #
      # See TableDefinition#column for details of the options you can use.
566
      def change(column_name, type, options = {})
567
        @base.change_column(name, column_name, type, options)
568 569
      end

570
      # Sets a new default value for a column.
571
      #
572 573
      #  t.change_default(:qualification, 'new')
      #  t.change_default(:authorized, 1)
574 575
      #
      # See SchemaStatements#change_column_default
576
      def change_default(column_name, default)
577
        @base.change_column_default(name, column_name, default)
578 579 580
      end

      # Removes the column(s) from the table definition.
581
      #
582 583
      #  t.remove(:qualification)
      #  t.remove(:qualification, :experience)
584 585
      #
      # See SchemaStatements#remove_columns
586
      def remove(*column_names)
587
        @base.remove_columns(name, *column_names)
588 589
      end

590
      # Removes the given index from the table.
591
      #
592 593 594 595 596
      #   t.remove_index(:branch_id)
      #   t.remove_index(column: [:branch_id, :party_id])
      #   t.remove_index(name: :by_branch_party)
      #
      # See SchemaStatements#remove_index
597
      def remove_index(options = {})
598
        @base.remove_index(name, options)
599 600
      end

601
      # Removes the timestamp columns (+created_at+ and +updated_at+) from the table.
602
      #
603
      #  t.remove_timestamps
604 605
      #
      # See SchemaStatements#remove_timestamps
606 607
      def remove_timestamps(options = {})
        @base.remove_timestamps(name, options)
608 609 610
      end

      # Renames a column.
611
      #
612
      #  t.rename(:description, :name)
613 614
      #
      # See SchemaStatements#rename_column
615
      def rename(column_name, new_column_name)
616
        @base.rename_column(name, column_name, new_column_name)
617 618
      end

619
      # Adds a reference. Optionally adds a +type+ column, if
620
      # <tt>:polymorphic</tt> option is provided.
621
      #
622
      #  t.references(:user)
623
      #  t.references(:user, type: "string")
624
      #  t.belongs_to(:supplier, polymorphic: true)
625
      #  t.belongs_to(:supplier, foreign_key: true)
626
      #
627
      # See SchemaStatements#add_reference
628 629
      def references(*args)
        options = args.extract_options!
630
        args.each do |ref_name|
631
          @base.add_reference(name, ref_name, options)
632 633 634 635
        end
      end
      alias :belongs_to :references

636
      # Removes a reference. Optionally removes a +type+ column.
637
      #
638 639 640
      #  t.remove_references(:user)
      #  t.remove_belongs_to(:supplier, polymorphic: true)
      #
641
      # See SchemaStatements#remove_reference
642 643
      def remove_references(*args)
        options = args.extract_options!
644
        args.each do |ref_name|
645
          @base.remove_reference(name, ref_name, options)
646 647
        end
      end
648
      alias :remove_belongs_to :remove_references
649

650
      # Adds a column or columns of a specified type.
651
      #
652 653
      #  t.string(:goat)
      #  t.string(:goat, :sheep)
654 655
      #
      # See SchemaStatements#add_column
656 657 658
      [:string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean].each do |column_type|
        define_method column_type do |*args|
          options = args.extract_options!
659 660
          args.each do |column_name|
            @base.add_column(name, column_name, column_type, options)
661
          end
662
        end
663 664
      end

665 666 667 668
      def foreign_key(*args) # :nodoc:
        @base.add_foreign_key(name, *args)
      end

A
Anton 已提交
669 670 671
      def foreign_key_exists?(*args) # :nodoc:
        @base.foreign_key_exists?(name, *args)
      end
672

673 674 675 676 677
      private
        def native
          @base.native_database_types
        end
    end
678
  end
679
end