oracle_enhanced 1.3.0.pre → 1.3.0.pre2
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.
- data/.gitignore +0 -1
- data/History.txt +1 -1
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/lib/active_record/connection_adapters/oracle_enhanced.rake +3 -2
- data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +223 -154
- data/lib/active_record/connection_adapters/oracle_enhanced_context_index.rb +227 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_core_ext.rb +1 -1
- data/lib/active_record/connection_adapters/oracle_enhanced_dirty.rb +1 -1
- data/lib/active_record/connection_adapters/oracle_enhanced_jdbc_connection.rb +3 -3
- data/lib/active_record/connection_adapters/oracle_enhanced_oci_connection.rb +6 -1
- data/lib/active_record/connection_adapters/oracle_enhanced_procedures.rb +3 -3
- data/lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb +2 -1
- data/lib/active_record/connection_adapters/oracle_enhanced_schema_dumper.rb +24 -9
- data/lib/active_record/connection_adapters/oracle_enhanced_version.rb +1 -1
- data/lib/{oracle_enhanced.rb → active_record/oracle_enhanced.rb} +0 -2
- data/oracle_enhanced.gemspec +7 -4
- data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +37 -10
- data/spec/active_record/connection_adapters/oracle_enhanced_adapter_structure_dumper_spec.rb +27 -20
- data/spec/active_record/connection_adapters/oracle_enhanced_context_index_spec.rb +292 -0
- data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +24 -28
- data/spec/active_record/connection_adapters/oracle_enhanced_dbms_output_spec.rb +13 -11
- data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +1 -1
- data/spec/active_record/connection_adapters/oracle_enhanced_procedures_spec.rb +70 -68
- data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +14 -1
- data/spec/spec_helper.rb +97 -24
- metadata +8 -5
@@ -107,31 +107,33 @@ module ActiveRecord
|
|
107
107
|
end
|
108
108
|
private :enhanced_write_lobs
|
109
109
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
110
|
+
# No such method in ActiveRecord 3.0
|
111
|
+
# and there is no real need to order by LOBs
|
112
|
+
# class << self
|
113
|
+
# # patch ORDER BY to work with LOBs
|
114
|
+
# def add_order_with_lobs!(sql, order, scope = :auto)
|
115
|
+
# if connection.is_a?(ConnectionAdapters::OracleEnhancedAdapter)
|
116
|
+
# order = connection.lob_order_by_expression(self, order) if order
|
117
|
+
#
|
118
|
+
# orig_scope = scope
|
119
|
+
# scope = scope(:find) if :auto == scope
|
120
|
+
# if scope
|
121
|
+
# new_scope_order = connection.lob_order_by_expression(self, scope[:order])
|
122
|
+
# if new_scope_order != scope[:order]
|
123
|
+
# scope = scope.merge(:order => new_scope_order)
|
124
|
+
# else
|
125
|
+
# scope = orig_scope
|
126
|
+
# end
|
127
|
+
# end
|
128
|
+
# end
|
129
|
+
# add_order_without_lobs!(sql, order, scope = :auto)
|
130
|
+
# end
|
131
|
+
# private :add_order_with_lobs!
|
132
|
+
# #:stopdoc:
|
133
|
+
# alias_method :add_order_without_lobs!, :add_order!
|
134
|
+
# alias_method :add_order!, :add_order_with_lobs!
|
135
|
+
# #:startdoc:
|
136
|
+
# end
|
135
137
|
|
136
138
|
# Get table comment from schema definition.
|
137
139
|
def self.table_comment
|
@@ -236,7 +238,7 @@ module ActiveRecord
|
|
236
238
|
if OracleEnhancedAdapter.string_to_time_format && dt=Date._strptime(string, OracleEnhancedAdapter.string_to_time_format)
|
237
239
|
return Time.mktime(*dt.values_at(:year, :mon, :mday, :hour, :min, :sec, :zone, :wday))
|
238
240
|
end
|
239
|
-
DateTime.strptime(string, OracleEnhancedAdapter.string_to_date_format)
|
241
|
+
DateTime.strptime(string, OracleEnhancedAdapter.string_to_date_format).to_date
|
240
242
|
end
|
241
243
|
|
242
244
|
end
|
@@ -460,7 +462,8 @@ module ActiveRecord
|
|
460
462
|
def quote_column_name(name) #:nodoc:
|
461
463
|
# camelCase column names need to be quoted; not that anyone using Oracle
|
462
464
|
# would really do this, but handling this case means we pass the test...
|
463
|
-
|
465
|
+
name = name.to_s
|
466
|
+
@quoted_column_names[name] ||= name =~ /^[a-z][a-z_0-9\$#]*$/ ? "\"#{name.upcase}\"" : "\"#{name}\""
|
464
467
|
end
|
465
468
|
|
466
469
|
# unescaped table name should start with letter and
|
@@ -474,12 +477,8 @@ module ActiveRecord
|
|
474
477
|
end
|
475
478
|
|
476
479
|
def quote_table_name(name) #:nodoc:
|
477
|
-
|
478
|
-
@quoted_table_names[name] ||=
|
479
|
-
name
|
480
|
-
else
|
481
|
-
"\"#{name}\""
|
482
|
-
end
|
480
|
+
name = name.to_s
|
481
|
+
@quoted_table_names[name] ||= name.split('.').map{|n| n.split('@').map{|m| quote_column_name(m)}.join('@')}.join('.')
|
483
482
|
end
|
484
483
|
|
485
484
|
def quote_string(s) #:nodoc:
|
@@ -603,7 +602,6 @@ module ActiveRecord
|
|
603
602
|
sql_with_returning = sql.dup << @connection.returning_clause(quote_column_name(pk))
|
604
603
|
# hack to pass additional "with_returning" option without changing argument list
|
605
604
|
sql_with_returning.instance_variable_set(:@with_returning, true)
|
606
|
-
clear_query_cache
|
607
605
|
execute(sql_with_returning, name)
|
608
606
|
end
|
609
607
|
protected :insert_sql
|
@@ -651,10 +649,12 @@ module ActiveRecord
|
|
651
649
|
def add_limit_offset!(sql, options) #:nodoc:
|
652
650
|
# added to_i for limit and offset to protect from SQL injection
|
653
651
|
offset = (options[:offset] || 0).to_i
|
654
|
-
|
655
|
-
|
656
|
-
|
652
|
+
limit = options[:limit]
|
653
|
+
limit = limit.is_a?(String) && limit.blank? ? nil : limit && limit.to_i
|
654
|
+
if limit && offset > 0
|
657
655
|
sql.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_ where rownum <= #{offset+limit}) where raw_rnum_ > #{offset}"
|
656
|
+
elsif limit
|
657
|
+
sql.replace "select * from (#{sql}) where rownum <= #{limit}"
|
658
658
|
elsif offset > 0
|
659
659
|
sql.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_) where raw_rnum_ > #{offset}"
|
660
660
|
end
|
@@ -755,8 +755,13 @@ module ActiveRecord
|
|
755
755
|
end
|
756
756
|
|
757
757
|
def tables(name = nil) #:nodoc:
|
758
|
-
|
759
|
-
|
758
|
+
select_values(
|
759
|
+
"select decode(table_name,upper(table_name),lower(table_name),table_name) from all_tables where owner = sys_context('userenv','session_user') and secondary='N'",
|
760
|
+
name)
|
761
|
+
end
|
762
|
+
|
763
|
+
def materialized_views #:nodoc:
|
764
|
+
select_values("select lower(mview_name) from all_mviews where owner = sys_context('userenv','session_user')")
|
760
765
|
end
|
761
766
|
|
762
767
|
cattr_accessor :all_schema_indexes #:nodoc:
|
@@ -768,14 +773,19 @@ module ActiveRecord
|
|
768
773
|
unless all_schema_indexes
|
769
774
|
default_tablespace_name = default_tablespace
|
770
775
|
result = select_all(<<-SQL)
|
771
|
-
SELECT
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
+
SELECT LOWER(i.table_name) AS table_name, LOWER(i.index_name) AS index_name, i.uniqueness,
|
777
|
+
i.index_type, i.ityp_owner, i.ityp_name, i.parameters,
|
778
|
+
LOWER(i.tablespace_name) AS tablespace_name,
|
779
|
+
LOWER(c.column_name) AS column_name, e.column_expression
|
780
|
+
FROM all_indexes#{db_link} i
|
781
|
+
JOIN all_ind_columns#{db_link} c ON c.index_name = i.index_name AND c.index_owner = i.owner
|
782
|
+
LEFT OUTER JOIN all_ind_expressions#{db_link} e ON e.index_name = i.index_name AND
|
783
|
+
e.index_owner = i.owner AND e.column_position = c.column_position
|
784
|
+
WHERE i.owner = '#{owner}'
|
776
785
|
AND i.table_owner = '#{owner}'
|
777
|
-
AND NOT EXISTS (SELECT uc.index_name FROM all_constraints uc
|
778
|
-
|
786
|
+
AND NOT EXISTS (SELECT uc.index_name FROM all_constraints uc
|
787
|
+
WHERE uc.index_name = i.index_name AND uc.owner = i.owner AND uc.constraint_type = 'P')
|
788
|
+
ORDER BY i.index_name, c.column_position
|
779
789
|
SQL
|
780
790
|
|
781
791
|
current_index = nil
|
@@ -785,11 +795,25 @@ module ActiveRecord
|
|
785
795
|
# have to keep track of indexes because above query returns dups
|
786
796
|
# there is probably a better query we could figure out
|
787
797
|
if current_index != row['index_name']
|
788
|
-
|
798
|
+
statement_parameters = nil
|
799
|
+
if row['index_type'] == 'DOMAIN' && row['ityp_owner'] == 'CTXSYS' && row['ityp_name'] == 'CONTEXT'
|
800
|
+
procedure_name = default_datastore_procedure(row['index_name'])
|
801
|
+
statement_parameters = select_value(<<-SQL)
|
802
|
+
SELECT SUBSTR(text,LENGTH('-- add_context_index_parameters ')+1)
|
803
|
+
FROM all_source#{db_link}
|
804
|
+
WHERE owner = '#{owner}'
|
805
|
+
AND name = '#{procedure_name.upcase}'
|
806
|
+
AND text LIKE '-- add_context_index_parameters %'
|
807
|
+
SQL
|
808
|
+
end
|
809
|
+
all_schema_indexes << OracleEnhancedIndexDefinition.new(row['table_name'], row['index_name'],
|
810
|
+
row['uniqueness'] == "UNIQUE", row['index_type'] == 'DOMAIN' ? "#{row['ityp_owner']}.#{row['ityp_name']}" : nil,
|
811
|
+
row['parameters'], statement_parameters,
|
789
812
|
row['tablespace_name'] == default_tablespace_name ? nil : row['tablespace_name'], [])
|
790
813
|
current_index = row['index_name']
|
791
814
|
end
|
792
|
-
all_schema_indexes.last.columns << (row['column_expression'].nil? ? row['column_name'] :
|
815
|
+
all_schema_indexes.last.columns << (row['column_expression'].nil? ? row['column_name'] :
|
816
|
+
row['column_expression'].gsub('"','').downcase)
|
793
817
|
end
|
794
818
|
end
|
795
819
|
|
@@ -852,7 +876,7 @@ module ActiveRecord
|
|
852
876
|
AND table_name = '#{desc_table_name}'
|
853
877
|
AND status = 'ENABLED'
|
854
878
|
SQL
|
855
|
-
select_value(pkt_sql) ? true : false
|
879
|
+
select_value(pkt_sql, 'Primary Key Trigger') ? true : false
|
856
880
|
end
|
857
881
|
|
858
882
|
##
|
@@ -866,8 +890,7 @@ module ActiveRecord
|
|
866
890
|
self.cache_columns = false
|
867
891
|
|
868
892
|
def columns(table_name, name = nil) #:nodoc:
|
869
|
-
|
870
|
-
if @@cache_columns && !(defined?(Rails) && Rails.configuration.cache_classes)
|
893
|
+
if @@cache_columns
|
871
894
|
@@columns_cache ||= {}
|
872
895
|
@@columns_cache[table_name] ||= columns_without_cache(table_name, name)
|
873
896
|
else
|
@@ -876,14 +899,15 @@ module ActiveRecord
|
|
876
899
|
end
|
877
900
|
|
878
901
|
def columns_without_cache(table_name, name = nil) #:nodoc:
|
902
|
+
table_name = table_name.to_s
|
879
903
|
# get ignored_columns by original table name
|
880
904
|
ignored_columns = ignored_table_columns(table_name)
|
881
905
|
|
882
906
|
(owner, desc_table_name, db_link) = @connection.describe(table_name)
|
883
907
|
|
884
|
-
|
885
|
-
|
886
|
-
|
908
|
+
@@do_not_prefetch_primary_key[table_name] =
|
909
|
+
!has_primary_key?(table_name, owner, desc_table_name, db_link) ||
|
910
|
+
has_primary_key_trigger?(table_name, owner, desc_table_name, db_link)
|
887
911
|
|
888
912
|
table_cols = <<-SQL
|
889
913
|
select column_name as name, data_type as sql_type, data_default, nullable,
|
@@ -929,11 +953,15 @@ module ActiveRecord
|
|
929
953
|
# used just in tests to clear column cache
|
930
954
|
def clear_columns_cache #:nodoc:
|
931
955
|
@@columns_cache = nil
|
956
|
+
@@pk_and_sequence_for_cache = nil
|
932
957
|
end
|
933
958
|
|
934
959
|
# used in migrations to clear column cache for specified table
|
935
960
|
def clear_table_columns_cache(table_name)
|
936
|
-
|
961
|
+
if @@cache_columns
|
962
|
+
@@columns_cache ||= {}
|
963
|
+
@@columns_cache[table_name.to_s] = nil
|
964
|
+
end
|
937
965
|
end
|
938
966
|
|
939
967
|
##
|
@@ -1051,11 +1079,7 @@ module ActiveRecord
|
|
1051
1079
|
if Hash === options # legacy support, since this param was a string
|
1052
1080
|
index_type = options[:unique] ? "UNIQUE" : ""
|
1053
1081
|
index_name = options[:name] || index_name
|
1054
|
-
tablespace =
|
1055
|
-
" TABLESPACE #{options[:tablespace]}"
|
1056
|
-
else
|
1057
|
-
""
|
1058
|
-
end
|
1082
|
+
tablespace = options[:tablespace] ? " TABLESPACE #{options[:tablespace]}" : ""
|
1059
1083
|
else
|
1060
1084
|
index_type = options
|
1061
1085
|
end
|
@@ -1072,18 +1096,21 @@ module ActiveRecord
|
|
1072
1096
|
# returned shortened index name if default is too large
|
1073
1097
|
def index_name(table_name, options) #:nodoc:
|
1074
1098
|
default_name = super(table_name, options)
|
1075
|
-
|
1099
|
+
# sometimes options can be String or Array with column names
|
1100
|
+
options = {} unless options.is_a?(Hash)
|
1101
|
+
identifier_max_length = options[:identifier_max_length] || IDENTIFIER_MAX_LENGTH
|
1102
|
+
return default_name if default_name.length <= identifier_max_length
|
1076
1103
|
|
1077
1104
|
# remove 'index', 'on' and 'and' keywords
|
1078
1105
|
shortened_name = "i_#{table_name}_#{Array(options[:column]) * '_'}"
|
1079
1106
|
|
1080
1107
|
# leave just first three letters from each word
|
1081
|
-
if shortened_name.length >
|
1108
|
+
if shortened_name.length > identifier_max_length
|
1082
1109
|
shortened_name = shortened_name.split('_').map{|w| w[0,3]}.join('_')
|
1083
1110
|
end
|
1084
1111
|
# generate unique name using hash function
|
1085
|
-
if shortened_name.length >
|
1086
|
-
shortened_name = 'i'+Digest::SHA1.hexdigest(default_name)[0,
|
1112
|
+
if shortened_name.length > identifier_max_length
|
1113
|
+
shortened_name = 'i'+Digest::SHA1.hexdigest(default_name)[0,identifier_max_length-1]
|
1087
1114
|
end
|
1088
1115
|
@logger.warn "#{adapter_name} shortened default index name #{default_name} to #{shortened_name}" if @logger
|
1089
1116
|
shortened_name
|
@@ -1182,15 +1209,24 @@ module ActiveRecord
|
|
1182
1209
|
|
1183
1210
|
# Find a table's primary key and sequence.
|
1184
1211
|
# *Note*: Only primary key is implemented - sequence will be nil.
|
1185
|
-
def pk_and_sequence_for(table_name) #:nodoc:
|
1186
|
-
|
1212
|
+
def pk_and_sequence_for(table_name, owner=nil, desc_table_name=nil, db_link=nil) #:nodoc:
|
1213
|
+
if @@cache_columns
|
1214
|
+
@@pk_and_sequence_for_cache ||= {}
|
1215
|
+
@@pk_and_sequence_for_cache[table_name] ||= pk_and_sequence_for_without_cache(table_name, owner, desc_table_name, db_link)
|
1216
|
+
else
|
1217
|
+
pk_and_sequence_for_without_cache(table_name, owner, desc_table_name, db_link)
|
1218
|
+
end
|
1219
|
+
end
|
1220
|
+
|
1221
|
+
def pk_and_sequence_for_without_cache(table_name, owner=nil, desc_table_name=nil, db_link=nil) #:nodoc:
|
1222
|
+
(owner, desc_table_name, db_link) = @connection.describe(table_name) unless owner
|
1187
1223
|
|
1188
|
-
# changed
|
1224
|
+
# changed back from user_constraints to all_constraints for consistency
|
1189
1225
|
pks = select_values(<<-SQL, 'Primary Key')
|
1190
1226
|
select cc.column_name
|
1191
|
-
from
|
1227
|
+
from all_constraints#{db_link} c, all_cons_columns#{db_link} cc
|
1192
1228
|
where c.owner = '#{owner}'
|
1193
|
-
and c.table_name = '#{
|
1229
|
+
and c.table_name = '#{desc_table_name}'
|
1194
1230
|
and c.constraint_type = 'P'
|
1195
1231
|
and cc.owner = c.owner
|
1196
1232
|
and cc.constraint_name = c.constraint_name
|
@@ -1200,16 +1236,29 @@ module ActiveRecord
|
|
1200
1236
|
pks.size == 1 ? [oracle_downcase(pks.first), nil] : nil
|
1201
1237
|
end
|
1202
1238
|
|
1239
|
+
# Returns just a table's primary key
|
1240
|
+
def primary_key(table_name)
|
1241
|
+
pk_and_sequence = pk_and_sequence_for(table_name)
|
1242
|
+
pk_and_sequence && pk_and_sequence.first
|
1243
|
+
end
|
1244
|
+
|
1245
|
+
def has_primary_key?(table_name, owner=nil, desc_table_name=nil, db_link=nil) #:nodoc:
|
1246
|
+
!pk_and_sequence_for(table_name, owner, desc_table_name, db_link).nil?
|
1247
|
+
end
|
1248
|
+
|
1249
|
+
# Statements separator used in structure dump to allow loading of structure dump also with SQL*Plus
|
1250
|
+
STATEMENT_TOKEN = "\n\n/\n\n"
|
1251
|
+
|
1203
1252
|
def structure_dump #:nodoc:
|
1204
|
-
|
1205
|
-
|
1253
|
+
structure = select_values("select sequence_name from user_sequences order by 1").map do |seq|
|
1254
|
+
"CREATE SEQUENCE \"#{seq}\""
|
1206
1255
|
end
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1256
|
+
select_values("select table_name from all_tables t
|
1257
|
+
where owner = sys_context('userenv','session_user') and secondary='N'
|
1258
|
+
and not exists (select mv.mview_name from all_mviews mv where mv.owner = t.owner and mv.mview_name = t.table_name)
|
1259
|
+
order by 1").each do |table_name|
|
1211
1260
|
virtual_columns = virtual_columns_for(table_name)
|
1212
|
-
ddl = "
|
1261
|
+
ddl = "CREATE#{ ' GLOBAL TEMPORARY' if temporary_table?(table_name)} TABLE \"#{table_name}\" (\n"
|
1213
1262
|
cols = select_all(%Q{
|
1214
1263
|
select column_name, data_type, data_length, char_used, char_length, data_precision, data_scale, data_default, nullable
|
1215
1264
|
from user_tab_columns
|
@@ -1224,15 +1273,16 @@ module ActiveRecord
|
|
1224
1273
|
end
|
1225
1274
|
ddl << cols.join(",\n ")
|
1226
1275
|
ddl << structure_dump_constraints(table_name)
|
1227
|
-
ddl << "\n)
|
1276
|
+
ddl << "\n)"
|
1228
1277
|
structure << ddl
|
1229
1278
|
structure << structure_dump_indexes(table_name)
|
1230
1279
|
end
|
1280
|
+
|
1281
|
+
join_with_statement_token(structure) << structure_dump_fk_constraints
|
1231
1282
|
end
|
1232
|
-
|
1233
|
-
def
|
1234
|
-
|
1235
|
-
col = "#{column['column_name'].downcase} #{column['data_type'].downcase}"
|
1283
|
+
|
1284
|
+
def structure_dump_column(column) #:nodoc:
|
1285
|
+
col = "\"#{column['column_name']}\" #{column['data_type']}"
|
1236
1286
|
if column['data_type'] =='NUMBER' and !column['data_precision'].nil?
|
1237
1287
|
col << "(#{column['data_precision'].to_i}"
|
1238
1288
|
col << ",#{column['data_scale'].to_i}" if !column['data_scale'].nil?
|
@@ -1241,11 +1291,14 @@ module ActiveRecord
|
|
1241
1291
|
length = column['char_used'] == 'C' ? column['char_length'].to_i : column['data_length'].to_i
|
1242
1292
|
col << "(#{length})"
|
1243
1293
|
end
|
1244
|
-
col << "
|
1294
|
+
col << " DEFAULT #{column['data_default']}" if !column['data_default'].nil?
|
1295
|
+
col << ' NOT NULL' if column['nullable'] == 'N'
|
1296
|
+
col
|
1245
1297
|
end
|
1246
|
-
|
1247
|
-
def
|
1248
|
-
|
1298
|
+
|
1299
|
+
def structure_dump_virtual_column(column, data_default) #:nodoc:
|
1300
|
+
data_default = data_default.gsub(/"/, '')
|
1301
|
+
col = "\"#{column['column_name']}\" #{column['data_type']}"
|
1249
1302
|
if column['data_type'] =='NUMBER' and !column['data_precision'].nil?
|
1250
1303
|
col << "(#{column['data_precision'].to_i}"
|
1251
1304
|
col << ",#{column['data_scale'].to_i}" if !column['data_scale'].nil?
|
@@ -1254,16 +1307,14 @@ module ActiveRecord
|
|
1254
1307
|
length = column['char_used'] == 'C' ? column['char_length'].to_i : column['data_length'].to_i
|
1255
1308
|
col << "(#{length})"
|
1256
1309
|
end
|
1257
|
-
col << "
|
1258
|
-
col << ' not null' if column['nullable'] == 'N'
|
1259
|
-
col
|
1310
|
+
col << " GENERATED ALWAYS AS (#{data_default}) VIRTUAL"
|
1260
1311
|
end
|
1261
|
-
|
1312
|
+
|
1262
1313
|
def structure_dump_constraints(table) #:nodoc:
|
1263
1314
|
out = [structure_dump_primary_key(table), structure_dump_unique_keys(table)].flatten.compact
|
1264
1315
|
out.length > 0 ? ",\n#{out.join(",\n")}" : ''
|
1265
1316
|
end
|
1266
|
-
|
1317
|
+
|
1267
1318
|
def structure_dump_primary_key(table) #:nodoc:
|
1268
1319
|
opts = {:name => '', :cols => []}
|
1269
1320
|
pks = select_all(<<-SQL, "Primary Keys")
|
@@ -1281,7 +1332,7 @@ module ActiveRecord
|
|
1281
1332
|
end
|
1282
1333
|
opts[:cols].length > 0 ? " CONSTRAINT #{opts[:name]} PRIMARY KEY (#{opts[:cols].join(',')})" : nil
|
1283
1334
|
end
|
1284
|
-
|
1335
|
+
|
1285
1336
|
def structure_dump_unique_keys(table) #:nodoc:
|
1286
1337
|
keys = {}
|
1287
1338
|
uks = select_all(<<-SQL, "Primary Keys")
|
@@ -1301,7 +1352,23 @@ module ActiveRecord
|
|
1301
1352
|
" CONSTRAINT #{k} UNIQUE (#{v.join(',')})"
|
1302
1353
|
end
|
1303
1354
|
end
|
1304
|
-
|
1355
|
+
|
1356
|
+
def structure_dump_indexes(table_name) #:nodoc:
|
1357
|
+
indexes(table_name).map do |options|
|
1358
|
+
column_names = options[:columns]
|
1359
|
+
options = {:name => options[:name], :unique => options[:unique]}
|
1360
|
+
index_name = index_name(table_name, :column => column_names)
|
1361
|
+
if Hash === options # legacy support, since this param was a string
|
1362
|
+
index_type = options[:unique] ? "UNIQUE" : ""
|
1363
|
+
index_name = options[:name] || index_name
|
1364
|
+
else
|
1365
|
+
index_type = options
|
1366
|
+
end
|
1367
|
+
quoted_column_names = column_names.map { |e| quote_column_name(e) }.join(", ")
|
1368
|
+
"CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})"
|
1369
|
+
end
|
1370
|
+
end
|
1371
|
+
|
1305
1372
|
def structure_dump_fk_constraints #:nodoc:
|
1306
1373
|
fks = select_all("select table_name from all_tables where owner = sys_context('userenv','session_user') order by 1").map do |table|
|
1307
1374
|
if respond_to?(:foreign_keys) && (foreign_keys = foreign_keys(table["table_name"])).any?
|
@@ -1312,18 +1379,24 @@ module ActiveRecord
|
|
1312
1379
|
sql << "#{foreign_key_definition(fk.to_table, fk.options)}"
|
1313
1380
|
end
|
1314
1381
|
end
|
1315
|
-
end.flatten.compact
|
1316
|
-
fks
|
1382
|
+
end.flatten.compact
|
1383
|
+
join_with_statement_token(fks)
|
1317
1384
|
end
|
1318
|
-
|
1385
|
+
|
1386
|
+
def dump_schema_information #:nodoc:
|
1387
|
+
sm_table = ActiveRecord::Migrator.schema_migrations_table_name
|
1388
|
+
migrated = select_values("SELECT version FROM #{sm_table}")
|
1389
|
+
join_with_statement_token(migrated.map{|v| "INSERT INTO #{sm_table} (version) VALUES ('#{v}')" })
|
1390
|
+
end
|
1391
|
+
|
1319
1392
|
# Extract all stored procedures, packages, synonyms and views.
|
1320
1393
|
def structure_dump_db_stored_code #:nodoc:
|
1321
|
-
structure =
|
1322
|
-
select_all("select distinct name, type
|
1323
|
-
from all_source
|
1324
|
-
where type in ('PROCEDURE', 'PACKAGE', 'PACKAGE BODY', 'FUNCTION', 'TRIGGER', 'TYPE')
|
1394
|
+
structure = []
|
1395
|
+
select_all("select distinct name, type
|
1396
|
+
from all_source
|
1397
|
+
where type in ('PROCEDURE', 'PACKAGE', 'PACKAGE BODY', 'FUNCTION', 'TRIGGER', 'TYPE')
|
1325
1398
|
and owner = sys_context('userenv','session_user') order by type").each do |source|
|
1326
|
-
ddl = "
|
1399
|
+
ddl = "CREATE OR REPLACE \n"
|
1327
1400
|
lines = select_all(%Q{
|
1328
1401
|
select text
|
1329
1402
|
from all_source
|
@@ -1332,79 +1405,62 @@ module ActiveRecord
|
|
1332
1405
|
and owner = sys_context('userenv','session_user')
|
1333
1406
|
order by line
|
1334
1407
|
}).map do |row|
|
1335
|
-
ddl << row['text']
|
1408
|
+
ddl << row['text']
|
1336
1409
|
end
|
1337
|
-
ddl << ";" unless ddl.strip
|
1338
|
-
structure << ddl
|
1410
|
+
ddl << ";" unless ddl.strip[-1,1] == ";"
|
1411
|
+
structure << ddl
|
1339
1412
|
end
|
1340
1413
|
|
1341
1414
|
# export views
|
1342
1415
|
select_all("select view_name, text from user_views").each do |view|
|
1343
|
-
|
1344
|
-
# any views with empty lines will cause OCI to barf when loading. remove blank lines =/
|
1345
|
-
ddl << view['text'].gsub(/^\n/, '')
|
1346
|
-
structure << ddl << STATEMENT_TOKEN
|
1416
|
+
structure << "CREATE OR REPLACE VIEW #{view['view_name']} AS\n #{view['text']}"
|
1347
1417
|
end
|
1348
1418
|
|
1349
|
-
# export synonyms
|
1419
|
+
# export synonyms
|
1350
1420
|
select_all("select owner, synonym_name, table_name, table_owner
|
1351
1421
|
from all_synonyms
|
1352
1422
|
where owner = sys_context('userenv','session_user') ").each do |synonym|
|
1353
|
-
|
1354
|
-
structure <<
|
1423
|
+
structure << "CREATE OR REPLACE #{synonym['owner'] == 'PUBLIC' ? 'PUBLIC' : '' } SYNONYM #{synonym['synonym_name']}"
|
1424
|
+
structure << " FOR #{synonym['table_owner']}.#{synonym['table_name']}"
|
1355
1425
|
end
|
1356
1426
|
|
1357
|
-
structure
|
1427
|
+
join_with_statement_token(structure)
|
1358
1428
|
end
|
1359
1429
|
|
1360
|
-
def structure_dump_indexes(table_name) #:nodoc:
|
1361
|
-
statements = indexes(table_name).map do |options|
|
1362
|
-
#def add_index(table_name, column_name, options = {})
|
1363
|
-
column_names = options[:columns]
|
1364
|
-
options = {:name => options[:name], :unique => options[:unique]}
|
1365
|
-
index_name = index_name(table_name, :column => column_names)
|
1366
|
-
if Hash === options # legacy support, since this param was a string
|
1367
|
-
index_type = options[:unique] ? "UNIQUE" : ""
|
1368
|
-
index_name = options[:name] || index_name
|
1369
|
-
else
|
1370
|
-
index_type = options
|
1371
|
-
end
|
1372
|
-
quoted_column_names = column_names.map { |e| quote_column_name(e) }.join(", ")
|
1373
|
-
"CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})"
|
1374
|
-
end
|
1375
|
-
statements.length > 0 ? "#{statements.join(STATEMENT_TOKEN)}#{STATEMENT_TOKEN}" : ''
|
1376
|
-
end
|
1377
|
-
|
1378
1430
|
def structure_drop #:nodoc:
|
1379
|
-
|
1380
|
-
|
1431
|
+
statements = select_values("select sequence_name from user_sequences order by 1").map do |seq|
|
1432
|
+
"DROP SEQUENCE \"#{seq}\""
|
1381
1433
|
end
|
1382
|
-
|
1383
|
-
|
1384
|
-
|
1385
|
-
|
1434
|
+
select_values("select table_name from all_tables t
|
1435
|
+
where owner = sys_context('userenv','session_user') and secondary='N'
|
1436
|
+
and not exists (select mv.mview_name from all_mviews mv where mv.owner = t.owner and mv.mview_name = t.table_name)
|
1437
|
+
order by 1").each do |table|
|
1438
|
+
statements << "DROP TABLE \"#{table}\" CASCADE CONSTRAINTS"
|
1386
1439
|
end
|
1440
|
+
join_with_statement_token(statements)
|
1387
1441
|
end
|
1388
|
-
|
1442
|
+
|
1389
1443
|
def temp_table_drop #:nodoc:
|
1390
|
-
|
1391
|
-
|
1392
|
-
|
1393
|
-
|
1444
|
+
join_with_statement_token(select_values(
|
1445
|
+
"select table_name from all_tables
|
1446
|
+
where owner = sys_context('userenv','session_user') and secondary='N' and temporary = 'Y' order by 1").map do |table|
|
1447
|
+
"DROP TABLE \"#{table}\" CASCADE CONSTRAINTS"
|
1448
|
+
end)
|
1394
1449
|
end
|
1395
|
-
|
1450
|
+
|
1396
1451
|
def full_drop(preserve_tables=false) #:nodoc:
|
1397
1452
|
s = preserve_tables ? [] : [structure_drop]
|
1398
1453
|
s << temp_table_drop if preserve_tables
|
1399
1454
|
s << drop_sql_for_feature("view")
|
1455
|
+
s << drop_sql_for_feature("materialized view")
|
1400
1456
|
s << drop_sql_for_feature("synonym")
|
1401
1457
|
s << drop_sql_for_feature("type")
|
1402
1458
|
s << drop_sql_for_object("package")
|
1403
1459
|
s << drop_sql_for_object("function")
|
1404
1460
|
s << drop_sql_for_object("procedure")
|
1405
|
-
s.join
|
1461
|
+
s.join
|
1406
1462
|
end
|
1407
|
-
|
1463
|
+
|
1408
1464
|
def add_column_options!(sql, options) #:nodoc:
|
1409
1465
|
type = options[:type] || ((column = options[:column]) && column.type)
|
1410
1466
|
type = type && type.to_sym
|
@@ -1452,9 +1508,6 @@ module ActiveRecord
|
|
1452
1508
|
select_value("select temporary from user_tables where table_name = '#{table_name.upcase}'") == 'Y'
|
1453
1509
|
end
|
1454
1510
|
|
1455
|
-
# statements separator used in structure dump
|
1456
|
-
STATEMENT_TOKEN = "\n\n--@@@--\n\n"
|
1457
|
-
|
1458
1511
|
# ORDER BY clause for the passed order option.
|
1459
1512
|
#
|
1460
1513
|
# Uses column aliases as defined by #distinct.
|
@@ -1530,8 +1583,8 @@ module ActiveRecord
|
|
1530
1583
|
"#{table_name.to_s[0,IDENTIFIER_MAX_LENGTH-4]}_pkt"
|
1531
1584
|
end
|
1532
1585
|
|
1533
|
-
def compress_lines(string,
|
1534
|
-
string.split($/).map { |line| line.strip }.join(
|
1586
|
+
def compress_lines(string, join_with = "\n")
|
1587
|
+
string.split($/).map { |line| line.strip }.join(join_with)
|
1535
1588
|
end
|
1536
1589
|
|
1537
1590
|
# virtual columns are an 11g feature. This returns [] if feature is not
|
@@ -1550,15 +1603,28 @@ module ActiveRecord
|
|
1550
1603
|
[]
|
1551
1604
|
end
|
1552
1605
|
end
|
1553
|
-
|
1606
|
+
|
1554
1607
|
def drop_sql_for_feature(type)
|
1555
|
-
|
1608
|
+
short_type = type == 'materialized view' ? 'mview' : type
|
1609
|
+
join_with_statement_token(
|
1610
|
+
select_values("select #{short_type}_name from user_#{short_type.tableize}").map do |name|
|
1611
|
+
"DROP #{type.upcase} \"#{name}\""
|
1612
|
+
end)
|
1556
1613
|
end
|
1557
|
-
|
1614
|
+
|
1558
1615
|
def drop_sql_for_object(type)
|
1559
|
-
|
1616
|
+
join_with_statement_token(
|
1617
|
+
select_values("select object_name from user_objects where object_type = '#{type.upcase}'").map do |name|
|
1618
|
+
"DROP #{type.upcase} \"#{name}\""
|
1619
|
+
end)
|
1560
1620
|
end
|
1561
|
-
|
1621
|
+
|
1622
|
+
def join_with_statement_token(array)
|
1623
|
+
string = array.join(STATEMENT_TOKEN)
|
1624
|
+
string << STATEMENT_TOKEN unless string.blank?
|
1625
|
+
string
|
1626
|
+
end
|
1627
|
+
|
1562
1628
|
public
|
1563
1629
|
# DBMS_OUTPUT =============================================
|
1564
1630
|
#
|
@@ -1641,7 +1707,7 @@ require 'active_record/connection_adapters/oracle_enhanced_dirty'
|
|
1641
1707
|
begin
|
1642
1708
|
require 'active_record/connection_adapters/oracle_enhanced_tasks'
|
1643
1709
|
rescue LoadError
|
1644
|
-
end if defined?(
|
1710
|
+
end if defined?(RAILS_ROOT)
|
1645
1711
|
|
1646
1712
|
# Handles quoting of oracle reserved words
|
1647
1713
|
require 'active_record/connection_adapters/oracle_enhanced_reserved_words'
|
@@ -1655,6 +1721,9 @@ require 'active_record/connection_adapters/oracle_enhanced_schema_statements_ext
|
|
1655
1721
|
# Extensions for schema definition
|
1656
1722
|
require 'active_record/connection_adapters/oracle_enhanced_schema_definitions'
|
1657
1723
|
|
1724
|
+
# Extensions for context index definition
|
1725
|
+
require 'active_record/connection_adapters/oracle_enhanced_context_index'
|
1726
|
+
|
1658
1727
|
# Add BigDecimal#to_d, Fixnum#to_d and Bignum#to_d methods if not already present
|
1659
1728
|
require 'active_record/connection_adapters/oracle_enhanced_core_ext'
|
1660
1729
|
|