Module: ActiveRecord::ConnectionAdapters::OracleEnhancedSchemaStatements

Defined in:
lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb

Instance Method Summary collapse

Instance Method Details

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

:nodoc:



241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb', line 241

def add_column(table_name, column_name, type, options = {}) #:nodoc:
  if type.to_sym == :virtual
    type = options[:type]
  end
  add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} "
  add_column_sql << type_to_sql(type, options[:limit], options[:precision], options[:scale]) if type

  add_column_options!(add_column_sql, options.merge(:type=>type, :column_name=>column_name, :table_name=>table_name))

  add_column_sql << tablespace_for((type_to_sql(type).downcase.to_sym), nil, table_name, column_name) if type

  execute(add_column_sql)
ensure
  clear_table_columns_cache(table_name)
end

#add_comment(table_name, column_name, comment) ⇒ Object

:nodoc:



321
322
323
324
# File 'lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb', line 321

def add_comment(table_name, column_name, comment) #:nodoc:
  return if comment.blank?
  execute "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{column_name} IS '#{comment}'"
end

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

clear cached indexes when adding new index



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb', line 149

def add_index(table_name, column_name, options = {}) #:nodoc:
  column_names = Array(column_name)
  index_name   = index_name(table_name, :column => column_names)

  if Hash === options # legacy support, since this param was a string
    index_type = options[:unique] ? "UNIQUE" : ""
    index_name = options[:name].to_s if options.key?(:name)
    tablespace = tablespace_for(:index, options[:tablespace])
  else
    index_type = options
  end

  if index_name.to_s.length > index_name_length
    raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters"
  end
  if index_name_exists?(table_name, index_name, false)
    raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
  end
  quoted_column_names = column_names.map { |e| quote_column_name_or_expression(e) }.join(", ")

  execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})#{tablespace} #{options[:options]}"
ensure
  self.all_schema_indexes = nil
end

#add_table_comment(table_name, comment) ⇒ Object

:nodoc:



326
327
328
329
# File 'lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb', line 326

def add_table_comment(table_name, comment) #:nodoc:
  return if comment.blank?
  execute "COMMENT ON TABLE #{quote_table_name(table_name)} IS '#{comment}'"
end

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

:nodoc:



273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
# File 'lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb', line 273

def change_column(table_name, column_name, type, options = {}) #:nodoc:
  column = column_for(table_name, column_name)

  # remove :null option if its value is the same as current column definition
  # otherwise Oracle will raise error
  if options.has_key?(:null) && options[:null] == column.null
    options[:null] = nil
  end
  if type.to_sym == :virtual
    type = options[:type]
  end
  change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} MODIFY #{quote_column_name(column_name)} "
  change_column_sql << "#{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" if type

  add_column_options!(change_column_sql, options.merge(:type=>type, :column_name=>column_name, :table_name=>table_name))

  change_column_sql << tablespace_for((type_to_sql(type).downcase.to_sym), nil, options[:table_name], options[:column_name]) if type

  execute(change_column_sql)
ensure
  clear_table_columns_cache(table_name)
end

#change_column_default(table_name, column_name, default) ⇒ Object

:nodoc:



257
258
259
260
261
# File 'lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb', line 257

def change_column_default(table_name, column_name, default) #:nodoc:
  execute "ALTER TABLE #{quote_table_name(table_name)} MODIFY #{quote_column_name(column_name)} DEFAULT #{quote(default)}"
ensure
  clear_table_columns_cache(table_name)
end

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

:nodoc:



263
264
265
266
267
268
269
270
271
# File 'lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb', line 263

def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
  column = column_for(table_name, column_name)

  unless null || default.nil?
    execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
  end

  change_column table_name, column_name, column.sql_type, :null => null
end

#column_comment(table_name, column_name) ⇒ Object

:nodoc:



340
341
342
343
344
345
346
347
348
# File 'lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb', line 340

def column_comment(table_name, column_name) #:nodoc:
  (owner, table_name, db_link) = @connection.describe(table_name)
  select_value <<-SQL
    SELECT comments FROM all_col_comments#{db_link}
    WHERE owner = '#{owner}'
      AND table_name = '#{table_name}'
      AND column_name = '#{column_name.upcase}'
  SQL
end

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

Additional options for create_table method in migration files.

You can specify individual starting value in table creation migration file, e.g.:

create_table :users, :sequence_start_value => 100 do |t|
  # ...
end

You can also specify other sequence definition additional parameters, e.g.:

create_table :users, :sequence_start_value => “100 NOCACHE INCREMENT BY 10” do |t|
  # ...
end

Create primary key trigger (so that you can skip primary key value in INSERT statement). By default trigger name will be “table_name_pkt”, you can override the name with :trigger_name option (but it is not recommended to override it as then this trigger will not be detected by ActiveRecord model and it will still do prefetching of sequence value). Example:

