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



410
411
412
413
414
415
416
417
418
419
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 410

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



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

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("    ALTER TABLE \#{quote_table_name(table_name)}\n    ALTER COLUMN \#{quote_column_name(column_name)}\n    POSITION \#{options[:position] + 1}\n  end_sql\nend\n"))

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



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

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("    UPDATE \#{table_name} SET \#{quote_column_name(copy_column)} = \#{quote_column_name(column_name)};\n    END_SQL\n    remove_column table_name, column_name\n    rename_column table_name, copy_column, column_name\n  else\n    execute(squish_sql(<<-END_SQL))\n    ALTER TABLE \#{quote_table_name(table_name)}\n    ALTER COLUMN \#{quote_column_name(column_name)} TYPE \#{type_sql}\n    END_SQL\n  end\n  change_column_null(table_name, column_name, !!options[:null]) if options.key?(:null)\n  change_column_default(table_name, column_name, options[:default]) if options.key?(:default)\nend\n"))

#change_column_default(table_name, column_name, default) ⇒ Object



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

def change_column_default(table_name, column_name, default)
  execute(squish_sql("    ALTER TABLE \#{quote_table_name(table_name)}\n    ALTER \#{quote_column_name(column_name)}\n    SET DEFAULT \#{quote(default)}\n  END_SQL\nend\n"))

#change_column_null(table_name, column_name, null, default = nil) ⇒ Object



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

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("    UPDATE \#{table_name} SET \#{quote_column_name(copy_column)} = \#{quote_column_name(column_name)};\n  END_SQL\n  remove_column table_name, column_name\n  rename_column table_name, copy_column, column_name\nend\n"))

#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



329
330
331
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 329

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

#create_sequence(sequence_name) ⇒ Object



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

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

#drop_sequence(sequence_name) ⇒ Object



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

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

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

:nodoc:



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

def drop_table(name, options = {}) # :nodoc:

  drop_sql = "DROP TABLE #{quote_table_name(name)}"
  drop = if options[:if_exists]
           !execute(squish_sql("  select 1 from rdb$relations where rdb$relation_name = \#{quote_table_name(name).tr('\"', '\\'')}\n           END_SQL\n                .empty?\n         else\n           false\n         end\n\n  trigger_name = \"N$\#{name.upcase}\"\n  drop_trigger(trigger_name) if trigger_exists?(trigger_name)\n\n  sequence_name = options[:sequence] || default_sequence_name(name)\n  drop_sequence(sequence_name) if sequence_exists?(sequence_name)\n\n  execute(drop_sql) if drop\nend\n"))

#drop_trigger(trigger_name) ⇒ Object



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

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

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

Returns:

  • (Boolean)


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

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:



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

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



325
326
327
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 325

def native_database_types
  @native_database_types ||= initialize_native_database_types.freeze
end

#primary_key(table_name) ⇒ Object



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

def primary_key(table_name)
  row = @connection.query("    SELECT s.rdb$field_name\n    FROM rdb$indices i\n    JOIN rdb$index_segments s ON i.rdb$index_name = s.rdb$index_name\n    LEFT JOIN rdb$relation_constraints c ON i.rdb$index_name = c.rdb$index_name\n    WHERE i.rdb$relation_name = '\#{ar_to_rdb_case(table_name)}'\n    AND c.rdb$constraint_type = 'PRIMARY KEY';\n  END_SQL\n\n  row.first && rdb_to_ar_case(row.first[0].rstrip)\nend\n")

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



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

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("  select 1 from RDB$RELATION_FIELDS rf\n    where lower(rf.RDB$RELATION_NAME) = '\#{table_name.downcase}' and lower(rf.RDB$FIELD_NAME) = '\#{column_name.downcase}'\n  END_SQL\n                 .empty?\n  super if column_exist\nend\n"))

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



159
160
161
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 159

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



225
226
227
228
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 225

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



221
222
223
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 221

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



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

def rename_column(table_name, column_name, new_column_name)
  execute(squish_sql("    ALTER TABLE \#{quote_table_name(table_name)}\n    ALTER \#{quote_column_name(column_name)}\n    TO \#{quote_column_name(new_column_name)}\n  END_SQL\n\n  rename_column_indexes(table_name, column_name, new_column_name)\nend\n"))

#sequence_exists?(sequence_name) ⇒ Boolean

Returns:

  • (Boolean)


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

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)


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

def trigger_exists?(trigger_name)
  !execute(squish_sql("    select 1\n    from rdb$triggers\n     where rdb$trigger_name = '\#{trigger_name}'\n  END_SQL\n    .empty?\nend\n"))

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



259
260
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
# File 'lib/active_record/connection_adapters/rdb/schema_statements.rb', line 259

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