Module: ActiveRecord::ConnectionAdapters::Rdb::SchemaStatements

Included in:
ActiveRecord::ConnectionAdapters::RdbAdapter
Defined in:
lib/active_record/connection_adapters/rdb/schema_statements.rb

Overview

:nodoc:

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.after(*names) ⇒ Object



413
414
415
416
417
418
419
420
421
422
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 413

def after(*names)
  names.flatten.each do |name|
    m = ActiveRecord::ConnectionAdapters::Rdb::SchemaStatements.instance_method(name)
    define_method(name) do |*args, &block|
      m.bind(self).call(*args, &block)
      yield
      commit_db_transaction
    end
  end
end

Instance Method Details

#add_column(table_name, column_name, type, options = {}) ⇒ Object



133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 133

def add_column(table_name, column_name, type, options = {})
  super

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

  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 = {}) ⇒ Object



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 165

def change_column(table_name, column_name, type, options = {})
  type_sql = type_to_sql(type, *options.values_at(:limit, :precision, :scale))

  if %i[text string].include?(type)
    copy_column = 'c_temp'
    add_column table_name, copy_column, type, options
    execute(squish_sql(<<-END_SQL))
    UPDATE #{table_name} SET #{quote_column_name(copy_column)} = #{quote_column_name(column_name)};
    END_SQL
    remove_column table_name, column_name
    rename_column table_name, copy_column, column_name
  else
    execute(squish_sql(<<-END_SQL))
    ALTER TABLE #{quote_table_name(table_name)}
    ALTER COLUMN #{quote_column_name(column_name)} TYPE #{type_sql}
    END_SQL
  end
  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) ⇒ Object



186
187
188
189
190
191
192
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 186

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) ⇒ Object



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 194

def change_column_null(table_name, column_name, null, default = nil)
  change_column_default(table_name, column_name, default) if default

  db_column = columns(table_name).find { |c| c.name == column_name.to_s }
  options = { null: null }
  options[:default] = db_column.default if !default && db_column.default
  options[:default] = default if default
  ar_type = db_column.type
  type = type_to_sql(ar_type.type, ar_type.limit, ar_type.precision, ar_type.scale)

  copy_column = 'c_temp'
  add_column table_name, copy_column, type, options
  execute(squish_sql(<<-END_SQL))
    UPDATE #{table_name} SET #{quote_column_name(copy_column)} = #{quote_column_name(column_name)};
  END_SQL
  remove_column table_name, column_name
  rename_column table_name, copy_column, column_name
end

#columns(table_name, _name = nil) ⇒ Object



38
39
40
41
42
43
44
45
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 38

def columns(table_name, _name = nil)
  @col_definitions ||= {}
  @col_definitions[table_name] = column_definitions(table_name).map do |field|
     = column_type_for(field)
    rdb_opt = { domain: field[:domain], sub_type: field[:sql_subtype] }
    RdbColumn.new(field[:name], field[:default], , field[:nullable], table_name, rdb_opt)
  end
end

#create_schema_dumper(options) ⇒ Object



331
332
333
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 331

def create_schema_dumper(options)
  Rdb::SchemaDumper.create(self, options)
end

#create_sequence(sequence_name) ⇒ Object



102
103
104
105
106
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 102

def create_sequence(sequence_name)
  execute("CREATE SEQUENCE #{sequence_name}")
rescue StandardError
  nil
end

#create_table(name, options = {}) ⇒ Object

:nodoc:

Raises:

  • (ActiveRecordError)


47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 47

