activerecord-oracle_enhanced-adapter 1.5.6 → 1.6.0.beta1

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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -2
  3. data/History.md +87 -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_cpk.rb → oracle_enhanced/cpk.rb} +0 -0
  12. data/lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb +257 -0
  13. data/lib/active_record/connection_adapters/{oracle_enhanced_database_tasks.rb → oracle_enhanced/database_tasks.rb} +0 -0
  14. data/lib/active_record/connection_adapters/oracle_enhanced/dirty.rb +40 -0
  15. data/lib/active_record/connection_adapters/{oracle_enhanced_jdbc_connection.rb → oracle_enhanced/jdbc_connection.rb} +0 -0
  16. data/lib/active_record/connection_adapters/{oracle_enhanced_oci_connection.rb → oracle_enhanced/oci_connection.rb} +0 -0
  17. data/lib/active_record/connection_adapters/{oracle_enhanced_procedures.rb → oracle_enhanced/procedures.rb} +0 -0
  18. data/lib/active_record/connection_adapters/{oracle_enhanced_schema_creation.rb → oracle_enhanced/schema_creation.rb} +17 -16
  19. data/lib/active_record/connection_adapters/oracle_enhanced/schema_definitions.rb +92 -0
  20. data/lib/active_record/connection_adapters/{oracle_enhanced_schema_dumper.rb → oracle_enhanced/schema_dumper.rb} +4 -32
  21. data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb +543 -0
  22. data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements_ext.rb +65 -0
  23. data/lib/active_record/connection_adapters/{oracle_enhanced_structure_dump.rb → oracle_enhanced/structure_dump.rb} +26 -4
  24. data/lib/active_record/connection_adapters/oracle_enhanced/version.rb +1 -0
  25. data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +159 -66
  26. data/lib/active_record/oracle_enhanced/type/integer.rb +13 -0
  27. data/lib/active_record/oracle_enhanced/type/raw.rb +13 -0
  28. data/lib/active_record/oracle_enhanced/type/timestamp.rb +11 -0
  29. data/lib/activerecord-oracle_enhanced-adapter.rb +1 -1
  30. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +6 -31
  31. data/spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb +1 -1
  32. data/spec/active_record/connection_adapters/oracle_enhanced_context_index_spec.rb +2 -2
  33. data/spec/active_record/connection_adapters/oracle_enhanced_cpk_spec.rb +2 -2
  34. data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +63 -63
  35. data/spec/active_record/connection_adapters/oracle_enhanced_database_tasks_spec.rb +1 -1
  36. data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +7 -13
  37. data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +21 -175
  38. data/spec/spec_config.yaml.template +10 -0
  39. data/spec/spec_helper.rb +21 -10
  40. metadata +29 -25
  41. data/lib/active_record/connection_adapters/oracle_enhanced_column_dumper.rb +0 -77
  42. data/lib/active_record/connection_adapters/oracle_enhanced_context_index.rb +0 -350
  43. data/lib/active_record/connection_adapters/oracle_enhanced_database_statements.rb +0 -262
  44. data/lib/active_record/connection_adapters/oracle_enhanced_dirty.rb +0 -45
  45. data/lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb +0 -197
  46. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb +0 -450
  47. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements_ext.rb +0 -258
  48. data/lib/active_record/connection_adapters/oracle_enhanced_version.rb +0 -1
@@ -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,92 @@
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
+ 'c'+Digest::SHA1.hexdigest(options[:name])[0,OracleEnhancedAdapter::IDENTIFIER_MAX_LENGTH-1]
18
+ else
19
+ options[:name]
20
+ end
21
+ end
22
+ end
23
+
24
+ class SynonymDefinition < Struct.new(:name, :table_owner, :table_name, :db_link) #:nodoc:
25
+ end
26
+
27
+ class IndexDefinition < ActiveRecord::ConnectionAdapters::IndexDefinition
28
+ attr_accessor :table, :name, :unique, :type, :parameters, :statement_parameters, :tablespace, :columns
29
+
30
+ def initialize(table, name, unique, type, parameters, statement_parameters, tablespace, columns)
31
+ @table = table
32
+ @name = name
33
+ @unique = unique
34
+ @type = type
35
+ @parameters = parameters
36
+ @statement_parameters = statement_parameters
37
+ @tablespace = tablespace
38
+ @columns = columns
39
+ super(table, name, unique, columns, nil, nil, nil, nil)
40
+ end
41
+ end
42
+
43
+ class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
44
+
45
+ def raw(name, options={})
46
+ column(name, :raw, options)
47
+ end
48
+
49
+ def virtual(* args)
50
+ options = args.extract_options!
51
+ column_names = args
52
+ column_names.each { |name| column(name, :virtual, options) }
53
+ end
54
+
55
+ def column(name, type, options = {})
56
+ if type == :virtual
57
+ default = {:type => options[:type]}
58
+ if options[:as]
59
+ default[:as] = options[:as]
60
+ elsif options[:default]
61
+ warn "[DEPRECATION] virtual column `:default` option is deprecated. Please use `:as` instead."
62
+ default[:as] = options[:default]
63
+ else
64
+ raise "No virtual column definition found."
65
+ end
66
+ options[:default] = default
67
+ end
68
+ super(name, type, options)
69
+ end
70
+
71
+ end
72
+
73
+ class AlterTable < ActiveRecord::ConnectionAdapters::AlterTable
74
+ def add_foreign_key(to_table, options)
75
+ @foreign_key_adds << OracleEnhanced::ForeignKeyDefinition.new(name, to_table, options)
76
+ end
77
+ end
78
+
79
+ class Table < ActiveRecord::ConnectionAdapters::Table
80
+ def foreign_key(to_table, options = {})
81
+ to_table = to_table.to_s.pluralize if ActiveRecord::Base.pluralize_table_names
82
+ @base.add_foreign_key(@name, to_table, options)
83
+ end
84
+
85
+ def remove_foreign_key(options = {})
86
+ @base.remove_foreign_key(@name, options)
87
+ end
88
+ end
89
+
90
+ end
91
+ end
92
+ end