module ActiveRecord::ConnectionAdapters::Fb::SchemaStatements

Public Instance Methods

add_column(table_name, column_name, type, options = {}) click to toggle source

Adds a new column to the named table. See TableDefinition#column for details of the options you can use.

Calls superclass method
# File lib/active_record/connection_adapters/fb/schema_statements.rb, line 113
        def add_column(table_name, column_name, type, options = {})
          while_ensuring_boolean_domain { super }

          if type == :primary_key && options[:sequence] != false
            create_sequence(options[:sequence] || default_sequence_name(table_name))
          end

          return unless options[:position]
          # position is 1-based but add 1 to skip id column
          execute(squish_sql(<<-end_sql))
            ALTER TABLE #{quote_table_name(table_name)}
            ALTER COLUMN #{quote_column_name(column_name)}
            POSITION #{options[:position] + 1}
          end_sql
        end
change_column(table_name, column_name, type, options = {}) click to toggle source

Changes the column’s definition according to the new options. See TableDefinition#column for details of the options you can use.

Examples
change_column(:suppliers, :name, :string, :limit => 80)
change_column(:accounts, :description, :text)
# File lib/active_record/connection_adapters/fb/schema_statements.rb, line 144
        def change_column(table_name, column_name, type, options = {})
          type_sql = type_to_sql(type, *options.values_at(:limit, :precision, :scale))

          execute(squish_sql(<<-end_sql))
            ALTER TABLE #{quote_table_name(table_name)}
            ALTER COLUMN #{quote_column_name(column_name)} TYPE #{type_sql}
          end_sql

          change_column_null(table_name, column_name, !!options[:null]) if options.key?(:null)
          change_column_default(table_name, column_name, options[:default]) if options.key?(:default)
        end
change_column_default(table_name, column_name, default) click to toggle source

Sets a new default value for a column. If you want to set the default value to NULL, you are out of luck. You need to DatabaseStatements#execute the appropriate SQL statement yourself.

Examples
change_column_default(:suppliers, :qualification, 'new')
change_column_default(:accounts, :authorized, 1)
# File lib/active_record/connection_adapters/fb/schema_statements.rb, line 162
        def change_column_default(table_name, column_name, default)
          execute(squish_sql(<<-end_sql))
            ALTER TABLE #{quote_table_name(table_name)}
            ALTER #{quote_column_name(column_name)}
            SET DEFAULT #{quote(default)}
          end_sql
        end
change_column_null(table_name, column_name, null, default = nil) click to toggle source
# File lib/active_record/connection_adapters/fb/schema_statements.rb, line 170
        def change_column_null(table_name, column_name, null, default = nil)
          change_column_default(table_name, column_name, default) if default

          execute(squish_sql(<<-end_sql))
            UPDATE RDB$RELATION_FIELDS
            SET RDB$NULL_FLAG=#{quote(null ? nil : 1)}
            WHERE RDB$FIELD_NAME='#{ar_to_fb_case(column_name)}'
            AND RDB$RELATION_NAME='#{ar_to_fb_case(table_name)}'
          end_sql
        end
columns(table_name, _name = nil) click to toggle source

Returns an array of Column objects for the table specified by table_name. See the concrete implementation for details on the expected parameter values.

# File lib/active_record/connection_adapters/fb/schema_statements.rb, line 53
def columns(table_name, _name = nil)
  column_definitions(table_name).map do |field|
    field.symbolize_keys!.each { |k, v| v.rstrip! if v.is_a?(String) }
    properties = field.values_at(:name, :default_source)
    properties += column_type_for(field)
    properties << !field[:null_flag]
    FbColumn.new(*properties, field.slice(:domain, :sub_type))
  end
end
create_sequence(sequence_name) click to toggle source

Creates a sequence

Examples
create_sequence('DOGS_SEQ')
# File lib/active_record/connection_adapters/fb/schema_statements.rb, line 100
def create_sequence(sequence_name)
  execute("CREATE SEQUENCE #{sequence_name}") rescue nil
end
drop_sequence(sequence_name) click to toggle source

Drops a sequence

Examples
drop_sequence('DOGS_SEQ')
# File lib/active_record/connection_adapters/fb/schema_statements.rb, line 107
def drop_sequence(sequence_name)
  execute("DROP SEQUENCE #{sequence_name}") rescue nil
end
indexes(table_name, _name = nil) click to toggle source

Returns an array of indexes for the given table.

# File lib/active_record/connection_adapters/fb/schema_statements.rb, line 30
def indexes(table_name, _name = nil)
  @connection.indexes.values.map { |ix|
    if ix.table_name == table_name.to_s && ix.index_name !~ /^rdb\$/
      IndexDefinition.new(table_name, ix.index_name, ix.unique, ix.columns)
    end
  }.compact
end
native_database_types() click to toggle source

Returns a Hash of mappings from the abstract data types to the native database types. See TableDefinition#column for details on the recognized abstract data types.

# File lib/active_record/connection_adapters/fb/schema_statements.rb, line 8
def native_database_types
  {
    :primary_key => 'integer not null primary key',
    :string      => { :name => 'varchar', :limit => 255 },
    :text        => { :name => 'blob sub_type text' },
    :integer     => { :name => 'integer' },
    :float       => { :name => 'float' },
    :decimal     => { :name => 'decimal' },
    :datetime    => { :name => 'timestamp' },
    :timestamp   => { :name => 'timestamp' },
    :time        => { :name => 'time' },
    :date        => { :name => 'date' },
    :binary      => { :name => 'blob' },
    :boolean     => { :name => boolean_domain[:name] }
  }
