activerecord-oracle_enhanced-adapter 1.5.6 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -2
  3. data/History.md +107 -0
  4. data/README.md +271 -174
  5. data/VERSION +1 -1
  6. data/activerecord-oracle_enhanced-adapter.gemspec +26 -22
  7. data/lib/active_record/connection_adapters/{oracle_enhanced_column.rb → oracle_enhanced/column.rb} +14 -63
  8. data/lib/active_record/connection_adapters/oracle_enhanced/column_dumper.rb +65 -0
  9. data/lib/active_record/connection_adapters/{oracle_enhanced_connection.rb → oracle_enhanced/connection.rb} +2 -2
  10. data/lib/active_record/connection_adapters/oracle_enhanced/context_index.rb +347 -0
  11. data/lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb +257 -0
  12. data/lib/active_record/connection_adapters/oracle_enhanced/dirty.rb +40 -0
  13. data/lib/active_record/connection_adapters/{oracle_enhanced_schema_creation.rb → oracle_enhanced/schema_creation.rb} +17 -16
  14. data/lib/active_record/connection_adapters/oracle_enhanced/schema_definitions.rb +95 -0
  15. data/lib/active_record/connection_adapters/{oracle_enhanced_schema_dumper.rb → oracle_enhanced/schema_dumper.rb} +4 -32
  16. data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb +546 -0
  17. data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements_ext.rb +65 -0
  18. data/lib/active_record/connection_adapters/{oracle_enhanced_structure_dump.rb → oracle_enhanced/structure_dump.rb} +26 -4
  19. data/lib/active_record/connection_adapters/oracle_enhanced/version.rb +1 -0
  20. data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +159 -66
  21. data/lib/active_record/oracle_enhanced/type/integer.rb +13 -0
  22. data/lib/active_record/oracle_enhanced/type/raw.rb +13 -0
  23. data/lib/active_record/oracle_enhanced/type/timestamp.rb +11 -0
  24. data/lib/activerecord-oracle_enhanced-adapter.rb +1 -1
  25. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +6 -31
  26. data/spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb +1 -1
  27. data/spec/active_record/connection_adapters/oracle_enhanced_context_index_spec.rb +2 -2
  28. data/spec/active_record/connection_adapters/oracle_enhanced_cpk_spec.rb +2 -2
  29. data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +63 -63
  30. data/spec/active_record/connection_adapters/oracle_enhanced_database_tasks_spec.rb +1 -1
  31. data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +7 -13
  32. data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +25 -178
  33. data/spec/active_record/connection_adapters/oracle_enhanced_schema_statements_spec.rb +14 -5
  34. data/spec/active_record/connection_adapters/oracle_enhanced_structure_dump_spec.rb +1 -0
  35. data/spec/spec_config.yaml.template +10 -0
  36. data/spec/spec_helper.rb +21 -10
  37. metadata +27 -23
  38. data/lib/active_record/connection_adapters/oracle_enhanced_column_dumper.rb +0 -77
  39. data/lib/active_record/connection_adapters/oracle_enhanced_context_index.rb +0 -350
  40. data/lib/active_record/connection_adapters/oracle_enhanced_database_statements.rb +0 -262
  41. data/lib/active_record/connection_adapters/oracle_enhanced_dirty.rb +0 -45
  42. data/lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb +0 -197
  43. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb +0 -450
  44. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements_ext.rb +0 -258
  45. data/lib/active_record/connection_adapters/oracle_enhanced_version.rb +0 -1
  46. /data/lib/active_record/connection_adapters/{oracle_enhanced_cpk.rb → oracle_enhanced/cpk.rb} +0 -0
  47. /data/lib/active_record/connection_adapters/{oracle_enhanced_database_tasks.rb → oracle_enhanced/database_tasks.rb} +0 -0
  48. /data/lib/active_record/connection_adapters/{oracle_enhanced_jdbc_connection.rb → oracle_enhanced/jdbc_connection.rb} +0 -0
  49. /data/lib/active_record/connection_adapters/{oracle_enhanced_oci_connection.rb → oracle_enhanced/oci_connection.rb} +0 -0
  50. /data/lib/active_record/connection_adapters/{oracle_enhanced_procedures.rb → oracle_enhanced/procedures.rb} +0 -0