def create_table(name, options = {}) # :nodoc:
  raise ActiveRecordError, 'Firebird does not support temporary tables' if options.key? :temporary

  raise ActiveRecordError, 'Firebird does not support creating tables with a select' if options.key? :as

  drop_table name, if_exists: true if options.key? :force

  needs_sequence = options[:id]

  super name, options do |table_def|
    yield table_def if block_given?
    needs_sequence ||= table_def.needs_sequence
  end

  return if options[:sequence] == false || !needs_sequence

  create_sequence(options[:sequence] || default_sequence_name(name))
  trg_sql = <<-END_SQL
    CREATE TRIGGER N$#{name.upcase} FOR #{name.upcase}
    ACTIVE BEFORE INSERT
    AS
    declare variable gen_val bigint;
    BEGIN
      if (new.ID is null) then
        new.ID = next value for #{options[:sequence] || default_sequence_name(name)};
      else begin
        gen_val = gen_id(#{options[:sequence] || default_sequence_name(name)}, 1);
        if (new.ID > gen_val) then
          gen_val = gen_id(#{options[:sequence] || default_sequence_name(name)}, new.ID - gen_val);
      end
    END
  END_SQL
  execute(trg_sql)
end

#drop_sequence(sequence_name) ⇒ Object



108
109
110
111
112
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 108

def drop_sequence(sequence_name)
  execute("DROP SEQUENCE #{sequence_name}")
rescue StandardError
  nil
end

#drop_table(name, options = {}) ⇒ Object

:nodoc:



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 82

def drop_table(name, options = {}) # :nodoc:
  drop_sql = "DROP TABLE #{quote_table_name(name)}"
  drop = if options[:if_exists]
           !execute(squish_sql(<<-END_SQL))
  select 1 from rdb$relations where rdb$relation_name = #{quote_table_name(name).tr('"', '\'')}
           END_SQL
                .empty?
         else
           false
         end

  trigger_name = "N$#{name.upcase}"
  drop_trigger(trigger_name) if trigger_exists?(trigger_name)

  sequence_name = options[:sequence] || default_sequence_name(name)
  drop_sequence(sequence_name) if sequence_exists?(sequence_name)

  execute(drop_sql) if drop
end

#drop_trigger(trigger_name) ⇒ Object



114
115
116
117
118
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 114

def drop_trigger(trigger_name)
  execute("DROP TRIGGER #{trigger_name}")
rescue StandardError
  nil
end

#index_exists?(table_name, column_name, options = {}) ⇒ Boolean

Returns:

  • (Boolean)


251
252
253
254
255
256
257
258
259
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 251

def index_exists?(table_name, column_name, options = {})
  column_names = Array(column_name).map(&:to_s)
  checks = []
  checks << lambda { |i| i.columns == column_names }
  checks << lambda(&:unique) if options[:unique]
  checks << lambda { |i| i.name.upcase == options[:name].to_s.upcase } if options[:name]

  indexes(table_name).any? { |i| checks.all? { |check| check[i] } }
end

#index_name(table_name, options) ⇒ Object

:nodoc:



232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 232

def index_name(table_name, options) #:nodoc:
  if options.respond_to?(:keys) # legacy support
    if options[:column]
      index_name = "#{table_name}_#{Array.wrap(options[:column]) * '_'}"
      if index_name.length > 31
        "IDX_#{Digest::SHA256.hexdigest(index_name)[0..22]}"
      else
        index_name
      end
    elsif options[:name]
      options[:name]
    else
      raise ArgumentError 'You must specify the index name'
    end
  else
    index_name(table_name, column: options)
  end
end

#index_name_exists?(table_name, index_name) ⇒ Boolean

Returns:

  • (Boolean)


33
34
35
36
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 33

def index_name_exists?(table_name, index_name)
  index_name = index_name.to_s.upcase
  indexes(table_name).detect { |i| i.name.upcase == index_name }
end

#indexes(table_name, _name = nil) ⇒ Object



27
28
29
30
31
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 27

def indexes(table_name, _name = nil)
  @connection.indexes.values.map do |ix|
    IndexDefinition.new(table_name, ix.index_name, ix.unique, ix.columns) if ix.table_name == table_name.to_s && ix.index_name !~ /^rdb\$/
  end.compact
end

#native_database_typesObject



327
328
329
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 327

def native_database_types
  @native_database_types ||= initialize_native_database_types.freeze
end

#primary_key(table_name) ⇒ Object



314
315
316
317
318
319
320
321
322
323
324
325
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 314

def primary_key(table_name)
  row = @connection.query(<<-END_SQL)
    SELECT s.rdb$field_name
    FROM rdb$indices i
    JOIN rdb$index_segments s ON i.rdb$index_name = s.rdb$index_name
    LEFT JOIN rdb$relation_constraints c ON i.rdb$index_name = c.rdb$index_name
    WHERE i.rdb$relation_name = '#{ar_to_rdb_case(table_name)}'
    AND c.rdb$constraint_type = 'PRIMARY KEY';
  END_SQL

  row.first && rdb_to_ar_case(row.first[0].rstrip)
end

#remove_column(table_name, column_name, type = nil, options = {}) ⇒ Object



148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 148

def remove_column(table_name, column_name, type = nil, options = {})
  indexes(table_name).each do |i|
    remove_index! i.table, i.name if i.columns.any? { |c| c == column_name.to_s }
  end

  column_exist = !execute(squish_sql(<<-END_SQL))
  select 1 from RDB$RELATION_FIELDS rf
    where lower(rf.RDB$RELATION_NAME) = '#{table_name.downcase}' and lower(rf.RDB$FIELD_NAME) = '#{column_name.downcase}'
  END_SQL
                 .empty?
  super if column_exist
end

#remove_column_for_alter(_table_name, column_name, _type = nil, _options = {}) ⇒ Object



161
162
163
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 161

def remove_column_for_alter(_table_name, column_name, _type = nil, _options = {})
  "DROP #{quote_column_name(column_name)}"
end

#remove_index(table_name, options = {}) ⇒ Object



227
228
229
230
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 227

def remove_index(table_name, options = {})
  index_name = index_name(table_name, options)
  execute "DROP INDEX #{quote_column_name(index_name)}"
end

#remove_index!(_table_name, index_name) ⇒ Object



223
224
225
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 223

def remove_index!(_table_name, index_name)
  execute "DROP INDEX #{quote_column_name(index_name)}"
end

#rename_column(table_name, column_name, new_column_name) ⇒ Object



213
214
215
216
217
218
219
220
221
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 213

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

#sequence_exists?(sequence_name) ⇒ Boolean

Returns:

  • (Boolean)


120
121
122
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 120

def sequence_exists?(sequence_name)
  @connection.generator_names.include?(sequence_name)
end

#tables(_name = nil) ⇒ Object



19
20
21
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 19

def tables(_name = nil)
  @connection.table_names
end

#trigger_exists?(trigger_name) ⇒ Boolean

Returns:

  • (Boolean)


124
125
126
127
128
129
130
131
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 124

def trigger_exists?(trigger_name)
  !execute(squish_sql(<<-END_SQL))
    select 1
    from rdb$triggers
     where rdb$trigger_name = '#{trigger_name}'
  END_SQL
    .empty?
end

#type_to_sql(type, limit = nil, precision = nil, scale = nil, **args) ⇒ Object



261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 261

def type_to_sql(type, limit = nil, precision = nil, scale = nil, **args)
  if !args.nil? && !args.empty?
    limit = args[:limit] if limit.nil?
    precision = args[:precision] if precision.nil?
    scale = args[:scale] if scale.nil?
  end
  case type
  when :integer
    integer_to_sql(limit)
  when :float
    float_to_sql(limit)
  when :text
    text_to_sql(limit)
  # when :blob
  #   binary_to_sql(limit)
  when :string
    string_to_sql(limit)
  else
    type = type.to_sym if type
    native = native_database_types[type]
    if native
      column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup

      if type == :decimal # ignore limit, use precision and scale
        scale ||= native[:scale]

        if precision ||= native[:precision]
          column_type_sql << if scale
                               "(#{precision},#{scale})"
                             else
                               "(#{precision})"
                             end
        elsif scale
          raise ArgumentError, 'Error adding decimal column: precision cannot be empty if scale is specified'
        end

      elsif %i[datetime timestamp time interval].include?(type) && precision ||= native[:precision]
        if (0..6) === precision
          column_type_sql << "(#{precision})"
        else
          raise(ActiveRecordError, "No #{native[:name]} type has precision of #{precision}. The allowed range of precision is from 0 to 6")
        end
      elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
        column_type_sql << "(#{limit})"
      end

      column_type_sql
    else
      type.to_s
    end
  end
end

#viewsObject



23
24
25
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 23

def views
  @connection.view_names
end