create_table :users, :primary_key_trigger => true do |t|
  # ...
end

It is possible to add table and column comments in table creation migration files:

create_table :employees, :comment => “Employees and contractors” do |t|
  t.string      :first_name, :comment => “Given name”
  t.string      :last_name, :comment => “Surname”
end


41
42
43
44
45
46
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb', line 41

def create_table(name, options = {}, &block)
  create_sequence = options[:id] != false
  column_comments = {}

  table_definition = TableDefinition.new(self)
  table_definition.primary_key(options[:primary_key] || Base.get_primary_key(name.to_s.singularize)) unless options[:id] == false

  # store that primary key was defined in create_table block
  unless create_sequence
    class << table_definition
      attr_accessor :create_sequence
      def primary_key(*args)
        self.create_sequence = true
        super(*args)
      end
    end
  end

  # store column comments
  class << table_definition
    attr_accessor :column_comments
    def column(name, type, options = {})
      if options[:comment]
        self.column_comments ||= {}
        self.column_comments[name] = options[:comment]
      end
      super(name, type, options)
    end
  end

  result = block.call(table_definition) if block
  create_sequence = create_sequence || table_definition.create_sequence
  column_comments = table_definition.column_comments if table_definition.column_comments
  tablespace = tablespace_for(:table, options[:tablespace])

  if options[:force] && table_exists?(name)
    drop_table(name, options)
  end

  create_sql = "CREATE#{' GLOBAL TEMPORARY' if options[:temporary]} TABLE "
  create_sql << quote_table_name(name)
  create_sql << " (#{table_definition.to_sql})"
  unless options[:temporary]
    create_sql << " ORGANIZATION #{options[:organization]}" if options[:organization]
    create_sql << tablespace
    table_definition.lob_columns.each{|cd| create_sql << tablespace_for(cd.sql_type.downcase.to_sym, nil, name, cd.name)}
  end
  create_sql << " #{options[:options]}"
  execute create_sql

  create_sequence_and_trigger(name, options) if create_sequence

  add_table_comment name, options[:comment]
  column_comments.each do |column_name, comment|
    add_comment name, column_name, comment
  end

end

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

:nodoc:



111
112
113
114
115
116
117
# File 'lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb', line 111

def drop_table(name, options = {}) #:nodoc:
  super(name)
  seq_name = options[:sequence_name] || default_sequence_name(name)
  execute "DROP SEQUENCE #{quote_table_name(seq_name)}" rescue nil
ensure
  clear_table_columns_cache(name)
end

#index_name(table_name, options) ⇒ Object

returned shortened index name if default is too large



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

def index_name(table_name, options) #:nodoc:
  default_name = super(table_name, options).to_s
  # sometimes options can be String or Array with column names
  options = {} unless options.is_a?(Hash)
  identifier_max_length = options[:identifier_max_length] || index_name_length
  return default_name if default_name.length <= identifier_max_length

  # remove 'index', 'on' and 'and' keywords
  shortened_name = "i_#{table_name}_#{Array(options[:column]) * '_'}"

  # leave just first three letters from each word
  if shortened_name.length > identifier_max_length
    shortened_name = shortened_name.split('_').map{|w| w[0,3]}.join('_')
  end
  # generate unique name using hash function
  if shortened_name.length > identifier_max_length
    shortened_name = 'i'+Digest::SHA1.hexdigest(default_name)[0,identifier_max_length-1]
  end
  @logger.warn "#{adapter_name} shortened default index name #{default_name} to #{shortened_name}" if @logger
  shortened_name
end

#index_name_exists?(table_name, index_name, default) ⇒ Boolean

Verify the existence of an index with a given name.

The default argument is returned if the underlying implementation does not define the indexes method, as there’s no way to determine the correct answer in that case.

Will always query database and not index cache.

Returns:

  • (Boolean)


220
221
222
223
224
225
226
227
228
229
230
# File 'lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb', line 220

def index_name_exists?(table_name, index_name, default)
  (owner, table_name, db_link) = @connection.describe(table_name)
  result = select_value(<<-SQL)
    SELECT 1 FROM all_indexes#{db_link} i
    WHERE i.owner = '#{owner}'
       AND i.table_owner = '#{owner}'
       AND i.table_name = '#{table_name}'
       AND i.index_name = '#{index_name.to_s.upcase}'
  SQL
  result == 1
end

#initialize_schema_migrations_tableObject



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
145
146
# File 'lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb', line 119

