activerecord-oracle_enhanced-adapter 1.5.6 → 1.6.9

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 (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