module ActiveRecord::ConnectionAdapters::Fb::SchemaStatements
Public Instance Methods
Adds a new column to the named table. See TableDefinition#column for details of the options you can use.
# 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
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
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
# 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
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
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
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
# 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
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
# 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
# File lib/active_record/connection_adapters/fb/schema_statements.rb, line 25 def tables(_name = nil) @connection.table_names end
# 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
# 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
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
# 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
# File lib/active_record/connection_adapters/fb/schema_statements.rb, line 253 def create_table_definition(*args) TableDefinition.new(native_database_types, *args) end
# 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
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
# File lib/active_record/connection_adapters/fb/schema_statements.rb, line 299 def sequence_exists?(sequence_name) @connection.generator_names.include?(sequence_name) end
# File lib/active_record/connection_adapters/fb/schema_statements.rb, line 288 def squish_sql(sql) sql.strip.gsub(/\s+/, ' ') end
# File lib/active_record/connection_adapters/fb/schema_statements.rb, line 257 def table_definition TableDefinition.new(self) end
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