activerecord-oracle_enhanced-adapter 1.5.6 → 1.6.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.travis/oracle/download.sh +14 -0
  3. data/.travis/oracle/install.sh +31 -0
  4. data/.travis/setup_accounts.sh +9 -0
  5. data/.travis.yml +39 -0
  6. data/Gemfile +8 -8
  7. data/History.md +189 -0
  8. data/README.md +388 -178
  9. data/RUNNING_TESTS.md +11 -6
  10. data/VERSION +1 -1
  11. data/activerecord-oracle_enhanced-adapter.gemspec +29 -26
  12. data/lib/active_record/connection_adapters/{oracle_enhanced_column.rb → oracle_enhanced/column.rb} +14 -63
  13. data/lib/active_record/connection_adapters/oracle_enhanced/column_dumper.rb +66 -0
  14. data/lib/active_record/connection_adapters/{oracle_enhanced_connection.rb → oracle_enhanced/connection.rb} +2 -2
  15. data/lib/active_record/connection_adapters/oracle_enhanced/context_index.rb +347 -0
  16. data/lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb +260 -0
  17. data/lib/active_record/connection_adapters/oracle_enhanced/dirty.rb +40 -0
  18. data/lib/active_record/connection_adapters/{oracle_enhanced_jdbc_connection.rb → oracle_enhanced/jdbc_connection.rb} +13 -4
  19. data/lib/active_record/connection_adapters/{oracle_enhanced_oci_connection.rb → oracle_enhanced/oci_connection.rb} +11 -5
  20. data/lib/active_record/connection_adapters/{oracle_enhanced_procedures.rb → oracle_enhanced/procedures.rb} +1 -1
  21. data/lib/active_record/connection_adapters/{oracle_enhanced_schema_creation.rb → oracle_enhanced/schema_creation.rb} +34 -35
  22. data/lib/active_record/connection_adapters/oracle_enhanced/schema_definitions.rb +95 -0
  23. data/lib/active_record/connection_adapters/{oracle_enhanced_schema_dumper.rb → oracle_enhanced/schema_dumper.rb} +14 -37
  24. data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb +562 -0
  25. data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements_ext.rb +65 -0
  26. data/lib/active_record/connection_adapters/{oracle_enhanced_structure_dump.rb → oracle_enhanced/structure_dump.rb} +63 -14
  27. data/lib/active_record/connection_adapters/oracle_enhanced/version.rb +1 -0
  28. data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +171 -73
  29. data/lib/active_record/oracle_enhanced/type/integer.rb +13 -0
  30. data/lib/active_record/oracle_enhanced/type/raw.rb +13 -0
  31. data/lib/active_record/oracle_enhanced/type/timestamp.rb +11 -0
  32. data/lib/activerecord-oracle_enhanced-adapter.rb +1 -1
  33. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +127 -49
  34. data/spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb +46 -5
  35. data/spec/active_record/connection_adapters/oracle_enhanced_context_index_spec.rb +11 -3
  36. data/spec/active_record/connection_adapters/oracle_enhanced_cpk_spec.rb +3 -3
  37. data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +151 -78
  38. data/spec/active_record/connection_adapters/oracle_enhanced_database_tasks_spec.rb +4 -4
  39. data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +10 -16
  40. data/spec/active_record/connection_adapters/oracle_enhanced_emulate_oracle_adapter_spec.rb +1 -1
  41. data/spec/active_record/connection_adapters/oracle_enhanced_procedures_spec.rb +5 -5
  42. data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +65 -181
  43. data/spec/active_record/connection_adapters/oracle_enhanced_schema_statements_spec.rb +114 -11
  44. data/spec/active_record/connection_adapters/oracle_enhanced_structure_dump_spec.rb +17 -1
  45. data/spec/spec_config.yaml.template +11 -0
  46. data/spec/spec_helper.rb +31 -12
  47. data/spec/support/alter_system_user_password.sql +2 -0
  48. data/spec/support/create_oracle_enhanced_users.sql +31 -0
  49. metadata +37 -27
  50. data/lib/active_record/connection_adapters/oracle_enhanced_column_dumper.rb +0 -77
  51. data/lib/active_record/connection_adapters/oracle_enhanced_context_index.rb +0 -350
  52. data/lib/active_record/connection_adapters/oracle_enhanced_database_statements.rb +0 -262
  53. data/lib/active_record/connection_adapters/oracle_enhanced_dirty.rb +0 -45
  54. data/lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb +0 -197
  55. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb +0 -450
  56. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements_ext.rb +0 -258
  57. data/lib/active_record/connection_adapters/oracle_enhanced_version.rb +0 -1
  58. /data/lib/active_record/connection_adapters/{oracle_enhanced_cpk.rb → oracle_enhanced/cpk.rb} +0 -0
  59. /data/lib/active_record/connection_adapters/{oracle_enhanced_database_tasks.rb → oracle_enhanced/database_tasks.rb} +0 -0
