unclebilly-activerecord-oracle_enhanced-adapter 1.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/History.txt +165 -0
  2. data/License.txt +20 -0
  3. data/Manifest.txt +32 -0
  4. data/README.rdoc +75 -0
  5. data/Rakefile +49 -0
  6. data/VERSION +1 -0
  7. data/lib/active_record/connection_adapters/emulation/oracle_adapter.rb +5 -0
  8. data/lib/active_record/connection_adapters/oracle_enhanced.rake +51 -0
  9. data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +1723 -0
  10. data/lib/active_record/connection_adapters/oracle_enhanced_connection.rb +121 -0
  11. data/lib/active_record/connection_adapters/oracle_enhanced_core_ext.rb +64 -0
  12. data/lib/active_record/connection_adapters/oracle_enhanced_cpk.rb +21 -0
  13. data/lib/active_record/connection_adapters/oracle_enhanced_dirty.rb +39 -0
  14. data/lib/active_record/connection_adapters/oracle_enhanced_jdbc_connection.rb +369 -0
  15. data/lib/active_record/connection_adapters/oracle_enhanced_oci_connection.rb +396 -0
  16. data/lib/active_record/connection_adapters/oracle_enhanced_procedures.rb +164 -0
  17. data/lib/active_record/connection_adapters/oracle_enhanced_reserved_words.rb +126 -0
  18. data/lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb +177 -0
  19. data/lib/active_record/connection_adapters/oracle_enhanced_schema_dumper.rb +214 -0
  20. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements_ext.rb +224 -0
  21. data/lib/active_record/connection_adapters/oracle_enhanced_tasks.rb +11 -0
  22. data/lib/active_record/connection_adapters/oracle_enhanced_version.rb +1 -0
  23. data/lib/active_record/connection_adapters/oracle_enhanced_virtual_column.rb +35 -0
  24. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +610 -0
  25. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_structure_dumper_spec.rb +266 -0
  26. data/spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb +206 -0
  27. data/spec/active_record/connection_adapters/oracle_enhanced_core_ext_spec.rb +40 -0
  28. data/spec/active_record/connection_adapters/oracle_enhanced_cpk_spec.rb +107 -0
  29. data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +984 -0
  30. data/spec/active_record/connection_adapters/oracle_enhanced_dbms_output_spec.rb +67 -0
  31. data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +93 -0
  32. data/spec/active_record/connection_adapters/oracle_enhanced_emulate_oracle_adapter_spec.rb +25 -0
  33. data/spec/active_record/connection_adapters/oracle_enhanced_procedures_spec.rb +370 -0
  34. data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +268 -0
  35. data/spec/active_record/connection_adapters/oracle_enhanced_schema_spec.rb +761 -0
  36. data/spec/spec.opts +6 -0
  37. data/spec/spec_helper.rb +130 -0
  38. metadata +149 -0
