pmacs-activerecord-oracle_enhanced-adapter 1.4.2.rc1

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.
Files changed (46) hide show
  1. data/.rspec +2 -0
  2. data/Gemfile +52 -0
  3. data/History.md +284 -0
  4. data/License.txt +20 -0
  5. data/README.md +403 -0
  6. data/RUNNING_TESTS.md +45 -0
  7. data/Rakefile +59 -0
  8. data/VERSION +1 -0
  9. data/lib/active_record/connection_adapters/emulation/oracle_adapter.rb +5 -0
  10. data/lib/active_record/connection_adapters/oracle_enhanced.rake +105 -0
  11. data/lib/active_record/connection_adapters/oracle_enhanced_activerecord_patches.rb +41 -0
  12. data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +1408 -0
  13. data/lib/active_record/connection_adapters/oracle_enhanced_base_ext.rb +118 -0
  14. data/lib/active_record/connection_adapters/oracle_enhanced_column.rb +141 -0
  15. data/lib/active_record/connection_adapters/oracle_enhanced_connection.rb +135 -0
  16. data/lib/active_record/connection_adapters/oracle_enhanced_context_index.rb +359 -0
  17. data/lib/active_record/connection_adapters/oracle_enhanced_core_ext.rb +25 -0
  18. data/lib/active_record/connection_adapters/oracle_enhanced_cpk.rb +21 -0
  19. data/lib/active_record/connection_adapters/oracle_enhanced_dirty.rb +44 -0
  20. data/lib/active_record/connection_adapters/oracle_enhanced_jdbc_connection.rb +565 -0
  21. data/lib/active_record/connection_adapters/oracle_enhanced_oci_connection.rb +491 -0
  22. data/lib/active_record/connection_adapters/oracle_enhanced_procedures.rb +260 -0
  23. data/lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb +231 -0
  24. data/lib/active_record/connection_adapters/oracle_enhanced_schema_dumper.rb +257 -0
  25. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb +397 -0
  26. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements_ext.rb +265 -0
  27. data/lib/active_record/connection_adapters/oracle_enhanced_structure_dump.rb +294 -0
  28. data/lib/active_record/connection_adapters/oracle_enhanced_tasks.rb +17 -0
  29. data/lib/active_record/connection_adapters/oracle_enhanced_version.rb +1 -0
  30. data/lib/pmacs-activerecord-oracle_enhanced-adapter.rb +25 -0
  31. data/pmacs-activerecord-oracle_enhanced-adapter.gemspec +131 -0
  32. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +778 -0
  33. data/spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb +332 -0
  34. data/spec/active_record/connection_adapters/oracle_enhanced_context_index_spec.rb +427 -0
  35. data/spec/active_record/connection_adapters/oracle_enhanced_core_ext_spec.rb +19 -0
  36. data/spec/active_record/connection_adapters/oracle_enhanced_cpk_spec.rb +113 -0
  37. data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +1376 -0
  38. data/spec/active_record/connection_adapters/oracle_enhanced_dbms_output_spec.rb +69 -0
  39. data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +141 -0
  40. data/spec/active_record/connection_adapters/oracle_enhanced_emulate_oracle_adapter_spec.rb +25 -0
  41. data/spec/active_record/connection_adapters/oracle_enhanced_procedures_spec.rb +378 -0
  42. data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +438 -0
  43. data/spec/active_record/connection_adapters/oracle_enhanced_schema_statements_spec.rb +1280 -0
  44. data/spec/active_record/connection_adapters/oracle_enhanced_structure_dump_spec.rb +339 -0
  45. data/spec/spec_helper.rb +187 -0
  46. metadata +302 -0
