pmacs-activerecord-oracle_enhanced-adapter 1.5.6.1 → 1.6.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +7 -7
- data/History.md +126 -0
- data/README.md +285 -178
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/lib/active_record/connection_adapters/{oracle_enhanced_column.rb → oracle_enhanced/column.rb} +14 -63
- data/lib/active_record/connection_adapters/oracle_enhanced/column_dumper.rb +65 -0
- data/lib/active_record/connection_adapters/{oracle_enhanced_connection.rb → oracle_enhanced/connection.rb} +2 -2
- data/lib/active_record/connection_adapters/oracle_enhanced/context_index.rb +347 -0
- data/lib/active_record/connection_adapters/{oracle_enhanced_cpk.rb → oracle_enhanced/cpk.rb} +0 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb +257 -0
- data/lib/active_record/connection_adapters/{oracle_enhanced_database_tasks.rb → oracle_enhanced/database_tasks.rb} +0 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/dirty.rb +40 -0
- data/lib/active_record/connection_adapters/{oracle_enhanced_jdbc_connection.rb → oracle_enhanced/jdbc_connection.rb} +0 -0
- data/lib/active_record/connection_adapters/{oracle_enhanced_oci_connection.rb → oracle_enhanced/oci_connection.rb} +0 -0
- data/lib/active_record/connection_adapters/{oracle_enhanced_procedures.rb → oracle_enhanced/procedures.rb} +1 -3
- data/lib/active_record/connection_adapters/{oracle_enhanced_schema_creation.rb → oracle_enhanced/schema_creation.rb} +34 -35
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_definitions.rb +95 -0
- data/lib/active_record/connection_adapters/{oracle_enhanced_schema_dumper.rb → oracle_enhanced/schema_dumper.rb} +4 -32
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb +548 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements_ext.rb +74 -0
- data/lib/active_record/connection_adapters/{oracle_enhanced_structure_dump.rb → oracle_enhanced/structure_dump.rb} +28 -6
- data/lib/active_record/connection_adapters/oracle_enhanced/version.rb +1 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +161 -68
- data/lib/active_record/oracle_enhanced/type/integer.rb +13 -0
- data/lib/active_record/oracle_enhanced/type/raw.rb +13 -0
- data/lib/active_record/oracle_enhanced/type/timestamp.rb +11 -0
- data/lib/pmacs-activerecord-oracle_enhanced-adapter.rb +1 -1
- data/pmacs-activerecord-oracle_enhanced-adapter.gemspec +35 -31
- data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +6 -31
- data/spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb +1 -1
- data/spec/active_record/connection_adapters/oracle_enhanced_context_index_spec.rb +2 -2
- data/spec/active_record/connection_adapters/oracle_enhanced_cpk_spec.rb +2 -2
- data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +75 -63
- data/spec/active_record/connection_adapters/oracle_enhanced_database_tasks_spec.rb +1 -1
- data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +7 -13
- data/spec/active_record/connection_adapters/oracle_enhanced_procedures_spec.rb +2 -1
- data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +25 -178
- data/spec/active_record/connection_adapters/oracle_enhanced_schema_statements_spec.rb +60 -5
- data/spec/active_record/connection_adapters/oracle_enhanced_structure_dump_spec.rb +1 -0
- data/spec/spec_helper.rb +21 -10
- metadata +32 -28
- data/lib/active_record/connection_adapters/oracle_enhanced_column_dumper.rb +0 -77
- data/lib/active_record/connection_adapters/oracle_enhanced_context_index.rb +0 -350
- data/lib/active_record/connection_adapters/oracle_enhanced_database_statements.rb +0 -262
- data/lib/active_record/connection_adapters/oracle_enhanced_dirty.rb +0 -45
- data/lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb +0 -223
- data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb +0 -450
- data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements_ext.rb +0 -267
- data/lib/active_record/connection_adapters/oracle_enhanced_version.rb +0 -1
@@ -1,450 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
require 'digest/sha1'
|
3
|
-
|
4
|
-
module ActiveRecord
|
5
|
-
module ConnectionAdapters
|
6
|
-
module OracleEnhancedSchemaStatements
|
7
|
-
# SCHEMA STATEMENTS ========================================
|
8
|
-
#
|
9
|
-
# see: abstract/schema_statements.rb
|
10
|
-
|
11
|
-
# Additional options for +create_table+ method in migration files.
|
12
|
-
#
|
13
|
-
# You can specify individual starting value in table creation migration file, e.g.:
|
14
|
-
#
|
15
|
-
# create_table :users, :sequence_start_value => 100 do |t|
|
16
|
-
# # ...
|
17
|
-
# end
|
18
|
-
#
|
19
|
-
# You can also specify other sequence definition additional parameters, e.g.:
|
20
|
-
#
|
21
|
-
# create_table :users, :sequence_start_value => “100 NOCACHE INCREMENT BY 10” do |t|
|
22
|
-
# # ...
|
23
|
-
# end
|
24
|
-
#
|
25
|
-
# Create primary key trigger (so that you can skip primary key value in INSERT statement).
|
26
|
-
# By default trigger name will be "table_name_pkt", you can override the name with
|
27
|
-
# :trigger_name option (but it is not recommended to override it as then this trigger will
|
28
|
-
# not be detected by ActiveRecord model and it will still do prefetching of sequence value).
|
29
|
-
# Example:
|
30
|
-
#
|
31
|
-
# create_table :users, :primary_key_trigger => true do |t|
|
32
|
-
# # ...
|
33
|
-
# end
|
34
|
-
#
|
35
|
-
# It is possible to add table and column comments in table creation migration files:
|
36
|
-
#
|
37
|
-
# create_table :employees, :comment => “Employees and contractors” do |t|
|
38
|
-
# t.string :first_name, :comment => “Given name”
|
39
|
-
# t.string :last_name, :comment => “Surname”
|
40
|
-
# end
|
41
|
-
|
42
|
-
def create_table(name, options = {})
|
43
|
-
create_sequence = options[:id] != false
|
44
|
-
column_comments = {}
|
45
|
-
temporary = options.delete(:temporary)
|
46
|
-
additional_options = options
|
47
|
-
table_definition = create_table_definition name, temporary, additional_options
|
48
|
-
table_definition.primary_key(options[:primary_key] || Base.get_primary_key(name.to_s.singularize)) unless options[:id] == false
|
49
|
-
|
50
|
-
# store that primary key was defined in create_table block
|
51
|
-
unless create_sequence
|
52
|
-
class << table_definition
|
53
|
-
attr_accessor :create_sequence
|
54
|
-
def primary_key(*args)
|
55
|
-
self.create_sequence = true
|
56
|
-
super(*args)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
# store column comments
|
62
|
-
class << table_definition
|
63
|
-
attr_accessor :column_comments
|
64
|
-
def column(name, type, options = {})
|
65
|
-
if options[:comment]
|
66
|
-
self.column_comments ||= {}
|
67
|
-
self.column_comments[name] = options[:comment]
|
68
|
-
end
|
69
|
-
super(name, type, options)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
yield table_definition if block_given?
|
74
|
-
create_sequence = create_sequence || table_definition.create_sequence
|
75
|
-
column_comments = table_definition.column_comments if table_definition.column_comments
|
76
|
-
tablespace = tablespace_for(:table, options[:tablespace])
|
77
|
-
|
78
|
-
if options[:force] && table_exists?(name)
|
79
|
-
drop_table(name, options)
|
80
|
-
end
|
81
|
-
|
82
|
-
execute schema_creation.accept table_definition
|
83
|
-
|
84
|
-
create_sequence_and_trigger(name, options) if create_sequence
|
85
|
-
|
86
|
-
add_table_comment name, options[:comment]
|
87
|
-
column_comments.each do |column_name, comment|
|
88
|
-
add_comment name, column_name, comment
|
89
|
-
end
|
90
|
-
table_definition.indexes.each_pair { |c,o| add_index name, c, o }
|
91
|
-
|
92
|
-
unless table_definition.foreign_keys.nil?
|
93
|
-
table_definition.foreign_keys.each do |foreign_key|
|
94
|
-
add_foreign_key(table_definition.name, foreign_key.to_table, foreign_key.options)
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
def create_table_definition(name, temporary, options)
|
100
|
-
TableDefinition.new native_database_types, name, temporary, options
|
101
|
-
end
|
102
|
-
|
103
|
-
def rename_table(table_name, new_name) #:nodoc:
|
104
|
-
if new_name.to_s.length > table_name_length
|
105
|
-
raise ArgumentError, "New table name '#{new_name}' is too long; the limit is #{table_name_length} characters"
|
106
|
-
end
|
107
|
-
if "#{new_name}_seq".to_s.length > sequence_name_length
|
108
|
-
raise ArgumentError, "New sequence name '#{new_name}_seq' is too long; the limit is #{sequence_name_length} characters"
|
109
|
-
end
|
110
|
-
execute "RENAME #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
|
111
|
-
execute "RENAME #{quote_table_name("#{table_name}_seq")} TO #{quote_table_name("#{new_name}_seq")}" rescue nil
|
112
|
-
|
113
|
-
rename_table_indexes(table_name, new_name)
|
114
|
-
end
|
115
|
-
|
116
|
-
def drop_table(name, options = {}) #:nodoc:
|
117
|
-
super(name)
|
118
|
-
seq_name = options[:sequence_name] || default_sequence_name(name)
|
119
|
-
execute "DROP SEQUENCE #{quote_table_name(seq_name)}" rescue nil
|
120
|
-
rescue ActiveRecord::StatementInvalid => e
|
121
|
-
raise e unless options[:if_exists]
|
122
|
-
ensure
|
123
|
-
clear_table_columns_cache(name)
|
124
|
-
self.all_schema_indexes = nil
|
125
|
-
end
|
126
|
-
|
127
|
-
def initialize_schema_migrations_table
|
128
|
-
sm_table = ActiveRecord::Migrator.schema_migrations_table_name
|
129
|
-
|
130
|
-
unless table_exists?(sm_table)
|
131
|
-
index_name = "#{Base.table_name_prefix}unique_schema_migrations#{Base.table_name_suffix}"
|
132
|
-
if index_name.length > index_name_length
|
133
|
-
truncate_to = index_name_length - index_name.to_s.length - 1
|
134
|
-
truncated_name = "unique_schema_migrations"[0..truncate_to]
|
135
|
-
index_name = "#{Base.table_name_prefix}#{truncated_name}#{Base.table_name_suffix}"
|
136
|
-
end
|
137
|
-
|
138
|
-
create_table(sm_table, :id => false) do |schema_migrations_table|
|
139
|
-
schema_migrations_table.column :version, :string, :null => false
|
140
|
-
end
|
141
|
-
add_index sm_table, :version, :unique => true, :name => index_name
|
142
|
-
|
143
|
-
# Backwards-compatibility: if we find schema_info, assume we've
|
144
|
-
# migrated up to that point:
|
145
|
-
si_table = Base.table_name_prefix + 'schema_info' + Base.table_name_suffix
|
146
|
-
if table_exists?(si_table)
|
147
|
-
ActiveSupport::Deprecation.warn "Usage of the schema table `#{si_table}` is deprecated. Please switch to using `schema_migrations` table"
|
148
|
-
|
149
|
-
old_version = select_value("SELECT version FROM #{quote_table_name(si_table)}").to_i
|
150
|
-
assume_migrated_upto_version(old_version)
|
151
|
-
drop_table(si_table)
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
# clear cached indexes when adding new index
|
157
|
-
def add_index(table_name, column_name, options = {}) #:nodoc:
|
158
|
-
column_names = Array(column_name)
|
159
|
-
index_name = index_name(table_name, column: column_names)
|
160
|
-
|
161
|
-
if Hash === options # legacy support, since this param was a string
|
162
|
-
options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :tablespace, :options, :using)
|
163
|
-
|
164
|
-
index_type = options[:unique] ? "UNIQUE" : ""
|
165
|
-
index_name = options[:name].to_s if options.key?(:name)
|
166
|
-
tablespace = tablespace_for(:index, options[:tablespace])
|
167
|
-
max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
|
168
|
-
additional_options = options[:options]
|
169
|
-
else
|
170
|
-
if options
|
171
|
-
message = "Passing a string as third argument of `add_index` is deprecated and will" +
|
172
|
-
" be removed in Rails 4.1." +
|
173
|
-
" Use add_index(#{table_name.inspect}, #{column_name.inspect}, unique: true) instead"
|
174
|
-
|
175
|
-
ActiveSupport::Deprecation.warn message
|
176
|
-
end
|
177
|
-
|
178
|
-
index_type = options
|
179
|
-
additional_options = nil
|
180
|
-
max_index_length = allowed_index_name_length
|
181
|
-
end
|
182
|
-
|
183
|
-
if index_name.to_s.length > max_index_length
|
184
|
-
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{max_index_length} characters"
|
185
|
-
end
|
186
|
-
if index_name_exists?(table_name, index_name, false)
|
187
|
-
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
|
188
|
-
end
|
189
|
-
quoted_column_names = column_names.map { |e| quote_column_name_or_expression(e) }.join(", ")
|
190
|
-
|
191
|
-
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})#{tablespace} #{additional_options}"
|
192
|
-
ensure
|
193
|
-
self.all_schema_indexes = nil
|
194
|
-
end
|
195
|
-
|
196
|
-
# Remove the given index from the table.
|
197
|
-
# Gives warning if index does not exist
|
198
|
-
def remove_index(table_name, options = {}) #:nodoc:
|
199
|
-
index_name = index_name(table_name, options)
|
200
|
-
unless index_name_exists?(table_name, index_name, true)
|
201
|
-
# sometimes options can be String or Array with column names
|
202
|
-
options = {} unless options.is_a?(Hash)
|
203
|
-
if options.has_key? :name
|
204
|
-
options_without_column = options.dup
|
205
|
-
options_without_column.delete :column
|
206
|
-
index_name_without_column = index_name(table_name, options_without_column)
|
207
|
-
return index_name_without_column if index_name_exists?(table_name, index_name_without_column, false)
|
208
|
-
end
|
209
|
-
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
|
210
|
-
end
|
211
|
-
remove_index!(table_name, index_name)
|
212
|
-
end
|
213
|
-
|
214
|
-
# clear cached indexes when removing index
|
215
|
-
def remove_index!(table_name, index_name) #:nodoc:
|
216
|
-
execute "DROP INDEX #{quote_column_name(index_name)}"
|
217
|
-
ensure
|
218
|
-
self.all_schema_indexes = nil
|
219
|
-
end
|
220
|
-
|
221
|
-
# returned shortened index name if default is too large
|
222
|
-
def index_name(table_name, options) #:nodoc:
|
223
|
-
default_name = super(table_name, options).to_s
|
224
|
-
# sometimes options can be String or Array with column names
|
225
|
-
options = {} unless options.is_a?(Hash)
|
226
|
-
identifier_max_length = options[:identifier_max_length] || index_name_length
|
227
|
-
return default_name if default_name.length <= identifier_max_length
|
228
|
-
|
229
|
-
# remove 'index', 'on' and 'and' keywords
|
230
|
-
shortened_name = "i_#{table_name}_#{Array(options[:column]) * '_'}"
|
231
|
-
|
232
|
-
# leave just first three letters from each word
|
233
|
-
if shortened_name.length > identifier_max_length
|
234
|
-
shortened_name = shortened_name.split('_').map{|w| w[0,3]}.join('_')
|
235
|
-
end
|
236
|
-
# generate unique name using hash function
|
237
|
-
if shortened_name.length > identifier_max_length
|
238
|
-
shortened_name = 'i'+Digest::SHA1.hexdigest(default_name)[0,identifier_max_length-1]
|
239
|
-
end
|
240
|
-
@logger.warn "#{adapter_name} shortened default index name #{default_name} to #{shortened_name}" if @logger
|
241
|
-
shortened_name
|
242
|
-
end
|
243
|
-
|
244
|
-
# Verify the existence of an index with a given name.
|
245
|
-
#
|
246
|
-
# The default argument is returned if the underlying implementation does not define the indexes method,
|
247
|
-
# as there's no way to determine the correct answer in that case.
|
248
|
-
#
|
249
|
-
# Will always query database and not index cache.
|
250
|
-
def index_name_exists?(table_name, index_name, default)
|
251
|
-
(owner, table_name, db_link) = @connection.describe(table_name)
|
252
|
-
result = select_value(<<-SQL)
|
253
|
-
SELECT 1 FROM all_indexes#{db_link} i
|
254
|
-
WHERE i.owner = '#{owner}'
|
255
|
-
AND i.table_owner = '#{owner}'
|
256
|
-
AND i.table_name = '#{table_name}'
|
257
|
-
AND i.index_name = '#{index_name.to_s.upcase}'
|
258
|
-
SQL
|
259
|
-
result == 1
|
260
|
-
end
|
261
|
-
|
262
|
-
def rename_index(table_name, index_name, new_index_name) #:nodoc:
|
263
|
-
unless index_name_exists?(table_name, index_name, true)
|
264
|
-
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
|
265
|
-
end
|
266
|
-
execute "ALTER INDEX #{quote_column_name(index_name)} rename to #{quote_column_name(new_index_name)}"
|
267
|
-
ensure
|
268
|
-
self.all_schema_indexes = nil
|
269
|
-
end
|
270
|
-
|
271
|
-
def add_column(table_name, column_name, type, options = {}) #:nodoc:
|
272
|
-
if type.to_sym == :virtual
|
273
|
-
type = options[:type]
|
274
|
-
end
|
275
|
-
add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} "
|
276
|
-
add_column_sql << type_to_sql(type, options[:limit], options[:precision], options[:scale]) if type
|
277
|
-
|
278
|
-
add_column_options!(add_column_sql, options.merge(:type=>type, :column_name=>column_name, :table_name=>table_name))
|
279
|
-
|
280
|
-
add_column_sql << tablespace_for((type_to_sql(type).downcase.to_sym), nil, table_name, column_name) if type
|
281
|
-
|
282
|
-
execute(add_column_sql)
|
283
|
-
|
284
|
-
create_sequence_and_trigger(table_name, options) if type && type.to_sym == :primary_key
|
285
|
-
ensure
|
286
|
-
clear_table_columns_cache(table_name)
|
287
|
-
end
|
288
|
-
|
289
|
-
def change_column_default(table_name, column_name, default) #:nodoc:
|
290
|
-
execute "ALTER TABLE #{quote_table_name(table_name)} MODIFY #{quote_column_name(column_name)} DEFAULT #{quote(default)}"
|
291
|
-
ensure
|
292
|
-
clear_table_columns_cache(table_name)
|
293
|
-
end
|
294
|
-
|
295
|
-
def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
|
296
|
-
column = column_for(table_name, column_name)
|
297
|
-
|
298
|
-
unless null || default.nil?
|
299
|
-
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
|
300
|
-
end
|
301
|
-
|
302
|
-
change_column table_name, column_name, column.sql_type, :null => null
|
303
|
-
end
|
304
|
-
|
305
|
-
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
306
|
-
column = column_for(table_name, column_name)
|
307
|
-
|
308
|
-
# remove :null option if its value is the same as current column definition
|
309
|
-
# otherwise Oracle will raise error
|
310
|
-
if options.has_key?(:null) && options[:null] == column.null
|
311
|
-
options[:null] = nil
|
312
|
-
end
|
313
|
-
if type.to_sym == :virtual
|
314
|
-
type = options[:type]
|
315
|
-
end
|
316
|
-
change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} MODIFY #{quote_column_name(column_name)} "
|
317
|
-
change_column_sql << "#{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" if type
|
318
|
-
|
319
|
-
add_column_options!(change_column_sql, options.merge(:type=>type, :column_name=>column_name, :table_name=>table_name))
|
320
|
-
|
321
|
-
change_column_sql << tablespace_for((type_to_sql(type).downcase.to_sym), nil, options[:table_name], options[:column_name]) if type
|
322
|
-
|
323
|
-
execute(change_column_sql)
|
324
|
-
ensure
|
325
|
-
clear_table_columns_cache(table_name)
|
326
|
-
end
|
327
|
-
|
328
|
-
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
329
|
-
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} to #{quote_column_name(new_column_name)}"
|
330
|
-
self.all_schema_indexes = nil
|
331
|
-
rename_column_indexes(table_name, column_name, new_column_name)
|
332
|
-
ensure
|
333
|
-
clear_table_columns_cache(table_name)
|
334
|
-
end
|
335
|
-
|
336
|
-
def remove_column(table_name, column_name, type = nil, options = {}) #:nodoc:
|
337
|
-
execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
|
338
|
-
ensure
|
339
|
-
clear_table_columns_cache(table_name)
|
340
|
-
self.all_schema_indexes = nil
|
341
|
-
end
|
342
|
-
|
343
|
-
def add_comment(table_name, column_name, comment) #:nodoc:
|
344
|
-
return if comment.blank?
|
345
|
-
execute "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{column_name} IS '#{comment}'"
|
346
|
-
end
|
347
|
-
|
348
|
-
def add_table_comment(table_name, comment) #:nodoc:
|
349
|
-
return if comment.blank?
|
350
|
-
execute "COMMENT ON TABLE #{quote_table_name(table_name)} IS '#{comment}'"
|
351
|
-
end
|
352
|
-
|
353
|
-
def table_comment(table_name) #:nodoc:
|
354
|
-
(owner, table_name, db_link) = @connection.describe(table_name)
|
355
|
-
select_value <<-SQL
|
356
|
-
SELECT comments FROM all_tab_comments#{db_link}
|
357
|
-
WHERE owner = '#{owner}'
|
358
|
-
AND table_name = '#{table_name}'
|
359
|
-
SQL
|
360
|
-
end
|
361
|
-
|
362
|
-
def column_comment(table_name, column_name) #:nodoc:
|
363
|
-
(owner, table_name, db_link) = @connection.describe(table_name)
|
364
|
-
select_value <<-SQL
|
365
|
-
SELECT comments FROM all_col_comments#{db_link}
|
366
|
-
WHERE owner = '#{owner}'
|
367
|
-
AND table_name = '#{table_name}'
|
368
|
-
AND column_name = '#{column_name.upcase}'
|
369
|
-
SQL
|
370
|
-
end
|
371
|
-
|
372
|
-
# Maps logical Rails types to Oracle-specific data types.
|
373
|
-
def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
|
374
|
-
# Ignore options for :text and :binary columns
|
375
|
-
return super(type, nil, nil, nil) if ['text', 'binary'].include?(type.to_s)
|
376
|
-
|
377
|
-
super
|
378
|
-
end
|
379
|
-
|
380
|
-
def tablespace(table_name)
|
381
|
-
select_value <<-SQL
|
382
|
-
SELECT tablespace_name
|
383
|
-
FROM user_tables
|
384
|
-
WHERE table_name='#{table_name.to_s.upcase}'
|
385
|
-
SQL
|
386
|
-
end
|
387
|
-
|
388
|
-
private
|
389
|
-
|
390
|
-
def tablespace_for(obj_type, tablespace_option, table_name=nil, column_name=nil)
|
391
|
-
tablespace_sql = ''
|
392
|
-
if tablespace = (tablespace_option || default_tablespace_for(obj_type))
|
393
|
-
tablespace_sql << if [:blob, :clob].include?(obj_type.to_sym)
|
394
|
-
" LOB (#{quote_column_name(column_name)}) STORE AS #{column_name.to_s[0..10]}_#{table_name.to_s[0..14]}_ls (TABLESPACE #{tablespace})"
|
395
|
-
else
|
396
|
-
" TABLESPACE #{tablespace}"
|
397
|
-
end
|
398
|
-
end
|
399
|
-
tablespace_sql
|
400
|
-
end
|
401
|
-
|
402
|
-
def default_tablespace_for(type)
|
403
|
-
(default_tablespaces[type] || default_tablespaces[native_database_types[type][:name]]) rescue nil
|
404
|
-
end
|
405
|
-
|
406
|
-
|
407
|
-
def column_for(table_name, column_name)
|
408
|
-
unless column = columns(table_name).find { |c| c.name == column_name.to_s }
|
409
|
-
raise "No such column: #{table_name}.#{column_name}"
|
410
|
-
end
|
411
|
-
column
|
412
|
-
end
|
413
|
-
|
414
|
-
def create_sequence_and_trigger(table_name, options)
|
415
|
-
seq_name = options[:sequence_name] || default_sequence_name(table_name)
|
416
|
-
seq_start_value = options[:sequence_start_value] || default_sequence_start_value
|
417
|
-
execute "CREATE SEQUENCE #{quote_table_name(seq_name)} START WITH #{seq_start_value}"
|
418
|
-
|
419
|
-
create_primary_key_trigger(table_name, options) if options[:primary_key_trigger]
|
420
|
-
end
|
421
|
-
|
422
|
-
def create_primary_key_trigger(table_name, options)
|
423
|
-
seq_name = options[:sequence_name] || default_sequence_name(table_name)
|
424
|
-
trigger_name = options[:trigger_name] || default_trigger_name(table_name)
|
425
|
-
primary_key = options[:primary_key] || Base.get_primary_key(table_name.to_s.singularize)
|
426
|
-
execute compress_lines(<<-SQL)
|
427
|
-
CREATE OR REPLACE TRIGGER #{quote_table_name(trigger_name)}
|
428
|
-
BEFORE INSERT ON #{quote_table_name(table_name)} FOR EACH ROW
|
429
|
-
BEGIN
|
430
|
-
IF inserting THEN
|
431
|
-
IF :new.#{quote_column_name(primary_key)} IS NULL THEN
|
432
|
-
SELECT #{quote_table_name(seq_name)}.NEXTVAL INTO :new.#{quote_column_name(primary_key)} FROM dual;
|
433
|
-
END IF;
|
434
|
-
END IF;
|
435
|
-
END;
|
436
|
-
SQL
|
437
|
-
end
|
438
|
-
|
439
|
-
def default_trigger_name(table_name)
|
440
|
-
# truncate table name if necessary to fit in max length of identifier
|
441
|
-
"#{table_name.to_s[0,table_name_length-4]}_pkt"
|
442
|
-
end
|
443
|
-
|
444
|
-
end
|
445
|
-
end
|
446
|
-
end
|
447
|
-
|
448
|
-
ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.class_eval do
|
449
|
-
include ActiveRecord::ConnectionAdapters::OracleEnhancedSchemaStatements
|
450
|
-
end
|
@@ -1,267 +0,0 @@
|
|
1
|
-
require 'digest/sha1'
|
2
|
-
|
3
|
-
module ActiveRecord
|
4
|
-
module ConnectionAdapters
|
5
|
-
module OracleEnhancedSchemaStatementsExt
|
6
|
-
def supports_foreign_keys? #:nodoc:
|
7
|
-
true
|
8
|
-
end
|
9
|
-
|
10
|
-
# Create primary key trigger (so that you can skip primary key value in INSERT statement).
|
11
|
-
# By default trigger name will be "table_name_pkt", you can override the name with
|
12
|
-
# :trigger_name option (but it is not recommended to override it as then this trigger will
|
13
|
-
# not be detected by ActiveRecord model and it will still do prefetching of sequence value).
|
14
|
-
#
|
15
|
-
# add_primary_key_trigger :users
|
16
|
-
#
|
17
|
-
# You can also create primary key trigger using +create_table+ with :primary_key_trigger
|
18
|
-
# option:
|
19
|
-
#
|
20
|
-
# create_table :users, :primary_key_trigger => true do |t|
|
21
|
-
# # ...
|
22
|
-
# end
|
23
|
-
#
|
24
|
-
def add_primary_key_trigger(table_name, options={})
|
25
|
-
# call the same private method that is used for create_table :primary_key_trigger => true
|
26
|
-
create_primary_key_trigger(table_name, options)
|
27
|
-
end
|
28
|
-
|
29
|
-
def table_definition_tablespace
|
30
|
-
# TODO: Support specifying an :index_tablespace option in create_table?
|
31
|
-
tablespace_sql = ''
|
32
|
-
if tablespace = default_tablespace_for(:index)
|
33
|
-
tablespace_sql << " USING INDEX TABLESPACE #{tablespace}"
|
34
|
-
end
|
35
|
-
tablespace_sql
|
36
|
-
end
|
37
|
-
|
38
|
-
# Adds a new foreign key to the +from_table+, referencing the primary key of +to_table+
|
39
|
-
# (syntax and partial implementation taken from http://github.com/matthuhiggins/foreigner)
|
40
|
-
#
|
41
|
-
# The foreign key will be named after the from and to tables unless you pass
|
42
|
-
# <tt>:name</tt> as an option.
|
43
|
-
#
|
44
|
-
# === Examples
|
45
|
-
# ==== Creating a foreign key
|
46
|
-
# add_foreign_key(:comments, :posts)
|
47
|
-
# generates
|
48
|
-
# ALTER TABLE comments ADD CONSTRAINT
|
49
|
-
# comments_post_id_fk FOREIGN KEY (post_id) REFERENCES posts (id)
|
50
|
-
#
|
51
|
-
# ==== Creating a named foreign key
|
52
|
-
# add_foreign_key(:comments, :posts, :name => 'comments_belongs_to_posts')
|
53
|
-
# generates
|
54
|
-
# ALTER TABLE comments ADD CONSTRAINT
|
55
|
-
# comments_belongs_to_posts FOREIGN KEY (post_id) REFERENCES posts (id)
|
56
|
-
#
|
57
|
-
# ==== Creating a cascading foreign_key on a custom column
|
58
|
-
# add_foreign_key(:people, :people, :column => 'best_friend_id', :dependent => :nullify)
|
59
|
-
# generates
|
60
|
-
# ALTER TABLE people ADD CONSTRAINT
|
61
|
-
# people_best_friend_id_fk FOREIGN KEY (best_friend_id) REFERENCES people (id)
|
62
|
-
# ON DELETE SET NULL
|
63
|
-
#
|
64
|
-
# ==== Creating a composite foreign key
|
65
|
-
# add_foreign_key(:comments, :posts, :columns => ['post_id', 'author_id'], :name => 'comments_post_fk')
|
66
|
-
# generates
|
67
|
-
# ALTER TABLE comments ADD CONSTRAINT
|
68
|
-
# comments_post_fk FOREIGN KEY (post_id, author_id) REFERENCES posts (post_id, author_id)
|
69
|
-
#
|
70
|
-
# === Supported options
|
71
|
-
# [:column]
|
72
|
-
# Specify the column name on the from_table that references the to_table. By default this is guessed
|
73
|
-
# to be the singular name of the to_table with "_id" suffixed. So a to_table of :posts will use "post_id"
|
74
|
-
# as the default <tt>:column</tt>.
|
75
|
-
# [:columns]
|
76
|
-
# An array of column names when defining composite foreign keys. An alias of <tt>:column</tt> provided for improved readability.
|
77
|
-
# [:primary_key]
|
78
|
-
# Specify the column name on the to_table that is referenced by this foreign key. By default this is
|
79
|
-
# assumed to be "id". Ignored when defining composite foreign keys.
|
80
|
-
# [:name]
|
81
|
-
# Specify the name of the foreign key constraint. This defaults to use from_table and foreign key column.
|
82
|
-
# [:dependent]
|
83
|
-
# If set to <tt>:delete</tt>, the associated records in from_table are deleted when records in to_table table are deleted.
|
84
|
-
# If set to <tt>:nullify</tt>, the foreign key column is set to +NULL+.
|
85
|
-
def add_foreign_key(from_table, to_table, options = {})
|
86
|
-
columns = options[:column] || options[:columns] || "#{to_table.to_s.singularize}_id"
|
87
|
-
constraint_name = foreign_key_constraint_name(from_table, columns, options)
|
88
|
-
sql = "ALTER TABLE #{quote_table_name(from_table)} ADD CONSTRAINT #{quote_column_name(constraint_name)} "
|
89
|
-
sql << foreign_key_definition(to_table, options)
|
90
|
-
execute sql
|
91
|
-
end
|
92
|
-
|
93
|
-
def foreign_key_definition(to_table, options = {}) #:nodoc:
|
94
|
-
columns = Array(options[:column] || options[:columns])
|
95
|
-
|
96
|
-
if columns.size > 1
|
97
|
-
# composite foreign key
|
98
|
-
columns_sql = columns.map {|c| quote_column_name(c)}.join(',')
|
99
|
-
references = options[:references] || columns
|
100
|
-
references_sql = references.map {|c| quote_column_name(c)}.join(',')
|
101
|
-
else
|
102
|
-
columns_sql = quote_column_name(columns.first || "#{to_table.to_s.singularize}_id")
|
103
|
-
references = options[:references] ? options[:references].first : nil
|
104
|
-
references_sql = quote_column_name(options[:primary_key] || references || "id")
|
105
|
-
end
|
106
|
-
|
107
|
-
table_name = ActiveRecord::Migrator.proper_table_name(to_table)
|
108
|
-
|
109
|
-
sql = "FOREIGN KEY (#{columns_sql}) REFERENCES #{quote_table_name(table_name)}(#{references_sql})"
|
110
|
-
|
111
|
-
case options[:dependent]
|
112
|
-
when :nullify
|
113
|
-
sql << " ON DELETE SET NULL"
|
114
|
-
when :delete
|
115
|
-
sql << " ON DELETE CASCADE"
|
116
|
-
end
|
117
|
-
sql
|
118
|
-
end
|
119
|
-
|
120
|
-
# Remove the given foreign key from the table.
|
121
|
-
#
|
122
|
-
# ===== Examples
|
123
|
-
# ====== Remove the suppliers_company_id_fk in the suppliers table.
|
124
|
-
# remove_foreign_key :suppliers, :companies
|
125
|
-
# ====== Remove the foreign key named accounts_branch_id_fk in the accounts table.
|
126
|
-
# remove_foreign_key :accounts, :column => :branch_id
|
127
|
-
# ====== Remove the foreign key named party_foreign_key in the accounts table.
|
128
|
-
# remove_foreign_key :accounts, :name => :party_foreign_key
|
129
|
-
def remove_foreign_key(from_table, options)
|
130
|
-
if Hash === options
|
131
|
-
constraint_name = foreign_key_constraint_name(from_table, options[:column], options)
|
132
|
-
else
|
133
|
-
constraint_name = foreign_key_constraint_name(from_table, "#{options.to_s.singularize}_id")
|
134
|
-
end
|
135
|
-
execute "ALTER TABLE #{quote_table_name(from_table)} DROP CONSTRAINT #{quote_column_name(constraint_name)}"
|
136
|
-
end
|
137
|
-
|
138
|
-
private
|
139
|
-
|
140
|
-
def foreign_key_constraint_name(table_name, columns, options = {})
|
141
|
-
columns = Array(columns)
|
142
|
-
constraint_name = original_name = options[:name] || "#{table_name}_#{columns.join('_')}_fk"
|
143
|
-
|
144
|
-
return constraint_name if constraint_name.length <= OracleEnhancedAdapter::IDENTIFIER_MAX_LENGTH
|
145
|
-
|
146
|
-
# leave just first three letters from each word
|
147
|
-
constraint_name = constraint_name.split('_').map{|w| w[0,3]}.join('_')
|
148
|
-
# generate unique name using hash function
|
149
|
-
if constraint_name.length > OracleEnhancedAdapter::IDENTIFIER_MAX_LENGTH
|
150
|
-
constraint_name = 'c'+Digest::SHA1.hexdigest(original_name)[0,OracleEnhancedAdapter::IDENTIFIER_MAX_LENGTH-1]
|
151
|
-
end
|
152
|
-
@logger.warn "#{adapter_name} shortened foreign key constraint name #{original_name} to #{constraint_name}" if @logger
|
153
|
-
constraint_name
|
154
|
-
end
|
155
|
-
|
156
|
-
|
157
|
-
public
|
158
|
-
|
159
|
-
# get table foreign keys for schema dump
|
160
|
-
def foreign_keys(table_name) #:nodoc:
|
161
|
-
(owner, desc_table_name, db_link) = @connection.describe(table_name)
|
162
|
-
|
163
|
-
fk_info = select_all(<<-SQL, 'Foreign Keys')
|
164
|
-
SELECT r.table_name to_table
|
165
|
-
,rc.column_name references_column
|
166
|
-
,cc.column_name
|
167
|
-
,c.constraint_name name
|
168
|
-
,c.delete_rule
|
169
|
-
FROM user_constraints#{db_link} c, user_cons_columns#{db_link} cc,
|
170
|
-
user_constraints#{db_link} r, user_cons_columns#{db_link} rc
|
171
|
-
WHERE c.owner = '#{owner}'
|
172
|
-
AND c.table_name = '#{desc_table_name}'
|
173
|
-
AND c.constraint_type = 'R'
|
174
|
-
AND cc.owner = c.owner
|
175
|
-
AND cc.constraint_name = c.constraint_name
|
176
|
-
AND r.constraint_name = c.r_constraint_name
|
177
|
-
AND r.owner = c.owner
|
178
|
-
AND rc.owner = r.owner
|
179
|
-
AND rc.constraint_name = r.constraint_name
|
180
|
-
AND rc.position = cc.position
|
181
|
-
ORDER BY name, to_table, column_name, references_column
|
182
|
-
SQL
|
183
|
-
|
184
|
-
fks = {}
|
185
|
-
|
186
|
-
fk_info.map do |row|
|
187
|
-
name = oracle_downcase(row['name'])
|
188
|
-
fks[name] ||= { :columns => [], :to_table => oracle_downcase(row['to_table']), :references => [] }
|
189
|
-
fks[name][:columns] << oracle_downcase(row['column_name'])
|
190
|
-
fks[name][:references] << oracle_downcase(row['references_column'])
|
191
|
-
case row['delete_rule']
|
192
|
-
when 'CASCADE'
|
193
|
-
fks[name][:dependent] = :delete
|
194
|
-
when 'SET NULL'
|
195
|
-
fks[name][:dependent] = :nullify
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
fks.map do |k, v|
|
200
|
-
options = {:name => k, :columns => v[:columns], :references => v[:references], :dependent => v[:dependent]}
|
201
|
-
OracleEnhancedForeignKeyDefinition.new(table_name, v[:to_table], options)
|
202
|
-
end
|
203
|
-
end
|
204
|
-
|
205
|
-
# REFERENTIAL INTEGRITY ====================================
|
206
|
-
|
207
|
-
def disable_referential_integrity(&block) #:nodoc:
|
208
|
-
sql_constraints = <<-SQL
|
209
|
-
SELECT constraint_name, owner, table_name
|
210
|
-
FROM user_constraints
|
211
|
-
WHERE constraint_type = 'R'
|
212
|
-
AND status = 'ENABLED'
|
213
|
-
SQL
|
214
|
-
old_constraints = select_all(sql_constraints)
|
215
|
-
begin
|
216
|
-
old_constraints.each do |constraint|
|
217
|
-
execute "ALTER TABLE #{constraint["table_name"]} DISABLE CONSTRAINT #{constraint["constraint_name"]}"
|
218
|
-
end
|
219
|
-
yield
|
220
|
-
ensure
|
221
|
-
old_constraints.each do |constraint|
|
222
|
-
execute "ALTER TABLE #{constraint["table_name"]} ENABLE CONSTRAINT #{constraint["constraint_name"]}"
|
223
|
-
end
|
224
|
-
end
|
225
|
-
end
|
226
|
-
|
227
|
-
# Add synonym to existing table or view or sequence. Can be used to create local synonym to
|
228
|
-
# remote table in other schema or in other database
|
229
|
-
# Examples:
|
230
|
-
#
|
231
|
-
# add_synonym :posts, "blog.posts"
|
232
|
-
# add_synonym :posts_seq, "blog.posts_seq"
|
233
|
-
# add_synonym :employees, "hr.employees@dblink", :force => true
|
234
|
-
#
|
235
|
-
def add_synonym(name, table_name, options = {})
|
236
|
-
sql = "CREATE"
|
237
|
-
if options[:force] == true
|
238
|
-
sql << " OR REPLACE"
|
239
|
-
end
|
240
|
-
sql << " SYNONYM #{quote_table_name(name)} FOR #{quote_table_name(table_name)}"
|
241
|
-
execute sql
|
242
|
-
end
|
243
|
-
|
244
|
-
# Remove existing synonym to table or view or sequence
|
245
|
-
# Example:
|
246
|
-
#
|
247
|
-
# remove_synonym :posts, "blog.posts"
|
248
|
-
#
|
249
|
-
def remove_synonym(name)
|
250
|
-
execute "DROP SYNONYM #{quote_table_name(name)}"
|
251
|
-
end
|
252
|
-
|
253
|
-
# get synonyms for schema dump
|
254
|
-
def synonyms #:nodoc:
|
255
|
-
select_all("SELECT synonym_name, table_owner, table_name, db_link FROM all_synonyms WHERE owner = SYS_CONTEXT('userenv', 'current_schema')").collect do |row|
|
256
|
-
OracleEnhancedSynonymDefinition.new(oracle_downcase(row['synonym_name']),
|
257
|
-
oracle_downcase(row['table_owner']), oracle_downcase(row['table_name']), oracle_downcase(row['db_link']))
|
258
|
-
end
|
259
|
-
end
|
260
|
-
|
261
|
-
end
|
262
|
-
end
|
263
|
-
end
|
264
|
-
|
265
|
-
ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.class_eval do
|
266
|
-
include ActiveRecord::ConnectionAdapters::OracleEnhancedSchemaStatementsExt
|
267
|
-
end
|