@@ -0,0 +1,126 @@
1
+ module ActiveRecord #:nodoc:
2
+ module ConnectionAdapters #:nodoc:
3
+ module OracleEnhancedReservedWords #:nodoc:
4
+
5
+ RESERVED_WORDS = {
6
+ "ACCESS" => true,
7
+ "ADD" => true,
8
+ "ALL" => true,
9
+ "ALTER" => true,
10
+ "AND" => true,
11
+ "ANY" => true,
12
+ "AS" => true,
13
+ "ASC" => true,
14
+ "AUDIT" => true,
15
+ "BETWEEN" => true,
16
+ "BY" => true,
17
+ "CHAR" => true,
18
+ "CHECK" => true,
19
+ "CLUSTER" => true,
20
+ "COLUMN" => true,
21
+ "COMMENT" => true,
22
+ "COMPRESS" => true,
23
+ "CONNECT" => true,
24
+ "CREATE" => true,
25
+ "CURRENT" => true,
26
+ "DATE" => true,
27
+ "DECIMAL" => true,
28
+ "DEFAULT" => true,
29
+ "DELETE" => true,
30
+ "DESC" => true,
31
+ "DISTINCT" => true,
32
+ "DROP" => true,
33
+ "ELSE" => true,
34
+ "EXCLUSIVE" => true,
35
+ "EXISTS" => true,
36
+ "FILE" => true,
37
+ "FLOAT" => true,
38
+ "FOR" => true,
39
+ "FROM" => true,
40
+ "GRANT" => true,
41
+ "GROUP" => true,
42
+ "HAVING" => true,
43
+ "IDENTIFIED" => true,
44
+ "IMMEDIATE" => true,
45
+ "IN" => true,
46
+ "INCREMENT" => true,
47
+ "INDEX" => true,
48
+ "INITIAL" => true,
49
+ "INSERT" => true,
50
+ "INTEGER" => true,
51
+ "INTERSECT" => true,
52
+ "INTO" => true,
53
+ "IS" => true,
54
+ "LEVEL" => true,
55
+ "LIKE" => true,
56
+ "LOCK" => true,
57
+ "LONG" => true,
58
+ "MAXEXTENTS" => true,
59
+ "MINUS" => true,
60
+ "MLSLABEL" => true,
61
+ "MODE" => true,
62
+ "MODIFY" => true,
63
+ "NOAUDIT" => true,
64
+ "NOCOMPRESS" => true,
65
+ "NOT" => true,
66
+ "NOWAIT" => true,
67
+ "NULL" => true,
68
+ "NUMBER" => true,
69
+ "OF" => true,
70
+ "OFFLINE" => true,
71
+ "ON" => true,
72
+ "ONLINE" => true,
73
+ "OPTION" => true,
74
+ "OR" => true,
75
+ "ORDER" => true,
76
+ "PCTFREE" => true,
77
+ "PRIOR" => true,
78
+ "PRIVILEGES" => true,
79
+ "PUBLIC" => true,
80
+ "RAW" => true,
81
+ "RENAME" => true,
82
+ "RESOURCE" => true,
83
+ "REVOKE" => true,
84
+ "ROW" => true,
85
+ "ROWID" => true,
86
+ "ROWNUM" => true,
87
+ "ROWS" => true,
88
+ "SELECT" => true,
89
+ "SESSION" => true,
90
+ "SET" => true,
91
+ "SHARE" => true,
92
+ "SIZE" => true,
93
+ "SMALLINT" => true,
94
+ "START" => true,
95
+ "SUCCESSFUL" => true,
96
+ "SYNONYM" => true,
97
+ "SYSDATE" => true,
98
+ "TABLE" => true,
99
+ "THEN" => true,
100
+ "TO" => true,
101
+ "TRIGGER" => true,
102
+ "UID" => true,
103
+ "UNION" => true,
104
+ "UNIQUE" => true,
105
+ "UPDATE" => true,
106
+ "USER" => true,
107
+ "VALIDATE" => true,
108
+ "VALUES" => true,
109
+ "VARCHAR" => true,
110
+ "VARCHAR2" => true,
111
+ "VIEW" => true,
112
+ "WHENEVER" => true,
113
+ "WHERE" => true,
114
+ "WITH" => true
115
+ }
116
+
117
+ def quote_oracle_reserved_words(name)
118
+ RESERVED_WORDS[name.to_s.upcase].nil? ? name : "\"#{name}\""
119
+ end
120
+ end
121
+ end
122
+ end
123
+
124
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.class_eval do
125
+ include ActiveRecord::ConnectionAdapters::OracleEnhancedReservedWords
126
+ end
@@ -0,0 +1,177 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ class OracleEnhancedForeignKeyDefinition < Struct.new(:from_table, :to_table, :options) #:nodoc:
4
+ end
5
+
6
+ class OracleEnhancedSynonymDefinition < Struct.new(:name, :table_owner, :table_name, :db_link) #:nodoc:
7
+ end
8
+
9
+ class OracleEnhancedIndexDefinition < Struct.new(:table, :name, :unique, :tablespace, :columns)
10
+ end
11
+
12
+ class OracleEnhancedColumnDefinition < ColumnDefinition
13
+ def to_sql
14
+ return super unless type==:virtual
15
+ #virtual column syntax
16
+ "#{base.quote_column_name(name)} AS (#{default})"
17
+ end
18
+ alias to_s :to_sql
19
+ end
20
+
21
+ module OracleEnhancedSchemaDefinitions #:nodoc:
22
+ def self.included(base)
23
+ base::TableDefinition.class_eval do
24
+ include OracleEnhancedTableDefinition
25
+ end
26
+
27
+ # Available starting from ActiveRecord 2.1
28
+ base::Table.class_eval do
29
+ include OracleEnhancedTable
30
+ end if defined?(base::Table)
31
+ end
32
+ end
33
+
34
+ module OracleEnhancedTableDefinition
35
+ class ForeignKey < Struct.new(:base, :to_table, :options) #:nodoc:
36
+ def to_sql
37
+ base.foreign_key_definition(to_table, options)
38
+ end
39
+ alias to_s :to_sql
40
+ end
41
+
42
+ def self.included(base) #:nodoc:
43
+ base.class_eval do
44
+ alias_method_chain :references, :foreign_keys
45
+ alias_method_chain :to_sql, :foreign_keys
46
+ end
47
+ end
48
+
49
+ # Adds a :foreign_key option to TableDefinition.references.
50
+ # If :foreign_key is true, a foreign key constraint is added to the table.
51
+ # You can also specify a hash, which is passed as foreign key options.
52
+ #
53
+ # ===== Examples
54
+ # ====== Add goat_id column and a foreign key to the goats table.
55
+ # t.references(:goat, :foreign_key => true)
56
+ # ====== Add goat_id column and a cascading foreign key to the goats table.
57
+ # t.references(:goat, :foreign_key => {:dependent => :delete})
58
+ #
59
+ # Note: No foreign key is created if :polymorphic => true is used.
60
+ # Note: If no name is specified, the database driver creates one for you!
61
+ def references_with_foreign_keys(*args)
62
+ options = args.extract_options!
63
+ fk_options = options.delete(:foreign_key)
64
+
65
+ if fk_options && !options[:polymorphic]
66
+ fk_options = {} if fk_options == true
67
+ args.each { |to_table| foreign_key(to_table, fk_options) }
68
+ end
69
+
70
+ references_without_foreign_keys(*(args << options))
71
+ end
72
+
73
+ # Defines a foreign key for the table. +to_table+ can be a single Symbol, or
74
+ # an Array of Symbols. See SchemaStatements#add_foreign_key
75
+ #
76
+ # ===== Examples
77
+ # ====== Creating a simple foreign key
78
+ # t.foreign_key(:people)
79
+ # ====== Defining the column
80
+ # t.foreign_key(:people, :column => :sender_id)
81
+ # ====== Creating a named foreign key
82
+ # t.foreign_key(:people, :column => :sender_id, :name => 'sender_foreign_key')
83
+ # ====== Defining the column of the +to_table+.
84
+ # t.foreign_key(:people, :column => :sender_id, :primary_key => :person_id)
85
+ def foreign_key(to_table, options = {})
86
+ if @base.respond_to?(:supports_foreign_keys?) && @base.supports_foreign_keys?
87
+ to_table = to_table.to_s.pluralize if ActiveRecord::Base.pluralize_table_names
88
+ foreign_keys << ForeignKey.new(@base, to_table, options)
89
+ else
90
+ raise ArgumentError, "this ActiveRecord adapter is not supporting foreign_key definition"
91
+ end
92
+ end
93
+
94
+ def to_sql_with_foreign_keys #:nodoc:
95
+ sql = to_sql_without_foreign_keys
96
+ sql << ', ' << (foreign_keys * ', ') unless foreign_keys.blank?
97
+ sql
98
+ end
99
+
100
+ private
101
+ def foreign_keys
102
+ @foreign_keys ||= []
103
+ end
104
+ end
105
+
106
+ module OracleEnhancedTable
107
+ def self.included(base) #:nodoc:
108
+ base.class_eval do
109
+ alias_method_chain :references, :foreign_keys
110
+ end
111
+ end
112
+
113
+ # Adds a new foreign key to the table. +to_table+ can be a single Symbol, or
114
+ # an Array of Symbols. See SchemaStatements#add_foreign_key
115
+ #
116
+ # ===== Examples
117
+ # ====== Creating a simple foreign key
118
+ # t.foreign_key(:people)
119
+ # ====== Defining the column
120
+ # t.foreign_key(:people, :column => :sender_id)
121
+ # ====== Creating a named foreign key
122
+ # t.foreign_key(:people, :column => :sender_id, :name => 'sender_foreign_key')
123
+ # ====== Defining the column of the +to_table+.
124
+ # t.foreign_key(:people, :column => :sender_id, :primary_key => :person_id)
125
+ def foreign_key(to_table, options = {})
126
+ if @base.respond_to?(:supports_foreign_keys?) && @base.supports_foreign_keys?
127
+ to_table = to_table.to_s.pluralize if ActiveRecord::Base.pluralize_table_names
128
+ @base.add_foreign_key(@table_name, to_table, options)
129
+ else
130
+ raise ArgumentError, "this ActiveRecord adapter is not supporting foreign_key definition"
131
+ end
132
+ end
133
+
134
+ # Remove the given foreign key from the table.
135
+ #
136
+ # ===== Examples
137
+ # ====== Remove the suppliers_company_id_fk in the suppliers table.
138
+ # t.remove_foreign_key :companies
139
+ # ====== Remove the foreign key named accounts_branch_id_fk in the accounts table.
140
+ # remove_foreign_key :column => :branch_id
141
+ # ====== Remove the foreign key named party_foreign_key in the accounts table.
142
+ # remove_index :name => :party_foreign_key
143
+ def remove_foreign_key(options = {})
144
+ @base.remove_foreign_key(@table_name, options)
145
+ end
146
+
147
+ # Adds a :foreign_key option to TableDefinition.references.
148
+ # If :foreign_key is true, a foreign key constraint is added to the table.
149
+ # You can also specify a hash, which is passed as foreign key options.
150
+ #
151
+ # ===== Examples
152
+ # ====== Add goat_id column and a foreign key to the goats table.
153
+ # t.references(:goat, :foreign_key => true)
154
+ # ====== Add goat_id column and a cascading foreign key to the goats table.
155
+ # t.references(:goat, :foreign_key => {:dependent => :delete})
156
+ #
157
+ # Note: No foreign key is created if :polymorphic => true is used.
158
+ def references_with_foreign_keys(*args)
159
+ options = args.extract_options!
160
+ polymorphic = options[:polymorphic]
161
+ fk_options = options.delete(:foreign_key)
162
+
163
+ references_without_foreign_keys(*(args << options))
164
+ # references_without_foreign_keys adds {:type => :integer}
165
+ args.extract_options!
166
+ if fk_options && !polymorphic
167
+ fk_options = {} if fk_options == true
168
+ args.each { |to_table| foreign_key(to_table, fk_options) }
169
+ end
170
+ end
171
+ end
172
+ end
173
+ end
174
+
175
+ ActiveRecord::ConnectionAdapters.class_eval do
176
+ include ActiveRecord::ConnectionAdapters::OracleEnhancedSchemaDefinitions
177
+ end
@@ -0,0 +1,214 @@
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 tables_with_oracle_enhanced(stream)
16
+ sorted_tables = @connection.tables.sort
17
+ sorted_tables.each do |tbl|
18
+ # add table prefix or suffix for schema_migrations
19
+ next if [ActiveRecord::Migrator.proper_table_name('schema_migrations'), ignore_tables].flatten.any? do |ignored|
20
+ case ignored
21
+ when String; tbl == ignored
22
+ when Regexp; tbl =~ ignored
23
+ else
24
+ raise StandardError, 'ActiveRecord::SchemaDumper.ignore_tables accepts an array of String and / or Regexp values.'
25
+ end
26
+ end
27
+ # change table name inspect method
28
+ tbl.extend TableInspect
29
+ oracle_enhanced_table(tbl, stream)
30
+ # add primary key trigger if table has it
31
+ primary_key_trigger(tbl, stream)
32
+ end
33
+ sorted_tables.each do |tbl|
34
+ # add foreign keys if table has them
35
+ foreign_keys(tbl, stream)
36
+ end
37
+ # add synonyms in local schema
38
+ synonyms(stream)
39
+ end
40
+
41
+ def primary_key_trigger(table_name, stream)
42
+ if @connection.respond_to?(:has_primary_key_trigger?) && @connection.has_primary_key_trigger?(table_name)
43
+ pk, pk_seq = @connection.pk_and_sequence_for(table_name)
44
+ stream.print " add_primary_key_trigger #{table_name.inspect}"
45
+ stream.print ", :primary_key => \"#{pk}\"" if pk != 'id'
46
+ stream.print "\n\n"
47
+ end
48
+ end
49
+
50
+ def foreign_keys(table_name, stream)
51
+ if @connection.respond_to?(:foreign_keys) && (foreign_keys = @connection.foreign_keys(table_name)).any?
52
+ add_foreign_key_statements = foreign_keys.map do |foreign_key|
53
+ statement_parts = [ ('add_foreign_key ' + foreign_key.from_table.inspect) ]
54
+ statement_parts << foreign_key.to_table.inspect
55
+ statement_parts << (':name => ' + foreign_key.options[:name].inspect)
56
+
57
+ if foreign_key.options[:column] != "#{foreign_key.to_table.singularize}_id"
58
+ statement_parts << (':column => ' + foreign_key.options[:column].inspect)
59
+ end
60
+ if foreign_key.options[:primary_key] != 'id'
61
+ statement_parts << (':primary_key => ' + foreign_key.options[:primary_key].inspect)
62
+ end
63
+ unless foreign_key.options[:dependent].blank?
64
+ statement_parts << (':dependent => ' + foreign_key.options[:dependent].inspect)
65
+ end
66
+
67
+ ' ' + statement_parts.join(', ')
68
+ end
69
+
70
+ stream.puts add_foreign_key_statements.sort.join("\n")
71
+ stream.puts
72
+ end
73
+ end
74
+
75
+ def synonyms(stream)
76
+ if @connection.respond_to?(:synonyms)
77
+ syns = @connection.synonyms
78
+ syns.each do |syn|
79
+ table_name = syn.table_name
80
+ table_name = "#{syn.table_owner}.#{table_name}" if syn.table_owner
81
+ table_name = "#{table_name}@#{syn.db_link}" if syn.db_link
82
+ stream.print " add_synonym #{syn.name.inspect}, #{table_name.inspect}, :force => true"
83
+ stream.puts
84
+ end
85
+ stream.puts unless syns.empty?
86
+ end
87
+ end
88
+
89
+ def indexes_with_oracle_enhanced(table, stream)
90
+ if (indexes = @connection.indexes(table)).any?
91
+ add_index_statements = indexes.map do |index|
92
+ # use table.inspect as it will remove prefix and suffix
93
+ statment_parts = [ ('add_index ' + table.inspect) ]
94
+ statment_parts << index.columns.inspect
95
+ statment_parts << (':name => ' + index.name.inspect)
96
+ statment_parts << ':unique => true' if index.unique
97
+ statment_parts << ':tablespace => ' + index.tablespace.inspect
98
+
99
+ ' ' + statment_parts.join(', ')
100
+ end
101
+
102
+ stream.puts add_index_statements.sort.join("\n")
103
+ stream.puts
104
+ end
105
+ end
106
+
107
+ def oracle_enhanced_table(table, stream)
108
+ columns = @connection.columns(table)
109
+ begin
110
+ tbl = StringIO.new
111
+
112
+ # first dump primary key column
113
+ if @connection.respond_to?(:pk_and_sequence_for)
114
+ pk, pk_seq = @connection.pk_and_sequence_for(table)
115
+ elsif @connection.respond_to?(:primary_key)
116
+ pk = @connection.primary_key(table)
117
+ end
118
+
119
+ tbl.print " create_table #{table.inspect}"
120
+
121
+ # addition to make temporary option work
122
+ tbl.print ", :temporary => true" if @connection.temporary?(table)
123
+
124
+ if columns.detect { |c| c.name == pk }
125
+ if pk != 'id'
126
+ tbl.print %Q(, :primary_key => "#{pk}")
127
+ end
128
+ else
129
+ tbl.print ", :id => false"
130
+ end
131
+ tbl.print ", :force => true"
132
+ tbl.puts " do |t|"
133
+
134
+ # then dump all non-primary key columns
135
+ column_specs = columns.map do |column|
136
+ raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" if @types[column.type].nil?
137
+ next if column.name == pk
138
+ spec = {}
139
+ spec[:name] = column.name.inspect
140
+ spec[:type] = column.virtual? ? 'virtual' : column.type.to_s
141
+ spec[:limit] = column.limit.inspect if column.limit != @types[column.type][:limit] && column.type != :decimal
142
+ spec[:precision] = column.precision.inspect if !column.precision.nil?
143
+ spec[:scale] = column.scale.inspect if !column.scale.nil?
144
+ spec[:null] = 'false' if !column.null
145
+ spec[:default] = column.default.inspect if column.virtual?
146
+ spec[:default] ||= default_string(column.default) if column.has_default?
147
+ (spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k.inspect} => ")}
148
+ spec
149
+ end.compact
150
+
151
+ # find all migration keys used in this table
152
+ keys = [:name, :limit, :precision, :scale, :default, :null] & column_specs.map(&:keys).flatten
153
+
154
+ # figure out the lengths for each column based on above keys
155
+ lengths = keys.map{ |key| column_specs.map{ |spec| spec[key] ? spec[key].length + 2 : 0 }.max }
156
+
157
+ # the string we're going to sprintf our values against, with standardized column widths
158
+ format_string = lengths.map{ |len| "%-#{len}s" }
159
+
160
+ # find the max length for the 'type' column, which is special
161
+ type_length = column_specs.map{ |column| column[:type].length }.max
162
+
163
+ # add column type definition to our format string
164
+ format_string.unshift " t.%-#{type_length}s "
165
+
166
+ format_string *= ''
167
+
168
+ column_specs.each do |colspec|
169
+ values = keys.zip(lengths).map{ |key, len| colspec.key?(key) ? colspec[key] + ", " : " " * len }
170
+ values.unshift colspec[:type]
171
+ tbl.print((format_string % values).gsub(/,\s*$/, ''))
172
+ tbl.puts
173
+ end
174
+
175
+ tbl.puts " end"
176
+ tbl.puts
177
+
178
+ indexes(table, tbl)
179
+
180
+ tbl.rewind
181
+ stream.print tbl.read
182
+ rescue => e
183
+ stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}"
184
+ stream.puts "# #{e.message}"
185
+ stream.puts
186
+ end
187
+
188
+ stream
189
+ end
190
+
191
+
192
+ # remove table name prefix and suffix when doing #inspect (which is used in tables method)
193
+ module TableInspect #:nodoc:
194
+ def inspect
195
+ remove_prefix_and_suffix(self)
196
+ end
197
+
198
+ private
199
+ def remove_prefix_and_suffix(table_name)
200
+ if table_name =~ /\A#{ActiveRecord::Base.table_name_prefix}(.*)#{ActiveRecord::Base.table_name_suffix}\Z/
201
+ "\"#{$1}\""
202
+ else
203
+ "\"#{table_name}\""
204
+ end
205
+ end
206
+ end
207
+
208
+ end
209
+ end
210
+ end
211
+
212
+ ActiveRecord::SchemaDumper.class_eval do
213
+ include ActiveRecord::ConnectionAdapters::OracleEnhancedSchemaDumper
214
+ end