]> source.dussan.org Git - sonarqube.git/blob
fff24546ec2a9b1b329872de5089c1bb6286135d
[sonarqube.git] /
1 module ActiveRecord #:nodoc:
2   module ConnectionAdapters #:nodoc:
3     module OracleEnhancedSchemaDumper #:nodoc:
4
5       def self.included(base) #:nodoc:
6         base.class_eval do
7           private
8           alias_method_chain :tables, :oracle_enhanced
9           alias_method_chain :indexes, :oracle_enhanced
10         end
11       end
12
13       private
14
15       def ignore_table?(table)
16         [ActiveRecord::Migrator.proper_table_name('schema_migrations'), ignore_tables].flatten.any? do |ignored|
17           case ignored
18           when String; table == ignored
19           when Regexp; table =~ ignored
20           else
21             raise StandardError, 'ActiveRecord::SchemaDumper.ignore_tables accepts an array of String and / or Regexp values.'
22           end
23         end
24       end
25
26       def tables_with_oracle_enhanced(stream)
27         return tables_without_oracle_enhanced(stream) unless @connection.respond_to?(:materialized_views)
28         # do not include materialized views in schema dump - they should be created separately after schema creation
29         sorted_tables = (@connection.tables - @connection.materialized_views).sort
30         sorted_tables.each do |tbl|
31           # add table prefix or suffix for schema_migrations
32           next if ignore_table? tbl
33           # change table name inspect method
34           tbl.extend TableInspect
35           oracle_enhanced_table(tbl, stream)
36           # add primary key trigger if table has it
37           primary_key_trigger(tbl, stream)
38         end
39         # following table definitions
40         # add foreign keys if table has them
41         sorted_tables.each do |tbl|
42           next if ignore_table? tbl
43           foreign_keys(tbl, stream)
44         end
45
46         # add synonyms in local schema
47         synonyms(stream)
48       end
49
50       def primary_key_trigger(table_name, stream)
51         if @connection.respond_to?(:has_primary_key_trigger?) && @connection.has_primary_key_trigger?(table_name)
52           pk, pk_seq = @connection.pk_and_sequence_for(table_name)
53           stream.print "  add_primary_key_trigger #{table_name.inspect}"
54           stream.print ", :primary_key => \"#{pk}\"" if pk != 'id'
55           stream.print "\n\n"
56         end
57       end
58
59       def foreign_keys(table_name, stream)
60         if @connection.respond_to?(:foreign_keys) && (foreign_keys = @connection.foreign_keys(table_name)).any?
61           add_foreign_key_statements = foreign_keys.map do |foreign_key|
62             statement_parts = [ ('add_foreign_key ' + foreign_key.from_table.inspect) ]
63             statement_parts << foreign_key.to_table.inspect
64             
65             if foreign_key.options[:columns].size == 1
66               column = foreign_key.options[:columns].first
67               if column != "#{foreign_key.to_table.singularize}_id"
68                 statement_parts << (':column => ' + column.inspect)
69               end
70               
71               if foreign_key.options[:references].first != 'id'
72                 statement_parts << (':primary_key => ' + foreign_key.options[:primary_key].inspect)
73               end
74             else
75               statement_parts << (':columns => ' + foreign_key.options[:columns].inspect)
76             end
77
78             statement_parts << (':name => ' + foreign_key.options[:name].inspect)
79             
80             unless foreign_key.options[:dependent].blank?
81               statement_parts << (':dependent => ' + foreign_key.options[:dependent].inspect)
82             end
83
84             '  ' + statement_parts.join(', ')
85           end
86
87           stream.puts add_foreign_key_statements.sort.join("\n")
88           stream.puts
89         end
90       end
91
92       def synonyms(stream)
93         if @connection.respond_to?(:synonyms)
94           syns = @connection.synonyms
95           syns.each do |syn|
96                         next if ignore_table? syn.name
97             table_name = syn.table_name
98             table_name = "#{syn.table_owner}.#{table_name}" if syn.table_owner
99             table_name = "#{table_name}@#{syn.db_link}" if syn.db_link
100             stream.print "  add_synonym #{syn.name.inspect}, #{table_name.inspect}, :force => true"
101             stream.puts
102           end
103           stream.puts unless syns.empty?
104         end
105       end
106
107       def indexes_with_oracle_enhanced(table, stream)
108         # return original method if not using oracle_enhanced
109         if (rails_env = defined?(Rails.env) ? Rails.env : (defined?(RAILS_ENV) ? RAILS_ENV : nil)) &&
110               ActiveRecord::Base.configurations[rails_env] &&
111               ActiveRecord::Base.configurations[rails_env]['adapter'] != 'oracle_enhanced'
112           return indexes_without_oracle_enhanced(table, stream)
113         end
114         if (indexes = @connection.indexes(table)).any?
115           add_index_statements = indexes.map do |index|
116             case index.type
117             when nil
118               # use table.inspect as it will remove prefix and suffix
119               statement_parts = [ ('add_index ' + table.inspect) ]
120               statement_parts << index.columns.inspect
121               statement_parts << (':name => ' + index.name.inspect)
122               statement_parts << ':unique => true' if index.unique
123               statement_parts << ':tablespace => ' + index.tablespace.inspect if index.tablespace
124             when 'CTXSYS.CONTEXT'
125               if index.statement_parameters
126                 statement_parts = [ ('add_context_index ' + table.inspect) ]
127                 statement_parts << index.statement_parameters
128               else
129                 statement_parts = [ ('add_context_index ' + table.inspect) ]
130                 statement_parts << index.columns.inspect
131                 statement_parts << (':name => ' + index.name.inspect)
132               end
133             else
134               # unrecognized index type
135               statement_parts = ["# unrecognized index #{index.name.inspect} with type #{index.type.inspect}"]
136             end
137             '  ' + statement_parts.join(', ')
138           end
139
140           stream.puts add_index_statements.sort.join("\n")
141           stream.puts
142         end
143       end
144
145       def oracle_enhanced_table(table, stream)
146         columns = @connection.columns(table)
147         begin
148           tbl = StringIO.new
149
150           # first dump primary key column
151           if @connection.respond_to?(:pk_and_sequence_for)
152             pk, pk_seq = @connection.pk_and_sequence_for(table)
153           elsif @connection.respond_to?(:primary_key)
154             pk = @connection.primary_key(table)
155           end
156           
157           tbl.print "  create_table #{table.inspect}"
158           
159           # addition to make temporary option work
160           tbl.print ", :temporary => true" if @connection.temporary_table?(table)
161           
162           if columns.detect { |c| c.name == pk }
163             if pk != 'id'
164               tbl.print %Q(, :primary_key => "#{pk}")
165             end
166           else
167             tbl.print ", :id => false"
168           end
169           tbl.print ", :force => true"
170           tbl.puts " do |t|"
171
172           # then dump all non-primary key columns
173           column_specs = columns.map do |column|
174             raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" if @types[column.type].nil?
175             next if column.name == pk
176             spec = {}
177             spec[:name]      = column.name.inspect
178             spec[:type]      = column.virtual? ? 'virtual' : column.type.to_s
179             spec[:limit]     = column.limit.inspect if column.limit != @types[column.type][:limit] && column.type != :decimal
180             spec[:precision] = column.precision.inspect if !column.precision.nil?
181             spec[:scale]     = column.scale.inspect if !column.scale.nil?
182             spec[:null]      = 'false' if !column.null
183             spec[:default]   = column.virtual_column_data_default if column.virtual?
184             spec[:default] ||= default_string(column.default) if column.has_default?
185             (spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k.inspect} => ")}
186             spec
187           end.compact
188
189           # find all migration keys used in this table
190           keys = [:name, :limit, :precision, :scale, :default, :null] & column_specs.map(&:keys).flatten
191
192           # figure out the lengths for each column based on above keys
193           lengths = keys.map{ |key| column_specs.map{ |spec| spec[key] ? spec[key].length + 2 : 0 }.max }
194
195           # the string we're going to sprintf our values against, with standardized column widths
196           format_string = lengths.map{ |len| "%-#{len}s" }
197
198           # find the max length for the 'type' column, which is special
199           type_length = column_specs.map{ |column| column[:type].length }.max
200
201           # add column type definition to our format string
202           format_string.unshift "    t.%-#{type_length}s "
203
204           format_string *= ''
205
206           column_specs.each do |colspec|
207             values = keys.zip(lengths).map{ |key, len| colspec.key?(key) ? colspec[key] + ", " : " " * len }
208             values.unshift colspec[:type]
209             tbl.print((format_string % values).gsub(/,\s*$/, ''))
210             tbl.puts
211           end
212
213           tbl.puts "  end"
214           tbl.puts
215           
216           indexes(table, tbl)
217
218           tbl.rewind
219           stream.print tbl.read
220         rescue => e
221           stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}"
222           stream.puts "#   #{e.message}"
223           stream.puts
224         end
225         
226         stream
227       end
228       
229       
230       # remove table name prefix and suffix when doing #inspect (which is used in tables method)
231       module TableInspect #:nodoc:
232         def inspect
233           remove_prefix_and_suffix(self)
234         end
235         
236         private
237         def remove_prefix_and_suffix(table_name)
238           if table_name =~ /\A#{ActiveRecord::Base.table_name_prefix.to_s.gsub('$','\$')}(.*)#{ActiveRecord::Base.table_name_suffix.to_s.gsub('$','\$')}\Z/
239             "\"#{$1}\""
240           else
241             "\"#{table_name}\""
242           end
243         end
244       end
245
246     end
247   end
248 end
249
250 ActiveRecord::SchemaDumper.class_eval do
251   include ActiveRecord::ConnectionAdapters::OracleEnhancedSchemaDumper
252 end