@@ -10,7 +10,7 @@ module ActiveRecord #:nodoc:
10
10
  "CREATE SEQUENCE \"#{seq}\""
11
11
  end
12
12
  select_values("SELECT table_name FROM all_tables t
13
- WHERE owner = SYS_CONTEXT('userenv', 'session_user') AND secondary = 'N'
13
+ WHERE owner = SYS_CONTEXT('userenv', 'current_schema') AND secondary = 'N'
14
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
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
16
  ORDER BY 1").each do |table_name|
@@ -34,6 +34,8 @@ module ActiveRecord #:nodoc:
34
34
  structure << ddl
35
35
  structure << structure_dump_indexes(table_name)
36
36
  structure << structure_dump_unique_keys(table_name)
37
+ structure << structure_dump_table_comments(table_name)
38
+ structure << structure_dump_column_comments(table_name)
37
39
  end
38
40
 
39
41
  join_with_statement_token(structure) << structure_dump_fk_constraints
@@ -77,7 +79,7 @@ module ActiveRecord #:nodoc:
77
79
  ON a.constraint_name = c.constraint_name
78
80
  WHERE c.table_name = '#{table.upcase}'
79
81
  AND c.constraint_type = 'P'
80
- AND c.owner = SYS_CONTEXT('userenv', 'session_user')
82
+ AND c.owner = SYS_CONTEXT('userenv', 'current_schema')
81
83
  SQL
82
84
  pks.each do |row|
83
85
  opts[:name] = row['constraint_name']
@@ -95,7 +97,7 @@ module ActiveRecord #:nodoc:
95
97
  ON a.constraint_name = c.constraint_name
96
98
  WHERE c.table_name = '#{table.upcase}'
97
99
  AND c.constraint_type = 'U'
98
- AND c.owner = SYS_CONTEXT('userenv', 'session_user')
100
+ AND c.owner = SYS_CONTEXT('userenv', 'current_schema')
99
101
  SQL
100
102
  uks.each do |uk|
101
103
  keys[uk['constraint_name']] ||= []
@@ -123,7 +125,7 @@ module ActiveRecord #:nodoc:
123
125
  end
124
126
 
125
127
  def structure_dump_fk_constraints #:nodoc:
126
- fks = select_all("SELECT table_name FROM all_tables WHERE owner = SYS_CONTEXT('userenv', 'session_user') ORDER BY 1").map do |table|
128
+ fks = select_all("SELECT table_name FROM all_tables WHERE owner = SYS_CONTEXT('userenv', 'current_schema') ORDER BY 1").map do |table|
127
129
  if respond_to?(:foreign_keys) && (foreign_keys = foreign_keys(table["table_name"])).any?
128
130
  foreign_keys.map do |fk|
129
131
  sql = "ALTER TABLE #{quote_table_name(fk.from_table)} ADD CONSTRAINT #{quote_column_name(fk.options[:name])} "
@@ -134,12 +136,59 @@ module ActiveRecord #:nodoc:
134
136
  join_with_statement_token(fks)
135
137
  end
136
138
 
137
- def dump_schema_information #:nodoc:
138
- sm_table = ActiveRecord::Migrator.schema_migrations_table_name
139
- migrated = select_values("SELECT version FROM #{sm_table} ORDER BY version")
140
- join_with_statement_token(migrated.map{|v| "INSERT INTO #{sm_table} (version) VALUES ('#{v}')" })
139
+ def structure_dump_table_comments(table_name)
140
+ comments = []
141
+ comment = table_comment(table_name)
142
+
143
+ unless comment.nil?
144
+ comments << "COMMENT ON TABLE #{quote_table_name(table_name)} IS '#{quote_string(comment)}'"
145
+ end
146
+
147
+ join_with_statement_token(comments)
148
+ end
149
+
150
+ def structure_dump_column_comments(table_name)
151
+ comments = []
152
+ columns = select_values("SELECT column_name FROM user_tab_columns WHERE table_name = '#{table_name}' ORDER BY column_id")
153
+
154
+ columns.each do |column|
155
+ comment = column_comment(table_name, column)
156
+ unless comment.nil?
157
+ comments << "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{quote_column_name(column)} IS '#{quote_string(comment)}'"
158
+ end
159
+ end
160
+
161
+ join_with_statement_token(comments)
141
162
  end
142
163
 
164
+ def foreign_key_definition(to_table, options = {}) #:nodoc:
165
+ columns = Array(options[:column] || options[:columns])
166
+
167
+ if columns.size > 1
168
+ # composite foreign key
169
+ columns_sql = columns.map {|c| quote_column_name(c)}.join(',')
170
+ references = options[:references] || columns
171
+ references_sql = references.map {|c| quote_column_name(c)}.join(',')
172
+ else
173
+ columns_sql = quote_column_name(columns.first || "#{to_table.to_s.singularize}_id")
174
+ references = options[:references] ? options[:references].first : nil
175
+ references_sql = quote_column_name(options[:primary_key] || references || "id")
176
+ end
177
+
178
+ table_name = to_table
179
+
180
+ sql = "FOREIGN KEY (#{columns_sql}) REFERENCES #{quote_table_name(table_name)}(#{references_sql})"
181
+
182
+ case options[:dependent]
183
+ when :nullify
184
+ sql << " ON DELETE SET NULL"
185
+ when :delete
186
+ sql << " ON DELETE CASCADE"
187
+ end
188
+ sql
189
+ end
190
+
191
+
143
192
  # Extract all stored procedures, packages, synonyms and views.
144
193
  def structure_dump_db_stored_code #:nodoc:
145
194
  structure = []
@@ -147,14 +196,14 @@ module ActiveRecord #:nodoc:
147
196
  FROM all_source
148
197
  WHERE type IN ('PROCEDURE', 'PACKAGE', 'PACKAGE BODY', 'FUNCTION', 'TRIGGER', 'TYPE')
149
198
  AND name NOT LIKE 'BIN$%'
150
- AND owner = SYS_CONTEXT('userenv', 'session_user') ORDER BY type").each do |source|
199
+ AND owner = SYS_CONTEXT('userenv', 'current_schema') ORDER BY type").each do |source|
151
200
  ddl = "CREATE OR REPLACE \n"
152
201
  select_all(%Q{
153
202
  SELECT text
154
203
  FROM all_source
155
204
  WHERE name = '#{source['name']}'
156
205
  AND type = '#{source['type']}'
157
- AND owner = SYS_CONTEXT('userenv', 'session_user')
206
+ AND owner = SYS_CONTEXT('userenv', 'current_schema')
158
207
  ORDER BY line
159
208
  }).each do |row|
160
209
  ddl << row['text']
@@ -171,7 +220,7 @@ module ActiveRecord #:nodoc:
171
220
  # export synonyms
172
221
  select_all("SELECT owner, synonym_name, table_name, table_owner
173
222
  FROM all_synonyms
174
- WHERE owner = SYS_CONTEXT('userenv', 'session_user') ").each do |synonym|
223
+ WHERE owner = SYS_CONTEXT('userenv', 'current_schema') ").each do |synonym|
175
224
  structure << "CREATE OR REPLACE #{synonym['owner'] == 'PUBLIC' ? 'PUBLIC' : '' } SYNONYM #{synonym['synonym_name']}
176
225
  FOR #{synonym['table_owner']}.#{synonym['table_name']}"
177
226
  end
@@ -184,7 +233,7 @@ module ActiveRecord #:nodoc:
184
233
  "DROP SEQUENCE \"#{seq}\""
185
234
  end
186
235
  select_values("SELECT table_name from all_tables t
187
- WHERE owner = SYS_CONTEXT('userenv', 'session_user') AND secondary = 'N'
236
+ WHERE owner = SYS_CONTEXT('userenv', 'current_schema') AND secondary = 'N'
188
237
  AND NOT EXISTS (SELECT mv.mview_name FROM all_mviews mv WHERE mv.owner = t.owner AND mv.mview_name = t.table_name)
189
238
  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
239
  ORDER BY 1").each do |table|
@@ -196,7 +245,7 @@ module ActiveRecord #:nodoc:
196
245
  def temp_table_drop #:nodoc:
197
246
  join_with_statement_token(select_values(
198
247
  "SELECT table_name FROM all_tables
199
- WHERE owner = SYS_CONTEXT('userenv', 'session_user') AND secondary = 'N' AND temporary = 'Y' ORDER BY 1").map do |table|
248
+ WHERE owner = SYS_CONTEXT('userenv', 'current_schema') AND secondary = 'N' AND temporary = 'Y' ORDER BY 1").map do |table|
200
249
  "DROP TABLE \"#{table}\" CASCADE CONSTRAINTS"
201
250
  end)
202
251
  end
@@ -258,7 +307,7 @@ module ActiveRecord #:nodoc:
258
307
  AND table_name = '#{table.upcase}'
259
308
  SQL
260
309
  # feature not supported previous to 11g
261
- rescue ActiveRecord::StatementInvalid => e
310
+ rescue ActiveRecord::StatementInvalid => _e
262
311
  []
263
312
  end
264
313
  end
@@ -0,0 +1 @@
1
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter::VERSION = File.read(File.expand_path('../../../../../VERSION', __FILE__)).chomp
@@ -30,9 +30,13 @@
30
30
  # portions Copyright 2005 Graham Jenkins
31
31
 
32
32
  require 'active_record/connection_adapters/abstract_adapter'
33
- require 'active_record/connection_adapters/oracle_enhanced_connection'
33
+ require 'active_record/connection_adapters/oracle_enhanced/connection'
34
+ require 'active_record/connection_adapters/oracle_enhanced/database_statements'
35
+ require 'active_record/connection_adapters/oracle_enhanced/schema_statements'
36
+ require 'active_record/connection_adapters/oracle_enhanced/column_dumper'
37
+ require 'active_record/connection_adapters/oracle_enhanced/context_index'
34
38
 
35
- require 'active_record/connection_adapters/oracle_enhanced_column'
39
+ require 'active_record/connection_adapters/oracle_enhanced/column'
36
40
 
37
41
  require 'digest/sha1'
38
42
 
@@ -131,7 +135,7 @@ module ActiveRecord
131
135
 
132
136
  def record_changed_lobs
133
137
  @changed_lob_columns = self.class.lob_columns.select do |col|
134
- (self.class.serialized_attributes.keys.include?(col.name) || self.send(:"#{col.name}_changed?")) && !self.class.readonly_attributes.to_a.include?(col.name)
138
+ self.attribute_changed?(col.name) && !self.class.readonly_attributes.to_a.include?(col.name)
135
139
  end
136
140
  end
137
141
  end
@@ -218,6 +222,15 @@ module ActiveRecord
218
222
  # * <tt>:nls_time_tz_format</tt>
219
223
  #
220
224
  class OracleEnhancedAdapter < AbstractAdapter
225
+ # TODO: Use relative
226
+ include ActiveRecord::ConnectionAdapters::OracleEnhanced::DatabaseStatements
227
+ include ActiveRecord::ConnectionAdapters::OracleEnhanced::SchemaStatements
228
+ include ActiveRecord::ConnectionAdapters::OracleEnhanced::ColumnDumper
229
+ include ActiveRecord::ConnectionAdapters::OracleEnhanced::ContextIndex
230
+
231
+ def schema_creation
232
+ OracleEnhanced::SchemaCreation.new self
233
+ end
221
234
 
222
235
  ##
223
236
  # :singleton-method:
@@ -270,13 +283,6 @@ module ActiveRecord
270
283
  cattr_accessor :emulate_dates_by_column_name
271
284
  self.emulate_dates_by_column_name = false
272
285
 
273
- ##
274
- # :singleton-method:
275
- # Specify how `NUMBER` datatype columns, without precision and scale, are handled in Rails world.
276
- # Default is :decimal and other valid option is :float. Be wary of setting it to other values.
277
- cattr_accessor :number_datatype_coercion
278
- self.number_datatype_coercion = :decimal
279
-
280
286
  # Check column name to identify if it is Date (and not Time) column.
281
287
  # Is used if +emulate_dates_by_column_name+ option is set to +true+.
282
288
  # Override this method definition in initializer file if different Date column recognition is needed.
@@ -311,7 +317,7 @@ module ActiveRecord
311
317
  # Is used if +emulate_integers_by_column_name+ option is set to +true+.
312
318
  # Override this method definition in initializer file if different Integer column recognition is needed.
313
319
  def self.is_integer_column?(name, table_name = nil)
314
- !!(name =~ /(^|_)id$/i)
320
+ name =~ /(^|_)id$/i
315
321
  end
316
322
 
317
323
  ##
@@ -326,9 +332,9 @@ module ActiveRecord
326
332
  # Check column name to identify if it is boolean (and not String) column.
327
333
  # Is used if +emulate_booleans_from_strings+ option is set to +true+.
328
334
  # Override this method definition in initializer file if different boolean column recognition is needed.
329
- def self.is_boolean_column?(name, field_type, table_name = nil)
330
- return true if ["CHAR(1)","VARCHAR2(1)"].include?(field_type)
331
- field_type =~ /^VARCHAR2/ && (name =~ /_flag$/i || name =~ /_yn$/i)
335
+ def self.is_boolean_column?(name, sql_type, table_name = nil)
336
+ return true if ["CHAR(1)","VARCHAR2(1)"].include?(sql_type)
337
+ sql_type =~ /^VARCHAR2/ && (name =~ /_flag$/i || name =~ /_yn$/i)
332
338
  end
333
339
 
334
340
  # How boolean value should be quoted to String.
@@ -383,21 +389,18 @@ module ActiveRecord
383
389
  end
384
390
  end
385
391
 
386
- class BindSubstitution < Arel::Visitors::Oracle #:nodoc:
387
- include Arel::Visitors::BindVisitor
388
- end
389
-
390
392
  def initialize(connection, logger, config) #:nodoc:
391
393
  super(connection, logger)
392
394
  @quoted_column_names, @quoted_table_names = {}, {}
393
395
  @config = config
394
396
  @statements = StatementPool.new(connection, config.fetch(:statement_limit) { 250 })
395
397
  @enable_dbms_output = false
396
- if config.fetch(:prepared_statements) { true }
397
- @visitor = Arel::Visitors::Oracle.new self
398
+ @visitor = Arel::Visitors::Oracle.new self
399
+
400
+ if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
398
401
  @prepared_statements = true
399
402
  else
400
- @visitor = unprepared_visitor
403
+ @prepared_statements = false
401
404
  end
402
405
  end
403
406
 
@@ -423,7 +426,13 @@ module ActiveRecord
423
426
  true
424
427
  end
425
428
 
426
- NUMBER_MAX_PRECISION = 38
429
+ def supports_foreign_keys?
430
+ true
431
+ end
432
+
433
+ def supports_views?
434
+ true
435
+ end
427
436
 
428
437
  #:stopdoc:
429
438
  DEFAULT_NLS_PARAMETERS = {
@@ -448,11 +457,11 @@ module ActiveRecord
448
457
 
449
458
  #:stopdoc:
450
459
  NATIVE_DATABASE_TYPES = {
451
- :primary_key => "NUMBER(#{NUMBER_MAX_PRECISION}) NOT NULL PRIMARY KEY",
460
+ :primary_key => "NUMBER(38) NOT NULL PRIMARY KEY",
452
461
  :string => { :name => "VARCHAR2", :limit => 255 },
453
462
  :text => { :name => "CLOB" },
454
- :integer => { :name => "NUMBER", :limit => NUMBER_MAX_PRECISION },
455
- :float => { :name => "NUMBER" },
463
+ :integer => { :name => "NUMBER", :limit => 38 },
464
+ :float => { :name => "BINARY_FLOAT" },
456
465
  :decimal => { :name => "DECIMAL" },
457
466
  :datetime => { :name => "DATE" },
458
467
  # changed to native TIMESTAMP type
@@ -462,7 +471,8 @@ module ActiveRecord
462
471
  :date => { :name => "DATE" },
463
472
  :binary => { :name => "BLOB" },
464
473
  :boolean => { :name => "NUMBER", :limit => 1 },
465
- :raw => { :name => "RAW", :limit => 2000 }
474
+ :raw => { :name => "RAW", :limit => 2000 },
475
+ :bigint => { :name => "NUMBER", :limit => 19 }
466
476
  }
467
477
  # if emulate_booleans_from_strings then store booleans in VARCHAR2
468
478
  NATIVE_DATABASE_TYPES_BOOLEAN_STRINGS = NATIVE_DATABASE_TYPES.dup.merge(
@@ -667,22 +677,26 @@ module ActiveRecord
667
677
 
668
678
  # Cast a +value+ to a type that the database understands.
669
679
  def type_cast(value, column)
670
- case value
671
- when true, false
672
- if emulate_booleans_from_strings || column && column.type == :string
673
- self.class.boolean_to_string(value)
674
- else
675
- value ? 1 : 0
676
- end
677
- when Date, Time
678
- if value.acts_like?(:time)
679
- zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
680
- value.respond_to?(zone_conversion_method) ? value.send(zone_conversion_method) : value
680
+ if column && column.cast_type.is_a?(Type::Serialized)
681
+ super
682
+ else
683
+ case value
684
+ when true, false
685
+ if emulate_booleans_from_strings || column && column.type == :string
686
+ self.class.boolean_to_string(value)
687
+ else
688
+ value ? 1 : 0
689
+ end
690
+ when Date, Time
691
+ if value.acts_like?(:time)
692
+ zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
693
+ value.respond_to?(zone_conversion_method) ? value.send(zone_conversion_method) : value
694
+ else
695
+ value
696
+ end
681
697
  else
682
- value
698
+ super
683
699
  end
684
- else
685
- super
686
700
  end
687
701
  end
688
702
 
@@ -794,8 +808,8 @@ module ActiveRecord
794
808
  select NVL(max(#{quote_column_name(primary_key)}),0) + 1 from #{quote_table_name(table_name)}
795
809
  ", new_start_value)
796
810
 
797
- execute ("DROP SEQUENCE #{quote_table_name(sequence_name)}")
798
- execute ("CREATE SEQUENCE #{quote_table_name(sequence_name)} START WITH #{new_start_value}")
811
+ execute "DROP SEQUENCE #{quote_table_name(sequence_name)}"
812
+ execute "CREATE SEQUENCE #{quote_table_name(sequence_name)} START WITH #{new_start_value}"
799
813
  end
800
814
  end
801
815
 
@@ -811,8 +825,8 @@ module ActiveRecord
811
825
  columns.each do |col|
812
826
  value = attributes[col.name]
813
827
  # changed sequence of next two lines - should check if value is nil before converting to yaml
814
- next if value.nil? || (value == '')
815
- value = value.to_yaml if col.text? && klass.serialized_attributes[col.name]
828
+ next if value.blank?
829
+ value = col.cast_type.type_cast_for_database(value)
816
830
  uncached do
817
831
  sql = is_with_cpk ? "SELECT #{quote_column_name(col.name)} FROM #{quote_table_name(table_name)} WHERE #{klass.composite_where_clause(id)} FOR UPDATE" :
818
832
  "SELECT #{quote_column_name(col.name)} FROM #{quote_table_name(table_name)} WHERE #{quote_column_name(klass.primary_key)} = #{id} FOR UPDATE"
@@ -835,27 +849,32 @@ module ActiveRecord
835
849
  select_value("SELECT SYS_CONTEXT('userenv', 'session_user') FROM dual")
836
850
  end
837
851
 
852
+ # Current database session schema
853
+ def current_schema
854
+ select_value("SELECT SYS_CONTEXT('userenv', 'current_schema') FROM dual")
855
+ end
856
+
838
857
  # Default tablespace name of current user
839
858
  def default_tablespace
840
- select_value("SELECT LOWER(default_tablespace) FROM user_users WHERE username = SYS_CONTEXT('userenv', 'session_user')")
859
+ select_value("SELECT LOWER(default_tablespace) FROM user_users WHERE username = SYS_CONTEXT('userenv', 'current_schema')")
841
860
  end
842
861
 
843
862
  def tables(name = nil) #:nodoc:
844
863
  select_values(
845
- "SELECT DECODE(table_name, UPPER(table_name), LOWER(table_name), table_name) FROM all_tables WHERE owner = SYS_CONTEXT('userenv', 'session_user') AND secondary = 'N'",
864
+ "SELECT DECODE(table_name, UPPER(table_name), LOWER(table_name), table_name) FROM all_tables WHERE owner = SYS_CONTEXT('userenv', 'current_schema') AND secondary = 'N'",
846
865
  name)
847
866
  end
848
867
 
849
868
  # Will return true if database object exists (to be able to use also views and synonyms for ActiveRecord models)
850
869
  def table_exists?(table_name)
851
- (owner, table_name, db_link) = @connection.describe(table_name)
870
+ (_owner, table_name, _db_link) = @connection.describe(table_name)
852
871
  true
853
872
  rescue
854
873
  false
855
874
  end
856
875
 
857
876
  def materialized_views #:nodoc:
858
- select_values("SELECT LOWER(mview_name) FROM all_mviews WHERE owner = SYS_CONTEXT('userenv', 'session_user')")
877
+ select_values("SELECT LOWER(mview_name) FROM all_mviews WHERE owner = SYS_CONTEXT('userenv', 'current_schema')")
859
878
  end
860
879
 
861
880
  cattr_accessor :all_schema_indexes #:nodoc:
@@ -906,7 +925,7 @@ module ActiveRecord
906
925
  statement_parameters = $1
907
926
  end
908
927
  end
909
- all_schema_indexes << OracleEnhancedIndexDefinition.new(row['table_name'], row['index_name'],
928
+ all_schema_indexes << OracleEnhanced::IndexDefinition.new(row['table_name'], row['index_name'],
910
929
  row['uniqueness'] == "UNIQUE", row['index_type'] == 'DOMAIN' ? "#{row['ityp_owner']}.#{row['ityp_name']}" : nil,
911
930
  row['parameters'], statement_parameters,
912
931
  row['tablespace_name'] == default_tablespace_name ? nil : row['tablespace_name'], [])
@@ -1038,7 +1057,7 @@ module ActiveRecord
1038
1057
  end.map do |row|
1039
1058
  limit, scale = row['limit'], row['scale']
1040
1059
  if limit || scale
1041
- row['sql_type'] += "(#{(limit || NUMBER_MAX_PRECISION).to_i}" + ((scale = scale.to_i) > 0 ? ",#{scale})" : ")")
1060
+ row['sql_type'] += "(#{(limit || 38).to_i}" + ((scale = scale.to_i) > 0 ? ",#{scale})" : ")")
1042
1061
  end
1043
1062
 
1044
1063
  if row['sql_type_owner']
@@ -1055,19 +1074,61 @@ module ActiveRecord
1055
1074
  # match newlines.
1056
1075
  row['data_default'].sub!(/^'(.*)'$/m, '\1')
1057
1076
  row['data_default'] = nil if row['data_default'] =~ /^(null|empty_[bc]lob\(\))$/i
1077
+ # TODO: Needs better fix to fallback "N" to false
1078
+ row['data_default'] = false if (row['data_default'] == "N" && OracleEnhancedAdapter.emulate_booleans_from_strings)
1058
1079
  end
1059
1080
 
1060
- OracleEnhancedColumn.new(oracle_downcase(row['name']),
1081
+ # TODO: Consider to extract another method such as `get_cast_type`
1082
+ case row['sql_type']
1083
+ when /decimal|numeric|number/i
1084
+ if get_type_for_column(table_name, oracle_downcase(row['name'])) == :integer
1085
+ cast_type = ActiveRecord::OracleEnhanced::Type::Integer.new
1086
+ elsif OracleEnhancedAdapter.emulate_booleans && row['sql_type'].upcase == "NUMBER(1)"
1087
+ cast_type = Type::Boolean.new
1088
+ elsif OracleEnhancedAdapter.emulate_integers_by_column_name && OracleEnhancedAdapter.is_integer_column?(row['name'], table_name)
1089
+ cast_type = ActiveRecord::OracleEnhanced::Type::Integer.new
1090
+ else
1091
+ cast_type = lookup_cast_type(row['sql_type'])
1092
+ end
1093
+ when /char/i
1094
+ if get_type_for_column(table_name, oracle_downcase(row['name'])) == :string
1095
+ cast_type = Type::String.new
1096
+ elsif get_type_for_column(table_name, oracle_downcase(row['name'])) == :boolean
1097
+ cast_type = Type::Boolean.new
1098
+ elsif OracleEnhancedAdapter.emulate_booleans_from_strings && OracleEnhancedAdapter.is_boolean_column?(row['name'], row['sql_type'], table_name)
1099
+ cast_type = Type::Boolean.new
1100
+ else
1101
+ cast_type = lookup_cast_type(row['sql_type'])
1102
+ end
1103
+ when /date/i
1104
+ if get_type_for_column(table_name, oracle_downcase(row['name'])) == :date
1105
+ cast_type = Type::Date.new
1106
+ elsif get_type_for_column(table_name, oracle_downcase(row['name'])) == :datetime
1107
+ cast_type = Type::DateTime.new
1108
+ elsif OracleEnhancedAdapter.emulate_dates_by_column_name && OracleEnhancedAdapter.is_date_column?(row['name'], table_name)
1109
+ cast_type = Type::Date.new
1110
+ else
1111
+ cast_type = lookup_cast_type(row['sql_type'])
1112
+ end
1113
+ else
1114
+ cast_type = lookup_cast_type(row['sql_type'])
1115
+ end
1116
+
1117
+ new_column(oracle_downcase(row['name']),
1061
1118
  row['data_default'],
1119
+ cast_type,
1062
1120
  row['sql_type'],
1063
1121
  row['nullable'] == 'Y',
1064
- # pass table name for table specific column definitions
1065
1122
  table_name,
1066
- # pass column type if specified in class definition
1067
- get_type_for_column(table_name, oracle_downcase(row['name'])), is_virtual)
1123
+ is_virtual,
1124
+ false )
1068
1125
  end
1069
1126
  end
1070
1127
 
1128
+ def new_column(name, default, cast_type, sql_type = nil, null = true, table_name = nil, virtual=false, returning_id=false)
1129
+ OracleEnhancedColumn.new(name, default, cast_type, sql_type, null, table_name, virtual, returning_id)
1130
+ end
1131
+
1071
1132
  # used just in tests to clear column cache
1072
1133
  def clear_columns_cache #:nodoc:
1073
1134
  @@columns_cache = nil
@@ -1207,6 +1268,34 @@ module ActiveRecord
1207
1268
 
1208
1269
  protected
1209
1270
 
1271
+ def initialize_type_map(m)
1272
+ super
1273
+ # oracle
1274
+ register_class_with_limit m, %r(date)i, Type::DateTime
1275
+ register_class_with_limit m, %r(raw)i, ActiveRecord::OracleEnhanced::Type::Raw
1276
+ register_class_with_limit m, %r(timestamp)i, ActiveRecord::OracleEnhanced::Type::Timestamp
1277
+
1278
+ m.register_type(%r(NUMBER)i) do |sql_type|
1279
+ scale = extract_scale(sql_type)
1280
+ precision = extract_precision(sql_type)
1281
+ limit = extract_limit(sql_type)
1282
+ if scale == 0
1283
+ ActiveRecord::OracleEnhanced::Type::Integer.new(precision: precision, limit: limit)
1284
+ else
1285
+ Type::Decimal.new(precision: precision, scale: scale)
1286
+ end
1287
+ end
1288
+ end
1289
+
1290
+ def extract_limit(sql_type) #:nodoc:
1291
+ case sql_type
1292
+ when /^bigint/i
1293
+ 19
1294
+ when /\((.*)\)/
1295
+ $1.to_i
1296
+ end
1297
+ end
1298
+
1210
1299
  def translate_exception(exception, message) #:nodoc:
1211
1300
  case @connection.error_code(exception)
1212
1301
  when 1
@@ -1220,6 +1309,10 @@ module ActiveRecord
1220
1309
 
1221
1310
  private
1222
1311
 
1312
+ def select(sql, name = nil, binds = [])
1313
+ exec_query(sql, name, binds)
1314
+ end
1315
+
1223
1316
  def oracle_downcase(column_name)
1224
1317
  @connection.oracle_downcase(column_name)
1225
1318
  end
@@ -1256,12 +1349,8 @@ module ActiveRecord
1256
1349
  end
1257
1350
 
1258
1351
  protected
1259
- def log(sql, name, binds = nil) #:nodoc:
1260
- if binds
1261
- super sql, name, binds
1262
- else
1263
- super sql, name
1264
- end
1352
+ def log(sql, name = "SQL", binds = [], statement_name = nil) #:nodoc:
1353
+ super
1265
1354
  ensure
1266
1355
  log_dbms_output if dbms_output_enabled?
1267
1356
  end
@@ -1289,38 +1378,47 @@ module ActiveRecord
1289
1378
  end
1290
1379
 
1291
1380
  # Implementation of standard schema definition statements and extensions for schema definition
1292
- require 'active_record/connection_adapters/oracle_enhanced_schema_statements'
1293
- require 'active_record/connection_adapters/oracle_enhanced_schema_statements_ext'
1381
+ require 'active_record/connection_adapters/oracle_enhanced/schema_statements'
1382
+ require 'active_record/connection_adapters/oracle_enhanced/schema_statements_ext'
1294
1383
 
1295
1384
  # Extensions for schema definition
1296
- require 'active_record/connection_adapters/oracle_enhanced_schema_definitions'
1385
+ require 'active_record/connection_adapters/oracle_enhanced/schema_definitions'
1297
1386
 
1298
1387
  # Extensions for context index definition
1299
- require 'active_record/connection_adapters/oracle_enhanced_context_index'
1388
+ require 'active_record/connection_adapters/oracle_enhanced/context_index'
1300
1389
 
1301
1390
  # Load additional methods for composite_primary_keys support
1302
- require 'active_record/connection_adapters/oracle_enhanced_cpk'
1391
+ require 'active_record/connection_adapters/oracle_enhanced/cpk'
1303
1392
 
1304
1393
  # Load patch for dirty tracking methods
1305
- require 'active_record/connection_adapters/oracle_enhanced_dirty'
1394
+ require 'active_record/connection_adapters/oracle_enhanced/dirty'
1306
1395
 
1307
1396
  # Patches and enhancements for schema dumper
1308
- require 'active_record/connection_adapters/oracle_enhanced_schema_dumper'
1397
+ require 'active_record/connection_adapters/oracle_enhanced/schema_dumper'
1309
1398
 
1310
1399
  # Implementation of structure dump
1311
- require 'active_record/connection_adapters/oracle_enhanced_structure_dump'
1400
+ require 'active_record/connection_adapters/oracle_enhanced/structure_dump'
1312
1401
 
1313
- require 'active_record/connection_adapters/oracle_enhanced_version'
1402
+ require 'active_record/connection_adapters/oracle_enhanced/version'
1314
1403
 
1315
1404
  module ActiveRecord
1316
- autoload :OracleEnhancedProcedures, 'active_record/connection_adapters/oracle_enhanced_procedures'
1405
+ autoload :OracleEnhancedProcedures, 'active_record/connection_adapters/oracle_enhanced/procedures'
1317
1406
  end
1318
1407
 
1319
1408
  # Patches and enhancements for column dumper
1320
- require 'active_record/connection_adapters/oracle_enhanced_column_dumper'
1409
+ require 'active_record/connection_adapters/oracle_enhanced/column_dumper'
1321
1410
 
1322
1411
  # Moved SchemaCreation class
1323
- require 'active_record/connection_adapters/oracle_enhanced_schema_creation'
1412
+ require 'active_record/connection_adapters/oracle_enhanced/schema_creation'
1324
1413
 
1325
1414
  # Moved DatabaseStetements
1326
- require 'active_record/connection_adapters/oracle_enhanced_database_statements'
1415
+ require 'active_record/connection_adapters/oracle_enhanced/database_statements'
1416
+
1417
+ # Add Type:Raw
1418
+ require 'active_record/oracle_enhanced/type/raw'
1419
+
1420
+ # Add Type:Timestamp
1421
+ require 'active_record/oracle_enhanced/type/timestamp'
1422
+
1423
+ # Add OracleEnhanced::Type::Integer
1424
+ require 'active_record/oracle_enhanced/type/integer'
@@ -0,0 +1,13 @@
1
+ module ActiveRecord
2
+ module OracleEnhanced
3
+ module Type
4
+ class Integer < ActiveRecord::Type::Integer # :nodoc:
5
+ private
6
+
7
+ def max_value
8
+ ("9"*38).to_i
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'active_record/type/string'
2
+
3
+ module ActiveRecord
4
+ module OracleEnhanced
5
+ module Type
6
+ class Raw < ActiveRecord::Type::String # :nodoc:
7
+ def type
8
+ :raw
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ module ActiveRecord
2
+ module OracleEnhanced
3
+ module Type
4
+ class Timestamp < ActiveRecord::Type::Value # :nodoc:
5
+ def type
6
+ :timestamp
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -5,7 +5,7 @@ if defined?(::Rails::Railtie)
5
5
  module ConnectionAdapters
6
6
  class OracleEnhancedRailtie < ::Rails::Railtie
7
7
  rake_tasks do
8
- load 'active_record/connection_adapters/oracle_enhanced_database_tasks.rb'
8
+ load 'active_record/connection_adapters/oracle_enhanced/database_tasks.rb'
9
9
  end
10
10
 
11
11
  ActiveSupport.on_load(:active_record) do