end
remove_column(table_name, column_name, type = nil, options = {}) click to toggle source
Calls superclass method
# File lib/active_record/connection_adapters/fb/schema_statements.rb, line 129
def remove_column(table_name, column_name, type = nil, options = {})
  indexes(table_name).each do |i|
    if i.columns.any? { |c| c == column_name.to_s }
      remove_index! i.table, i.name
    end
  end

  super
end
rename_column(table_name, column_name, new_column_name) click to toggle source

Renames a column.

Example
rename_column(:suppliers, :description, :name)
# File lib/active_record/connection_adapters/fb/schema_statements.rb, line 184
        def rename_column(table_name, column_name, new_column_name)
          execute(squish_sql(<<-end_sql))
            ALTER TABLE #{quote_table_name(table_name)}
            ALTER #{quote_column_name(column_name)}
            TO #{quote_column_name(new_column_name)}
          end_sql

          rename_column_indexes(table_name, column_name, new_column_name)
        end
rename_table(name, new_name) click to toggle source
# File lib/active_record/connection_adapters/fb/schema_statements.rb, line 84
def rename_table(name, new_name)
  fail ActiveRecordError, 'Firebird does not support renaming tables.'
end
tables(_name = nil) click to toggle source
# File lib/active_record/connection_adapters/fb/schema_statements.rb, line 25
def tables(_name = nil)
  @connection.table_names
end
type_to_sql(type, limit = nil, precision = nil, scale = nil) click to toggle source
Calls superclass method
# File lib/active_record/connection_adapters/fb/schema_statements.rb, line 212
def type_to_sql(type, limit = nil, precision = nil, scale = nil)
  case type
  when :integer then integer_to_sql(limit)
  when :float   then float_to_sql(limit)
  else super
  end
end

Private Instance Methods

column_definitions(table_name) click to toggle source
# File lib/active_record/connection_adapters/fb/schema_statements.rb, line 222
        def column_definitions(table_name)
          exec_query(squish_sql(<<-end_sql), 'SCHEMA')
            SELECT
              r.rdb$field_name name,
              r.rdb$field_source "domain",
              f.rdb$field_type type,
              f.rdb$field_sub_type "sub_type",
              f.rdb$field_length "limit",
              f.rdb$field_precision "precision",
              f.rdb$field_scale "scale",
              COALESCE(r.rdb$default_source, f.rdb$default_source) default_source,
              COALESCE(r.rdb$null_flag, f.rdb$null_flag) null_flag
            FROM rdb$relation_fields r
            JOIN rdb$fields f ON r.rdb$field_source = f.rdb$field_name
            WHERE r.rdb$relation_name = '#{ar_to_fb_case(table_name)}'
            ORDER BY r.rdb$field_position
          end_sql
        end
column_type_for(field) click to toggle source

We need to be very precise about our sql types.

# File lib/active_record/connection_adapters/fb/schema_statements.rb, line 242
def column_type_for(field)
  sql_type = FbColumn.sql_type_for(field)

  if ActiveRecord::VERSION::STRING < "4.2.0"
    [sql_type]
  else
    [lookup_cast_type(sql_type), sql_type]
  end
end
create_boolean_domain() click to toggle source
# File lib/active_record/connection_adapters/fb/schema_statements.rb, line 292
        def create_boolean_domain
          execute(squish_sql(<<-end_sql))
            CREATE DOMAIN #{boolean_domain[:name]} AS #{boolean_domain[:type]}
            CHECK (VALUE IN (#{quoted_true}, #{quoted_false}) OR VALUE IS NULL)
          end_sql
        end
create_table_definition(*args) click to toggle source
# File lib/active_record/connection_adapters/fb/schema_statements.rb, line 253
def create_table_definition(*args)
  TableDefinition.new(native_database_types, *args)
end
float_to_sql(limit) click to toggle source
# File lib/active_record/connection_adapters/fb/schema_statements.rb, line 275
def float_to_sql(limit)
  (limit.nil? || limit <= 4) ? 'float' : 'double precision'
end
integer_to_sql(limit) click to toggle source

Map logical Rails types to Firebird-specific data types.

# File lib/active_record/connection_adapters/fb/schema_statements.rb, line 263
def integer_to_sql(limit)
  return 'integer' if limit.nil?
  case limit
  when 1..2 then 'smallint'
  when 3..4 then 'integer'
  when 5..8 then 'bigint'
  else
    fail ActiveRecordError, "No integer type has byte size #{limit}. "\
                            "Use a NUMERIC with PRECISION 0 instead."
  end
end
sequence_exists?(sequence_name) click to toggle source
# File lib/active_record/connection_adapters/fb/schema_statements.rb, line 299
def sequence_exists?(sequence_name)
  @connection.generator_names.include?(sequence_name)
end
squish_sql(sql) click to toggle source
# File lib/active_record/connection_adapters/fb/schema_statements.rb, line 288
def squish_sql(sql)
  sql.strip.gsub(/\s+/, ' ')
end
table_definition() click to toggle source
# File lib/active_record/connection_adapters/fb/schema_statements.rb, line 257
def table_definition
  TableDefinition.new(self)
end
while_ensuring_boolean_domain(&block) click to toggle source

Creates a domain for boolean fields as needed

# File lib/active_record/connection_adapters/fb/schema_statements.rb, line 280
def while_ensuring_boolean_domain(&block)
  block.call
rescue ActiveRecordError => e
  raise unless e.message =~ /Specified domain or source column \w+ does not exist/
  create_boolean_domain
  block.call
end