@@ -0,0 +1,265 @@
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
+ sql = "FOREIGN KEY (#{columns_sql}) REFERENCES #{quote_table_name(to_table)}(#{references_sql})"
108
+
109
+ case options[:dependent]
110
+ when :nullify
111
+ sql << " ON DELETE SET NULL"
112
+ when :delete
113
+ sql << " ON DELETE CASCADE"
114
+ end
115
+ sql
116
+ end
117
+
118
+ # Remove the given foreign key from the table.
119
+ #
120
+ # ===== Examples
121
+ # ====== Remove the suppliers_company_id_fk in the suppliers table.
122
+ # remove_foreign_key :suppliers, :companies
123
+ # ====== Remove the foreign key named accounts_branch_id_fk in the accounts table.
124
+ # remove_foreign_key :accounts, :column => :branch_id
125
+ # ====== Remove the foreign key named party_foreign_key in the accounts table.
126
+ # remove_foreign_key :accounts, :name => :party_foreign_key
127
+ def remove_foreign_key(from_table, options)
128
+ if Hash === options
129
+ constraint_name = foreign_key_constraint_name(from_table, options[:column], options)
130
+ else
131
+ constraint_name = foreign_key_constraint_name(from_table, "#{options.to_s.singularize}_id")
132
+ end
133
+ execute "ALTER TABLE #{quote_table_name(from_table)} DROP CONSTRAINT #{quote_column_name(constraint_name)}"
134
+ end
135
+
136
+ private
137
+
138
+ def foreign_key_constraint_name(table_name, columns, options = {})
139
+ columns = Array(columns)
140
+ constraint_name = original_name = options[:name] || "#{table_name}_#{columns.join('_')}_fk"
141
+
142
+ return constraint_name if constraint_name.length <= OracleEnhancedAdapter::IDENTIFIER_MAX_LENGTH
143
+
144
+ # leave just first three letters from each word
145
+ constraint_name = constraint_name.split('_').map{|w| w[0,3]}.join('_')
146
+ # generate unique name using hash function
147
+ if constraint_name.length > OracleEnhancedAdapter::IDENTIFIER_MAX_LENGTH
148
+ constraint_name = 'c'+Digest::SHA1.hexdigest(original_name)[0,OracleEnhancedAdapter::IDENTIFIER_MAX_LENGTH-1]
149
+ end
150
+ @logger.warn "#{adapter_name} shortened foreign key constraint name #{original_name} to #{constraint_name}" if @logger
151
+ constraint_name
152
+ end
153
+
154
+
155
+ public
156
+
157
+ # get table foreign keys for schema dump
158
+ def foreign_keys(table_name) #:nodoc:
159
+ (owner, desc_table_name, db_link) = @connection.describe(table_name)
160
+
161
+ fk_info = select_all(<<-SQL, 'Foreign Keys')
162
+ SELECT r.table_name to_table
163
+ ,rc.column_name references_column
164
+ ,cc.column_name
165
+ ,c.constraint_name name
166
+ ,c.delete_rule
167
+ FROM user_constraints#{db_link} c, user_cons_columns#{db_link} cc,
168
+ user_constraints#{db_link} r, user_cons_columns#{db_link} rc
169
+ WHERE c.owner = '#{owner}'
170
+ AND c.table_name = '#{desc_table_name}'
171
+ AND c.constraint_type = 'R'
172
+ AND cc.owner = c.owner
173
+ AND cc.constraint_name = c.constraint_name
174
+ AND r.constraint_name = c.r_constraint_name
175
+ AND r.owner = c.owner
176
+ AND rc.owner = r.owner
177
+ AND rc.constraint_name = r.constraint_name
178
+ AND rc.position = cc.position
179
+ ORDER BY name, to_table, column_name, references_column
180
+ SQL
181
+
182
+ fks = {}
183
+
184
+ fk_info.map do |row|
185
+ name = oracle_downcase(row['name'])
186
+ fks[name] ||= { :columns => [], :to_table => oracle_downcase(row['to_table']), :references => [] }
187
+ fks[name][:columns] << oracle_downcase(row['column_name'])
188
+ fks[name][:references] << oracle_downcase(row['references_column'])
189
+ case row['delete_rule']
190
+ when 'CASCADE'
191
+ fks[name][:dependent] = :delete
192
+ when 'SET NULL'
193
+ fks[name][:dependent] = :nullify
194
+ end
195
+ end
196
+
197
+ fks.map do |k, v|
198
+ options = {:name => k, :columns => v[:columns], :references => v[:references], :dependent => v[:dependent]}
199
+ OracleEnhancedForeignKeyDefinition.new(table_name, v[:to_table], options)
200
+ end
201
+ end
202
+
203
+ # REFERENTIAL INTEGRITY ====================================
204
+
205
+ def disable_referential_integrity(&block) #:nodoc:
206
+ sql_constraints = <<-SQL
207
+ SELECT constraint_name, owner, table_name
208
+ FROM user_constraints
209
+ WHERE constraint_type = 'R'
210
+ AND status = 'ENABLED'
211
+ SQL
212
+ old_constraints = select_all(sql_constraints)
213
+ begin
214
+ old_constraints.each do |constraint|
215
+ execute "ALTER TABLE #{constraint["table_name"]} DISABLE CONSTRAINT #{constraint["constraint_name"]}"
216
+ end
217
+ yield
218
+ ensure
219
+ old_constraints.each do |constraint|
220
+ execute "ALTER TABLE #{constraint["table_name"]} ENABLE CONSTRAINT #{constraint["constraint_name"]}"
221
+ end
222
+ end
223
+ end
224
+
225
+ # Add synonym to existing table or view or sequence. Can be used to create local synonym to
226
+ # remote table in other schema or in other database
227
+ # Examples:
228
+ #
229
+ # add_synonym :posts, "blog.posts"
230
+ # add_synonym :posts_seq, "blog.posts_seq"
231
+ # add_synonym :employees, "hr.employees@dblink", :force => true
232
+ #
233
+ def add_synonym(name, table_name, options = {})
234
+ sql = "CREATE"
235
+ if options[:force] == true
236
+ sql << " OR REPLACE"
237
+ end
238
+ sql << " SYNONYM #{quote_table_name(name)} FOR #{quote_table_name(table_name)}"
239
+ execute sql
240
+ end
241
+
242
+ # Remove existing synonym to table or view or sequence
243
+ # Example:
244
+ #
245
+ # remove_synonym :posts, "blog.posts"
246
+ #
247
+ def remove_synonym(name)
248
+ execute "DROP SYNONYM #{quote_table_name(name)}"
249
+ end
250
+
251
+ # get synonyms for schema dump
252
+ def synonyms #:nodoc:
253
+ select_all("SELECT synonym_name, table_owner, table_name, db_link FROM all_synonyms WHERE owner = SYS_CONTEXT('userenv', 'current_schema')").collect do |row|
254
+ OracleEnhancedSynonymDefinition.new(oracle_downcase(row['synonym_name']),
255
+ oracle_downcase(row['table_owner']), oracle_downcase(row['table_name']), oracle_downcase(row['db_link']))
256
+ end
257
+ end
258
+
259
+ end
260
+ end
261
+ end
262
+
263
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.class_eval do
264
+ include ActiveRecord::ConnectionAdapters::OracleEnhancedSchemaStatementsExt
265
+ end
@@ -0,0 +1,294 @@
1
+ module ActiveRecord #:nodoc:
2
+ module ConnectionAdapters #:nodoc:
3
+ module OracleEnhancedStructureDump #:nodoc:
4
+
5
+ # Statements separator used in structure dump to allow loading of structure dump also with SQL*Plus
6
+ STATEMENT_TOKEN = "\n\n/\n\n"
7
+
8
+ def structure_dump #:nodoc:
9
+ structure = select_values("SELECT sequence_name FROM user_sequences ORDER BY 1").map do |seq|
10
+ "CREATE SEQUENCE \"#{seq}\""
11
+ end
12
+ select_values("SELECT table_name FROM all_tables t
13
+ WHERE owner = SYS_CONTEXT('userenv', 'current_schema') AND secondary = 'N'
14
+ AND NOT EXISTS (SELECT mv.mview_name FROM all_mviews mv WHERE mv.owner = t.owner AND mv.mview_name = t.table_name)
15
+ AND NOT EXISTS (SELECT mvl.log_table FROM all_mview_logs mvl WHERE mvl.log_owner = t.owner AND mvl.log_table = t.table_name)
16
+ ORDER BY 1").each do |table_name|
17
+ virtual_columns = virtual_columns_for(table_name)
18
+ ddl = "CREATE#{ ' GLOBAL TEMPORARY' if temporary_table?(table_name)} TABLE \"#{table_name}\" (\n"
19
+ cols = select_all(%Q{
20
+ SELECT column_name, data_type, data_length, char_used, char_length, data_precision, data_scale, data_default, nullable
21
+ FROM user_tab_columns
22
+ WHERE table_name = '#{table_name}'
23
+ ORDER BY column_id
24
+ }).map do |row|
25
+ if(v = virtual_columns.find {|col| col['column_name'] == row['column_name']})
26
+ structure_dump_virtual_column(row, v['data_default'])
27
+ else
28
+ structure_dump_column(row)
29
+ end
30
+ end
31
+ ddl << cols.join(",\n ")
32
+ ddl << structure_dump_primary_key(table_name)
33
+ ddl << "\n)"
34
+ structure << ddl
35
+ structure << structure_dump_indexes(table_name)
36
+ structure << structure_dump_unique_keys(table_name)
37
+ end
38
+
39
+ join_with_statement_token(structure) << structure_dump_fk_constraints
40
+ end
41
+
42
+ def structure_dump_column(column) #:nodoc:
43
+ col = "\"#{column['column_name']}\" #{column['data_type']}"
44
+ if column['data_type'] =='NUMBER' and !column['data_precision'].nil?
45
+ col << "(#{column['data_precision'].to_i}"
46
+ col << ",#{column['data_scale'].to_i}" if !column['data_scale'].nil?
47
+ col << ')'
48
+ elsif column['data_type'].include?('CHAR')
49
+ length = column['char_used'] == 'C' ? column['char_length'].to_i : column['data_length'].to_i
50
+ col << "(#{length})"
51
+ end
52
+ col << " DEFAULT #{column['data_default']}" if !column['data_default'].nil?
53
+ col << ' NOT NULL' if column['nullable'] == 'N'
54
+ col
55
+ end
56
+
57
+ def structure_dump_virtual_column(column, data_default) #:nodoc:
58
+ data_default = data_default.gsub(/"/, '')
59
+ col = "\"#{column['column_name']}\" #{column['data_type']}"
60
+ if column['data_type'] =='NUMBER' and !column['data_precision'].nil?
61
+ col << "(#{column['data_precision'].to_i}"
62
+ col << ",#{column['data_scale'].to_i}" if !column['data_scale'].nil?
63
+ col << ')'
64
+ elsif column['data_type'].include?('CHAR')
65
+ length = column['char_used'] == 'C' ? column['char_length'].to_i : column['data_length'].to_i
66
+ col << "(#{length})"
67
+ end
68
+ col << " GENERATED ALWAYS AS (#{data_default}) VIRTUAL"
69
+ end
70
+
71
+ def structure_dump_primary_key(table) #:nodoc:
72
+ opts = {:name => '', :cols => []}
73
+ pks = select_all(<<-SQL, "Primary Keys")
74
+ SELECT a.constraint_name, a.column_name, a.position
75
+ FROM user_cons_columns a
76
+ JOIN user_constraints c
77
+ ON a.constraint_name = c.constraint_name
78
+ WHERE c.table_name = '#{table.upcase}'
79
+ AND c.constraint_type = 'P'
80
+ AND c.owner = SYS_CONTEXT('userenv', 'current_schema')
81
+ SQL
82
+ pks.each do |row|
83
+ opts[:name] = row['constraint_name']
84
+ opts[:cols][row['position']-1] = row['column_name']
85
+ end
86
+ opts[:cols].length > 0 ? ",\n CONSTRAINT #{opts[:name]} PRIMARY KEY (#{opts[:cols].join(',')})" : ''
87
+ end
88
+
89
+ def structure_dump_unique_keys(table) #:nodoc:
90
+ keys = {}
91
+ uks = select_all(<<-SQL, "Primary Keys")
92
+ SELECT a.constraint_name, a.column_name, a.position
93
+ FROM user_cons_columns a
94
+ JOIN user_constraints c
95
+ ON a.constraint_name = c.constraint_name
96
+ WHERE c.table_name = '#{table.upcase}'
97
+ AND c.constraint_type = 'U'
98
+ AND c.owner = SYS_CONTEXT('userenv', 'current_schema')
99
+ SQL
100
+ uks.each do |uk|
101
+ keys[uk['constraint_name']] ||= []
102
+ keys[uk['constraint_name']][uk['position']-1] = uk['column_name']
103
+ end
104
+ keys.map do |k,v|
105
+ "ALTER TABLE #{table.upcase} ADD CONSTRAINT #{k} UNIQUE (#{v.join(',')})"
106
+ end
107
+ end
108
+
109
+ def structure_dump_indexes(table_name) #:nodoc:
110
+ indexes(table_name).map do |options|
111
+ column_names = options[:columns]
112
+ options = {:name => options[:name], :unique => options[:unique]}
113
+ index_name = index_name(table_name, :column => column_names)
114
+ if Hash === options # legacy support, since this param was a string
115
+ index_type = options[:unique] ? "UNIQUE" : ""
116
+ index_name = options[:name] || index_name
117
+ else
118
+ index_type = options
119
+ end
120
+ quoted_column_names = column_names.map { |e| quote_column_name_or_expression(e) }.join(", ")
121
+ "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})"
122
+ end
123
+ end
124
+
125
+ def structure_dump_fk_constraints #:nodoc:
126
+ fks = select_all("SELECT table_name FROM all_tables WHERE owner = SYS_CONTEXT('userenv', 'current_schema') ORDER BY 1").map do |table|
127
+ if respond_to?(:foreign_keys) && (foreign_keys = foreign_keys(table["table_name"])).any?
128
+ foreign_keys.map do |fk|
129
+ sql = "ALTER TABLE #{quote_table_name(fk.from_table)} ADD CONSTRAINT #{quote_column_name(fk.options[:name])} "
130
+ sql << "#{foreign_key_definition(fk.to_table, fk.options)}"
131
+ end
132
+ end
133
+ end.flatten.compact
134
+ join_with_statement_token(fks)
135
+ end
136
+
137
+ def dump_schema_information #:nodoc:
138
+ sm_table = ActiveRecord::Migrator.schema_migrations_table_name
139
+ migrated = select_values("SELECT version FROM #{sm_table}")
140
+ join_with_statement_token(migrated.map{|v| "INSERT INTO #{sm_table} (version) VALUES ('#{v}')" })
141
+ end
142
+
143
+ # Extract all stored procedures, packages, synonyms and views.
144
+ def structure_dump_db_stored_code #:nodoc:
145
+ structure = []
146
+ select_all("SELECT DISTINCT name, type
147
+ FROM all_source
148
+ WHERE type IN ('PROCEDURE', 'PACKAGE', 'PACKAGE BODY', 'FUNCTION', 'TRIGGER', 'TYPE')
149
+ AND name NOT LIKE 'BIN$%'
150
+ AND owner = SYS_CONTEXT('userenv', 'current_schema') ORDER BY type").each do |source|
151
+ ddl = "CREATE OR REPLACE \n"
152
+ lines = select_all(%Q{
153
+ SELECT text
154
+ FROM all_source
155
+ WHERE name = '#{source['name']}'
156
+ AND type = '#{source['type']}'
157
+ AND owner = SYS_CONTEXT('userenv', 'current_schema')
158
+ ORDER BY line
159
+ }).map do |row|
160
+ ddl << row['text']
161
+ end
162
+ ddl << ";" unless ddl.strip[-1,1] == ";"
163
+ structure << ddl
164
+ end
165
+
166
+ # export views
167
+ select_all("SELECT view_name, text FROM user_views").each do |view|
168
+ structure << "CREATE OR REPLACE VIEW #{view['view_name']} AS\n #{view['text']}"
169
+ end
170
+
171
+ # export synonyms
172
+ select_all("SELECT owner, synonym_name, table_name, table_owner
173
+ FROM all_synonyms
174
+ WHERE owner = SYS_CONTEXT('userenv', 'current_schema') ").each do |synonym|
175
+ structure << "CREATE OR REPLACE #{synonym['owner'] == 'PUBLIC' ? 'PUBLIC' : '' } SYNONYM #{synonym['synonym_name']}"
176
+ structure << " FOR #{synonym['table_owner']}.#{synonym['table_name']}"
177
+ end
178
+
179
+ join_with_statement_token(structure)
180
+ end
181
+
182
+ def structure_drop #:nodoc:
183
+ statements = select_values("SELECT sequence_name FROM user_sequences ORDER BY 1").map do |seq|
184
+ "DROP SEQUENCE \"#{seq}\""
185
+ end
186
+ select_values("SELECT table_name from all_tables t
187
+ WHERE owner = SYS_CONTEXT('userenv', 'current_schema') AND secondary = 'N'
188
+ AND NOT EXISTS (SELECT mv.mview_name FROM all_mviews mv WHERE mv.owner = t.owner AND mv.mview_name = t.table_name)
189
+ AND NOT EXISTS (SELECT mvl.log_table FROM all_mview_logs mvl WHERE mvl.log_owner = t.owner AND mvl.log_table = t.table_name)
190
+ ORDER BY 1").each do |table|
191
+ statements << "DROP TABLE \"#{table}\" CASCADE CONSTRAINTS"
192
+ end
193
+ join_with_statement_token(statements)
194
+ end
195
+
196
+ def temp_table_drop #:nodoc:
197
+ join_with_statement_token(select_values(
198
+ "SELECT table_name FROM all_tables
199
+ WHERE owner = SYS_CONTEXT('userenv', 'current_schema') AND secondary = 'N' AND temporary = 'Y' ORDER BY 1").map do |table|
200
+ "DROP TABLE \"#{table}\" CASCADE CONSTRAINTS"
201
+ end)
202
+ end
203
+
204
+ def full_drop(preserve_tables=false) #:nodoc:
205
+ s = preserve_tables ? [] : [structure_drop]
206
+ s << temp_table_drop if preserve_tables
207
+ s << drop_sql_for_feature("view")
208
+ s << drop_sql_for_feature("materialized view")
209
+ s << drop_sql_for_feature("synonym")
210
+ s << drop_sql_for_feature("type")
211
+ s << drop_sql_for_object("package")
212
+ s << drop_sql_for_object("function")
213
+ s << drop_sql_for_object("procedure")
214
+ s.join
215
+ end
216
+
217
+ def add_column_options!(sql, options) #:nodoc:
218
+ type = options[:type] || ((column = options[:column]) && column.type)
219
+ type = type && type.to_sym
220
+ # handle case of defaults for CLOB columns, which would otherwise get "quoted" incorrectly
221
+ if options_include_default?(options)
222
+ if type == :text
223
+ sql << " DEFAULT #{quote(options[:default])}"
224
+ else
225
+ # from abstract adapter
226
+ sql << " DEFAULT #{quote(options[:default], options[:column])}"
227
+ end
228
+ end
229
+ # must explicitly add NULL or NOT NULL to allow change_column to work on migrations
230
+ if options[:null] == false
231
+ sql << " NOT NULL"
232
+ elsif options[:null] == true
233
+ sql << " NULL" unless type == :primary_key
234
+ end
235
+ # add AS expression for virtual columns
236
+ if options[:as].present?
237
+ sql << " AS (#{options[:as]})"
238
+ end
239
+ end
240
+
241
+ def execute_structure_dump(string)
242
+ string.split(STATEMENT_TOKEN).each do |ddl|
243
+ ddl.chop! if ddl.last == ";"
244
+ execute(ddl) unless ddl.blank?
245
+ end
246
+ end
247
+
248
+ private
249
+
250
+ # virtual columns are an 11g feature. This returns [] if feature is not
251
+ # present or none are found.
252
+ # return [{'column_name' => 'FOOS', 'data_default' => '...'}, ...]
253
+ def virtual_columns_for(table)
254
+ begin
255
+ select_all <<-SQL
256
+ SELECT column_name, data_default
257
+ FROM user_tab_cols
258
+ WHERE virtual_column = 'YES'
259
+ AND table_name = '#{table.upcase}'
260
+ SQL
261
+ # feature not supported previous to 11g
262
+ rescue ActiveRecord::StatementInvalid => e
263
+ []
264
+ end
265
+ end
266
+
267
+ def drop_sql_for_feature(type)
268
+ short_type = type == 'materialized view' ? 'mview' : type
269
+ join_with_statement_token(
270
+ select_values("SELECT #{short_type}_name FROM user_#{short_type.tableize}").map do |name|
271
+ "DROP #{type.upcase} \"#{name}\""
272
+ end)
273
+ end
274
+
275
+ def drop_sql_for_object(type)
276
+ join_with_statement_token(
277
+ select_values("SELECT object_name FROM user_objects WHERE object_type = '#{type.upcase}'").map do |name|
278
+ "DROP #{type.upcase} \"#{name}\""
279
+ end)
280
+ end
281
+
282
+ def join_with_statement_token(array)
283
+ string = array.join(STATEMENT_TOKEN)
284
+ string << STATEMENT_TOKEN unless string.blank?
285
+ string
286
+ end
287
+
288
+ end
289
+ end
290
+ end
291
+
292
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.class_eval do
293
+ include ActiveRecord::ConnectionAdapters::OracleEnhancedStructureDump
294
+ end