@@ -0,0 +1,257 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module OracleEnhanced
4
+ module DatabaseStatements
5
+ # DATABASE STATEMENTS ======================================
6
+ #
7
+ # see: abstract/database_statements.rb
8
+
9
+ # Executes a SQL statement
10
+ def execute(sql, name = nil)
11
+ log(sql, name) { @connection.exec(sql) }
12
+ end
13
+
14
+ def clear_cache!
15
+ @statements.clear
16
+ reload_type_map
17
+ end
18
+
19
+ def exec_query(sql, name = 'SQL', binds = [])
20
+ type_casted_binds = binds.map { |col, val|
21
+ [col, type_cast(val, col)]
22
+ }
23
+ log(sql, name, type_casted_binds) do
24
+ cursor = nil
25
+ cached = false
26
+ if without_prepared_statement?(binds)
27
+ cursor = @connection.prepare(sql)
28
+ else
29
+ unless @statements.key? sql
30
+ @statements[sql] = @connection.prepare(sql)
31
+ end
32
+
33
+ cursor = @statements[sql]
34
+
35
+ binds.each_with_index do |bind, i|
36
+ col, val = bind
37
+ cursor.bind_param(i + 1, type_cast(val, col), col)
38
+ end
39
+
40
+ cached = true
41
+ end
42
+
43
+ cursor.exec
44
+
45
+ if name == 'EXPLAIN' and sql =~ /^EXPLAIN/
46
+ res = true
47
+ else
48
+ columns = cursor.get_col_names.map do |col_name|
49
+ @connection.oracle_downcase(col_name)
50
+ end
51
+ rows = []
52
+ fetch_options = {:get_lob_value => (name != 'Writable Large Object')}
53
+ while row = cursor.fetch(fetch_options)
54
+ rows << row
55
+ end
56
+ res = ActiveRecord::Result.new(columns, rows)
57
+ end
58
+
59
+ cursor.close unless cached
60
+ res
61
+ end
62
+ end
63
+
64
+ def supports_statement_cache?
65
+ true
66
+ end
67
+
68
+ def supports_explain?
69
+ true
70
+ end
71
+
72
+ def explain(arel, binds = [])
73
+ sql = "EXPLAIN PLAN FOR #{to_sql(arel, binds)}"
74
+ return if sql =~ /FROM all_/
75
+ if ORACLE_ENHANCED_CONNECTION == :jdbc
76
+ exec_query(sql, 'EXPLAIN', binds)
77
+ else
78
+ exec_query(sql, 'EXPLAIN')
79
+ end
80
+ select_values("SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY)", 'EXPLAIN').join("\n")
81
+ end
82
+
83
+ # Returns an array of arrays containing the field values.
84
+ # Order is the same as that returned by #columns.
85
+ def select_rows(sql, name = nil, binds = [])
86
+ exec_query(sql, name, binds).rows
87
+ end
88
+
89
+ # Executes an INSERT statement and returns the new record's ID
90
+ def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
91
+ # if primary key value is already prefetched from sequence
92
+ # or if there is no primary key
93
+ if id_value || pk.nil?
94
+ execute(sql, name)
95
+ return id_value
96
+ end
97
+
98
+ sql_with_returning = sql + @connection.returning_clause(quote_column_name(pk))
99
+ log(sql, name) do
100
+ @connection.exec_with_returning(sql_with_returning)
101
+ end
102
+ end
103
+ protected :insert_sql
104
+
105
+ # New method in ActiveRecord 3.1
106
+ # Will add RETURNING clause in case of trigger generated primary keys
107
+ def sql_for_insert(sql, pk, id_value, sequence_name, binds)
108
+ unless id_value || pk.nil? || (defined?(CompositePrimaryKeys) && pk.kind_of?(CompositePrimaryKeys::CompositeKeys))
109
+ sql = "#{sql} RETURNING #{quote_column_name(pk)} INTO :returning_id"
110
+ returning_id_col = new_column("returning_id", nil, Type::Value.new, "number", true, "dual", true, true)
111
+ (binds = binds.dup) << [returning_id_col, nil]
112
+ end
113
+ [sql, binds]
114
+ end
115
+
116
+ # New method in ActiveRecord 3.1
117
+ def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
118
+ type_casted_binds = binds.map { |col, val|
119
+ [col, type_cast(val, col)]
120
+ }
121
+ log(sql, name, type_casted_binds) do
122
+ returning_id_col = returning_id_index = nil
123
+ if without_prepared_statement?(binds)
124
+ cursor = @connection.prepare(sql)
125
+ else
126
+ unless @statements.key? (sql)
127
+ @statements[sql] = @connection.prepare(sql)
128
+ end
129
+
130
+ cursor = @statements[sql]
131
+
132
+ binds.each_with_index do |bind, i|
133
+ col, val = bind
134
+ if col.returning_id?
135
+ returning_id_col = [col]
136
+ returning_id_index = i + 1
137
+ cursor.bind_returning_param(returning_id_index, Integer)
138
+ else
139
+ cursor.bind_param(i + 1, type_cast(val, col), col)
140
+ end
141
+ end
142
+ end
143
+
144
+ cursor.exec_update
145
+
146
+ rows = []
147
+ if returning_id_index
148
+ returning_id = cursor.get_returning_param(returning_id_index, Integer)
149
+ rows << [returning_id]
150
+ end
151
+ ActiveRecord::Result.new(returning_id_col || [], rows)
152
+ end
153
+ end
154
+
155
+ # New method in ActiveRecord 3.1
156
+ def exec_update(sql, name, binds)
157
+ log(sql, name, binds) do
158
+ cached = false
159
+ if without_prepared_statement?(binds)
160
+ cursor = @connection.prepare(sql)
161
+ else
162
+ cursor = if @statements.key?(sql)
163
+ @statements[sql]
164
+ else
165
+ @statements[sql] = @connection.prepare(sql)
166
+ end
167
+
168
+ binds.each_with_index do |bind, i|
169
+ col, val = bind
170
+ cursor.bind_param(i + 1, type_cast(val, col), col)
171
+ end
172
+ cached = true
173
+ end
174
+
175
+ res = cursor.exec_update
176
+ cursor.close unless cached
177
+ res
178
+ end
179
+ end
180
+
181
+ alias :exec_delete :exec_update
182
+
183
+ def begin_db_transaction #:nodoc:
184
+ @connection.autocommit = false
185
+ end
186
+
187
+ def transaction_isolation_levels
188
+ # Oracle database supports `READ COMMITTED` and `SERIALIZABLE`
189
+ # No read uncommitted nor repeatable read supppoted
190
+ # http://docs.oracle.com/cd/E11882_01/server.112/e26088/statements_10005.htm#SQLRF55422
191
+ {
192
+ read_committed: "READ COMMITTED",
193
+ serializable: "SERIALIZABLE"
194
+ }
195
+ end
196
+
197
+ def begin_isolated_db_transaction(isolation)
198
+ begin_db_transaction
199
+ execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
200
+ end
201
+
202
+ def commit_db_transaction #:nodoc:
203
+ @connection.commit
204
+ ensure
205
+ @connection.autocommit = true
206
+ end
207
+
208
+ def exec_rollback_db_transaction #:nodoc:
209
+ @connection.rollback
210
+ ensure
211
+ @connection.autocommit = true
212
+ end
213
+
214
+ def create_savepoint(name = current_savepoint_name) #:nodoc:
215
+ execute("SAVEPOINT #{name}")
216
+ end
217
+
218
+ def exec_rollback_to_savepoint(name = current_savepoint_name) #:nodoc:
219
+ execute("ROLLBACK TO #{name}")
220
+ end
221
+
222
+ def release_savepoint(name = current_savepoint_name) #:nodoc:
223
+ # there is no RELEASE SAVEPOINT statement in Oracle
224
+ end
225
+
226
+ # Returns default sequence name for table.
227
+ # Will take all or first 26 characters of table name and append _seq suffix
228
+ def default_sequence_name(table_name, primary_key = nil)
229
+ table_name.to_s.gsub /(^|\.)([\w$-]{1,#{sequence_name_length-4}})([\w$-]*)$/, '\1\2_seq'
230
+ end
231
+
232
+ # Inserts the given fixture into the table. Overridden to properly handle lobs.
233
+ def insert_fixture(fixture, table_name) #:nodoc:
234
+ super
235
+
236
+ if ActiveRecord::Base.pluralize_table_names
237
+ klass = table_name.to_s.singularize.camelize
238
+ else
239
+ klass = table_name.to_s.camelize
240
+ end
241
+
242
+ klass = klass.constantize rescue nil
243
+ if klass.respond_to?(:ancestors) && klass.ancestors.include?(ActiveRecord::Base)
244
+ write_lobs(table_name, klass, fixture, klass.lob_columns)
245
+ end
246
+ end
247
+
248
+ private
249
+
250
+ def select(sql, name = nil, binds = [])
251
+ exec_query(sql, name, binds)
252
+ end
253
+
254
+ end
255
+ end
256
+ end
257
+ end
@@ -0,0 +1,40 @@
1
+ module ActiveRecord #:nodoc:
2
+ module ConnectionAdapters #:nodoc:
3
+ module OracleEnhancedDirty #:nodoc:
4
+
5
+ module InstanceMethods #:nodoc:
6
+ private
7
+
8
+ def _field_changed?(attr, old_value)
9
+ new_value = read_attribute(attr)
10
+ raw_value = read_attribute_before_type_cast(attr)
11
+
12
+ if self.class.columns_hash.include?(attr.to_s)
13
+ column = column_for_attribute(attr)
14
+
15
+ # Oracle stores empty string '' as NULL
16
+ # therefore need to convert empty string value to nil if old value is nil
17
+ if column.type == :string && column.null && old_value.nil?
18
+ new_value = nil if new_value == ''
19
+ end
20
+ column.changed?(old_value, new_value, raw_value)
21
+ else
22
+ new_value != old_value
23
+ end
24
+ end
25
+
26
+ def non_zero?(value)
27
+ value !~ /\A0+(\.0+)?\z/
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+ end
34
+ end
35
+
36
+ if ActiveRecord::Base.method_defined?(:changed?)
37
+ ActiveRecord::Base.class_eval do
38
+ include ActiveRecord::ConnectionAdapters::OracleEnhancedDirty::InstanceMethods
39
+ end
40
+ end
@@ -1,6 +1,6 @@
1
1
  module ActiveRecord
2
2
  module ConnectionAdapters
3
- class OracleEnhancedAdapter < AbstractAdapter
3
+ module OracleEnhanced
4
4
  class SchemaCreation < AbstractAdapter::SchemaCreation
5
5
  private
6
6
 
@@ -44,10 +44,6 @@ module ActiveRecord
44
44
  ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_tablespaces[native_database_types[type][:name]]) rescue nil
45
45
  end
46
46
 
47
- def foreign_key_definition(to_table, options = {})
48
- @conn.foreign_key_definition(to_table, options)
49
- end
50
-
51
47
  def add_column_options!(sql, options)
52
48
  type = options[:type] || ((column = options[:column]) && column.type)
53
49
  type = type && type.to_sym
@@ -56,8 +52,7 @@ module ActiveRecord
56
52
  if type == :text
57
53
  sql << " DEFAULT #{@conn.quote(options[:default])}"
58
54
  else
59
- # from abstract adapter
60
- sql << " DEFAULT #{@conn.quote(options[:default], options[:column])}"
55
+ sql << " DEFAULT #{quote_value(options[:default], options[:column])}"
61
56
  end
62
57
  end
63
58
  # must explicitly add NULL or NOT NULL to allow change_column to work on migrations
@@ -72,18 +67,24 @@ module ActiveRecord
72
67
  end
73
68
  end
74
69
 
75
- # This method does not exist in SchemaCreation at Rails 4.0
76
- # It can be removed only when Oracle enhanced adapter supports Rails 4.1 and higher
77
- def options_include_default?(options)
78
- options.include?(:default) && !(options[:null] == false && options[:default].nil?)
70
+ def action_sql(action, dependency)
71
+ if action == 'UPDATE'
72
+ raise ArgumentError, <<-MSG.strip_heredoc
73
+ '#{action}' is not supported by Oracle
74
+ MSG
75
+ end
76
+ case dependency
77
+ when :nullify then "ON #{action} SET NULL"
78
+ when :cascade then "ON #{action} CASCADE"
79
+ else
80
+ raise ArgumentError, <<-MSG.strip_heredoc
81
+ '#{dependency}' is not supported for #{action}
82
+ Supported values are: :nullify, :cascade
83
+ MSG
84
+ end
79
85
  end
80
86
 
81
87
  end
82
-
83
- def schema_creation
84
- SchemaCreation.new self
85
- end
86
-
87
88
  end
88
89
  end
89
90
  end
@@ -0,0 +1,95 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ #TODO: Overriding `aliased_types` cause another database adapter behavior changes
4
+ #It should be addressed by supporting `create_table_definition`
5
+ class TableDefinition
6
+ private
7
+ def aliased_types(name, fallback)
8
+ fallback
9
+ end
10
+ end
11
+
12
+ module OracleEnhanced
13
+
14
+ class ForeignKeyDefinition < ActiveRecord::ConnectionAdapters::ForeignKeyDefinition
15
+ def name
16
+ if options[:name].length > OracleEnhancedAdapter::IDENTIFIER_MAX_LENGTH
17
+ ActiveSupport::Deprecation.warn "Foreign key name #{options[:name]} is too long. It will not get shorten in later version of Oracle enhanced adapter"
18
+ 'c'+Digest::SHA1.hexdigest(options[:name])[0,OracleEnhancedAdapter::IDENTIFIER_MAX_LENGTH-1]
19
+ else
20
+ options[:name]
21
+ end
22
+ end
23
+ end
24
+
25
+ class SynonymDefinition < Struct.new(:name, :table_owner, :table_name, :db_link) #:nodoc:
26
+ end
27
+
28
+ class IndexDefinition < ActiveRecord::ConnectionAdapters::IndexDefinition
29
+ attr_accessor :table, :name, :unique, :type, :parameters, :statement_parameters, :tablespace, :columns
30
+
31
+ def initialize(table, name, unique, type, parameters, statement_parameters, tablespace, columns)
32
+ @table = table
33
+ @name = name
34
+ @unique = unique
35
+ @type = type
36
+ @parameters = parameters
37
+ @statement_parameters = statement_parameters
38
+ @tablespace = tablespace
39
+ @columns = columns
40
+ super(table, name, unique, columns, nil, nil, nil, nil)
41
+ end
42
+ end
43
+
44
+ class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
45
+
46
+ def raw(name, options={})
47
+ column(name, :raw, options)
48
+ end
49
+
50
+ def virtual(* args)
51
+ options = args.extract_options!
52
+ column_names = args
53
+ column_names.each { |name| column(name, :virtual, options) }
54
+ end
55
+
56
+ def column(name, type, options = {})
57
+ if type == :virtual
58
+ default = {:type => options[:type]}
59
+ if options[:as]
60
+ default[:as] = options[:as]
61
+ elsif options[:default]
62
+ warn "[DEPRECATION] virtual column `:default` option is deprecated. Please use `:as` instead."
63
+ default[:as] = options[:default]
64
+ else
65
+ raise "No virtual column definition found."
66
+ end
67
+ options[:default] = default
68
+ end
69
+ super(name, type, options)
70
+ end
71
+
72
+ end
73
+
74
+ class AlterTable < ActiveRecord::ConnectionAdapters::AlterTable
75
+ def add_foreign_key(to_table, options)
76
+ @foreign_key_adds << OracleEnhanced::ForeignKeyDefinition.new(name, to_table, options)
77
+ end
78
+ end
79
+
80
+ class Table < ActiveRecord::ConnectionAdapters::Table
81
+ def foreign_key(to_table, options = {})
82
+ ActiveSupport::Deprecation.warn "`foreign_key` option will be deprecated. Please use `references` option"
83
+ to_table = to_table.to_s.pluralize if ActiveRecord::Base.pluralize_table_names
84
+ @base.add_foreign_key(@name, to_table, options)
85
+ end
86
+
87
+ def remove_foreign_key(options = {})
88
+ ActiveSupport::Deprecation.warn "`remove_foreign_key` option will be deprecated. Please use `remove_references` option"
89
+ @base.remove_foreign_key(@name, options)
90
+ end
91
+ end
92
+
93
+ end
94
+ end
95
+ end
@@ -7,6 +7,7 @@ module ActiveRecord #:nodoc:
7
7
  private
8
8
  alias_method_chain :tables, :oracle_enhanced
9
9
  alias_method_chain :indexes, :oracle_enhanced
10
+ alias_method_chain :foreign_keys, :oracle_enhanced
10
11
  end
11
12
  end
12
13
 
@@ -56,37 +57,8 @@ module ActiveRecord #:nodoc:
56
57
  end
57
58
  end
58
59
 
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[:references].first.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
60
+ def foreign_keys_with_oracle_enhanced(table_name, stream)
61
+ return foreign_keys_without_oracle_enhanced(table_name, stream)
90
62
  end
91
63
 
92
64
  def synonyms(stream)
@@ -166,7 +138,7 @@ module ActiveRecord #:nodoc:
166
138
  else
167
139
  tbl.print ", id: false"
168
140
  end
169
- tbl.print ", force: true"
141
+ tbl.print ", force: :cascade"
170
142
  tbl.puts " do |t|"
171
143
 
172
144
  # then dump all non-primary key columns