def initialize_schema_migrations_table
  sm_table = ActiveRecord::Migrator.schema_migrations_table_name

  unless table_exists?(sm_table)
    index_name = "#{Base.table_name_prefix}unique_schema_migrations#{Base.table_name_suffix}"
    if index_name.length > index_name_length
      truncate_to    = index_name_length - index_name.to_s.length - 1
      truncated_name = "unique_schema_migrations"[0..truncate_to]
      index_name     = "#{Base.table_name_prefix}#{truncated_name}#{Base.table_name_suffix}"
    end

    create_table(sm_table, :id => false) do |schema_migrations_table|
      schema_migrations_table.column :version, :string, :null => false
    end
    add_index sm_table, :version, :unique => true, :name => index_name

    # Backwards-compatibility: if we find schema_info, assume we've
    # migrated up to that point:
    si_table = Base.table_name_prefix + 'schema_info' + Base.table_name_suffix
    if table_exists?(si_table)
      ActiveSupport::Deprecation.warn "Usage of the schema table `#{si_table}` is deprecated. Please switch to using `schema_migrations` table"

      old_version = select_value("SELECT version FROM #{quote_table_name(si_table)}").to_i
      assume_migrated_upto_version(old_version)
      drop_table(si_table)
    end
  end
end

#remove_column(table_name, *column_names) ⇒ Object

:nodoc:



302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
# File 'lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb', line 302

def remove_column(table_name, *column_names) #:nodoc:
  raise ArgumentError.new("You must specify at least one column name.  Example: remove_column(:people, :first_name)") if column_names.empty?

  major, minor = ActiveRecord::VERSION::MAJOR, ActiveRecord::VERSION::MINOR
  is_deprecated = (major == 3 and minor >= 2) or major > 3

  if column_names.flatten! and is_deprecated
    message = 'Passing array to remove_columns is deprecated, please use ' +
      'multiple arguments, like: `remove_columns(:posts, :foo, :bar)`'
    ActiveSupport::Deprecation.warn message, caller
  end

  column_names.each do |column_name|
    execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
  end
ensure
  clear_table_columns_cache(table_name)
end

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

Remove the given index from the table. Gives warning if index does not exist



176
177
178
179
180
181
182
# File 'lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb', line 176

def remove_index(table_name, options = {}) #:nodoc:
  index_name = index_name(table_name, options)
  unless index_name_exists?(table_name, index_name, true)
    raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
  end
  remove_index!(table_name, index_name)
end

#remove_index!(table_name, index_name) ⇒ Object

clear cached indexes when removing index



185
186
187
188
189
# File 'lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb', line 185

def remove_index!(table_name, index_name) #:nodoc:
  execute "DROP INDEX #{quote_column_name(index_name)}"
ensure
  self.all_schema_indexes = nil
end

#rename_column(table_name, column_name, new_column_name) ⇒ Object

:nodoc:



296
297
298
299
300
# File 'lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb', line 296

def rename_column(table_name, column_name, new_column_name) #:nodoc:
  execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} to #{quote_column_name(new_column_name)}"
ensure
  clear_table_columns_cache(table_name)
end

#rename_index(table_name, index_name, new_index_name) ⇒ Object

:nodoc:



232
233
234
235
236
237
238
239
# File 'lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb', line 232

def rename_index(table_name, index_name, new_index_name) #:nodoc:
  unless index_name_exists?(table_name, index_name, true)
    raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
  end
  execute "ALTER INDEX #{quote_column_name(index_name)} rename to #{quote_column_name(new_index_name)}"
ensure
  self.all_schema_indexes = nil
end

#rename_table(name, new_name) ⇒ Object

:nodoc:



100
101
102
103
104
105
106
107
108
109
# File 'lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb', line 100

def rename_table(name, new_name) #:nodoc:
  if new_name.to_s.length > table_name_length
    raise ArgumentError, "New table name '#{new_name}' is too long; the limit is #{table_name_length} characters"
  end
  if "#{new_name}_seq".to_s.length > sequence_name_length
    raise ArgumentError, "New sequence name '#{new_name}_seq' is too long; the limit is #{sequence_name_length} characters"
  end
  execute "RENAME #{quote_table_name(name)} TO #{quote_table_name(new_name)}"
  execute "RENAME #{quote_table_name("#{name}_seq")} TO #{quote_table_name("#{new_name}_seq")}"
end

#table_comment(table_name) ⇒ Object

:nodoc:



331
332
333
334
335
336
337
338
# File 'lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb', line 331

def table_comment(table_name) #:nodoc:
  (owner, table_name, db_link) = @connection.describe(table_name)
  select_value <<-SQL
    SELECT comments FROM all_tab_comments#{db_link}
    WHERE owner = '#{owner}'
      AND table_name = '#{table_name}'
  SQL
end

#tablespace(table_name) ⇒ Object



358
359
360
361
362
363
364
# File 'lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb', line 358

def tablespace(table_name)
  select_value <<-SQL
    SELECT tablespace_name
    FROM user_tables
    WHERE table_name='#{table_name.to_s.upcase}'
  SQL
end

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

Maps logical Rails types to Oracle-specific data types.



351
352
353
354
355
356
# File 'lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb', line 351

def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
  # Ignore options for :text and :binary columns
  return super(type, nil, nil, nil) if ['text', 'binary'].include?(type.to_s)

  super
end