activerecord-oracle_enhanced-adapter 1.6.9 → 1.7.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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +10 -11
  3. data/History.md +126 -14
  4. data/README.md +9 -6
  5. data/RUNNING_TESTS.md +1 -1
  6. data/Rakefile +1 -16
  7. data/VERSION +1 -1
  8. data/activerecord-oracle_enhanced-adapter.gemspec +15 -52
  9. data/lib/active_record/connection_adapters/oracle_enhanced/column.rb +8 -22
  10. data/lib/active_record/connection_adapters/oracle_enhanced/column_dumper.rb +53 -45
  11. data/lib/active_record/connection_adapters/oracle_enhanced/connection.rb +6 -1
  12. data/lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb +23 -62
  13. data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_connection.rb +46 -56
  14. data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_quoting.rb +35 -0
  15. data/lib/active_record/connection_adapters/oracle_enhanced/oci_connection.rb +34 -21
  16. data/lib/active_record/connection_adapters/oracle_enhanced/oci_quoting.rb +36 -0
  17. data/lib/active_record/connection_adapters/oracle_enhanced/procedures.rb +1 -1
  18. data/lib/active_record/connection_adapters/oracle_enhanced/quoting.rb +174 -0
  19. data/lib/active_record/connection_adapters/oracle_enhanced/schema_creation.rb +17 -8
  20. data/lib/active_record/connection_adapters/oracle_enhanced/schema_definitions.rb +17 -11
  21. data/lib/active_record/connection_adapters/oracle_enhanced/schema_dumper.rb +160 -178
  22. data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb +42 -94
  23. data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements_ext.rb +50 -54
  24. data/lib/active_record/connection_adapters/oracle_enhanced/structure_dump.rb +15 -11
  25. data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +197 -301
  26. data/lib/active_record/oracle_enhanced/type/integer.rb +3 -2
  27. data/lib/active_record/oracle_enhanced/type/national_character_string.rb +25 -0
  28. data/lib/active_record/oracle_enhanced/type/raw.rb +14 -2
  29. data/lib/active_record/oracle_enhanced/type/string.rb +28 -0
  30. data/lib/active_record/oracle_enhanced/type/text.rb +32 -0
  31. data/lib/activerecord-oracle_enhanced-adapter.rb +12 -17
  32. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +113 -135
  33. data/spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb +51 -59
  34. data/spec/active_record/connection_adapters/oracle_enhanced_context_index_spec.rb +40 -41
  35. data/spec/active_record/connection_adapters/oracle_enhanced_cpk_spec.rb +6 -6
  36. data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +281 -233
  37. data/spec/active_record/connection_adapters/oracle_enhanced_database_tasks_spec.rb +7 -7
  38. data/spec/active_record/connection_adapters/oracle_enhanced_dbms_output_spec.rb +10 -10
  39. data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +22 -22
  40. data/spec/active_record/connection_adapters/oracle_enhanced_emulate_oracle_adapter_spec.rb +2 -2
  41. data/spec/active_record/connection_adapters/oracle_enhanced_procedures_spec.rb +36 -37
  42. data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +86 -46
  43. data/spec/active_record/connection_adapters/oracle_enhanced_schema_statements_spec.rb +194 -294
  44. data/spec/active_record/connection_adapters/oracle_enhanced_structure_dump_spec.rb +53 -39
  45. data/spec/spec_helper.rb +0 -6
  46. metadata +42 -143
  47. data/.travis.yml +0 -39
  48. data/.travis/oracle/download.sh +0 -14
  49. data/.travis/oracle/install.sh +0 -31
  50. data/.travis/setup_accounts.sh +0 -9
  51. data/lib/active_record/connection_adapters/oracle_enhanced/dirty.rb +0 -40
  52. data/lib/active_record/oracle_enhanced/type/timestamp.rb +0 -11
  53. data/spec/spec_config.yaml.template +0 -11
  54. data/spec/support/alter_system_user_password.sql +0 -2
  55. data/spec/support/create_oracle_enhanced_users.sql +0 -31
@@ -1,4 +1,3 @@
1
- # -*- coding: utf-8 -*-
2
1
  # oracle_enhanced_adapter.rb -- ActiveRecord adapter for Oracle 8i, 9i, 10g, 11g
3
2
  #
4
3
  # Authors or original oracle_adapter: Graham Jenkins, Michael Schoen
@@ -33,15 +32,14 @@ require 'active_record/connection_adapters/abstract_adapter'
33
32
  require 'active_record/connection_adapters/oracle_enhanced/connection'
34
33
  require 'active_record/connection_adapters/oracle_enhanced/database_statements'
35
34
  require 'active_record/connection_adapters/oracle_enhanced/schema_statements'
35
+ require 'active_record/connection_adapters/oracle_enhanced/schema_statements_ext'
36
36
  require 'active_record/connection_adapters/oracle_enhanced/column_dumper'
37
37
  require 'active_record/connection_adapters/oracle_enhanced/context_index'
38
-
39
38
  require 'active_record/connection_adapters/oracle_enhanced/column'
39
+ require 'active_record/connection_adapters/oracle_enhanced/quoting'
40
40
 
41
41
  require 'digest/sha1'
42
42
 
43
- require 'arel/visitors/bind_visitor'
44
-
45
43
  ActiveRecord::Base.class_eval do
46
44
  class_attribute :custom_create_method, :custom_update_method, :custom_delete_method
47
45
  end
@@ -60,21 +58,30 @@ module ActiveRecord
60
58
  #
61
59
  # set_date_columns :created_on, :updated_on
62
60
  def self.set_date_columns(*args)
63
- connection.set_type_for_columns(table_name,:date,*args)
61
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
62
+ 'set_date_columns` has been deprecated. Please use Rails attribute API.
63
+ MSG
64
+ # connection.set_type_for_columns(table_name,:date,*args)
64
65
  end
65
66
 
66
67
  # Specify which table columns should be typecasted to Time (or DateTime), e.g.:
67
68
  #
68
69
  # set_datetime_columns :created_date, :updated_date
69
70
  def self.set_datetime_columns(*args)
70
- connection.set_type_for_columns(table_name,:datetime,*args)
71
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
72
+ 'set_datetime_columns` has been deprecated. Please use Rails attribute API.
73
+ MSG
74
+ # connection.set_type_for_columns(table_name,:datetime,*args)
71
75
  end
72
76
 
73
77
  # Specify which table columns should be typecasted to boolean values +true+ or +false+, e.g.:
74
78
  #
75
79
  # set_boolean_columns :is_valid, :is_completed
76
80
  def self.set_boolean_columns(*args)
77
- connection.set_type_for_columns(table_name,:boolean,*args)
81
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
82
+ 'set_boolean_columns` has been deprecated. Please use Rails attribute API.
83
+ MSG
84
+ # connection.set_type_for_columns(table_name,:boolean,*args)
78
85
  end
79
86
 
80
87
  # Specify which table columns should be typecasted to integer values.
@@ -83,7 +90,10 @@ module ActiveRecord
83
90
  #
84
91
  # set_integer_columns :version_number, :object_identifier
85
92
  def self.set_integer_columns(*args)
86
- connection.set_type_for_columns(table_name,:integer,*args)
93
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
94
+ 'set_integer_columns` has been deprecated. Please use Rails attribute API.
95
+ MSG
96
+ # connection.set_type_for_columns(table_name,:integer,*args)
87
97
  end
88
98
 
89
99
  # Specify which table columns should be typecasted to string values.
@@ -91,11 +101,15 @@ module ActiveRecord
91
101
  #
92
102
  # set_string_columns :active_flag
93
103
  def self.set_string_columns(*args)
94
- connection.set_type_for_columns(table_name,:string,*args)
104
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
105
+ 'set_string_columns` has been deprecated. Please use Rails attribute API.
106
+ MSG
107
+ # connection.set_type_for_columns(table_name,:string,*args)
95
108
  end
96
109
 
97
110
  # Get table comment from schema definition.
98
111
  def self.table_comment
112
+ #TODO: may be deprecated
99
113
  connection.table_comment(self.table_name)
100
114
  end
101
115
 
@@ -225,8 +239,10 @@ module ActiveRecord
225
239
  # TODO: Use relative
226
240
  include ActiveRecord::ConnectionAdapters::OracleEnhanced::DatabaseStatements
227
241
  include ActiveRecord::ConnectionAdapters::OracleEnhanced::SchemaStatements
242
+ include ActiveRecord::ConnectionAdapters::OracleEnhanced::SchemaStatementsExt
228
243
  include ActiveRecord::ConnectionAdapters::OracleEnhanced::ColumnDumper
229
244
  include ActiveRecord::ConnectionAdapters::OracleEnhanced::ContextIndex
245
+ include ActiveRecord::ConnectionAdapters::OracleEnhanced::Quoting
230
246
 
231
247
  def schema_creation
232
248
  OracleEnhanced::SchemaCreation.new self
@@ -389,19 +405,11 @@ module ActiveRecord
389
405
  end
390
406
  end
391
407
 
392
- def initialize(connection, logger, config) #:nodoc:
393
- super(connection, logger)
408
+ def initialize(connection, logger = nil, config = {}) # :nodoc:
409
+ super(connection, logger, config)
394
410
  @quoted_column_names, @quoted_table_names = {}, {}
395
- @config = config
396
411
  @statements = StatementPool.new(connection, config.fetch(:statement_limit) { 250 })
397
412
  @enable_dbms_output = false
398
- @visitor = Arel::Visitors::Oracle.new self
399
-
400
- if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
401
- @prepared_statements = true
402
- else
403
- @prepared_statements = false
404
- end
405
413
  end
406
414
 
407
415
  ADAPTER_NAME = 'OracleEnhanced'.freeze
@@ -410,6 +418,14 @@ module ActiveRecord
410
418
  ADAPTER_NAME
411
419
  end
412
420
 
421
+ def arel_visitor # :nodoc:
422
+ if supports_fetch_first_n_rows_and_offset?
423
+ Arel::Visitors::Oracle12.new(self)
424
+ else
425
+ Arel::Visitors::Oracle.new(self)
426
+ end
427
+ end
428
+
413
429
  def supports_migrations? #:nodoc:
414
430
  true
415
431
  end
@@ -434,6 +450,24 @@ module ActiveRecord
434
450
  true
435
451
  end
436
452
 
453
+ def supports_fetch_first_n_rows_and_offset?
454
+ if @connection.database_version == [12,1]
455
+ true
456
+ else
457
+ false
458
+ end
459
+ end
460
+
461
+ def supports_datetime_with_precision?
462
+ #TODO: Needs to consider to return false to keep old behaviour
463
+ #to map :datetime as DATE
464
+ @connection.database_version.first >= 9
465
+ end
466
+
467
+ def supports_comments?
468
+ true
469
+ end
470
+
437
471
  #:stopdoc:
438
472
  DEFAULT_NLS_PARAMETERS = {
439
473
  :nls_calendar => nil,
@@ -463,11 +497,12 @@ module ActiveRecord
463
497
  :integer => { :name => "NUMBER", :limit => 38 },
464
498
  :float => { :name => "BINARY_FLOAT" },
465
499
  :decimal => { :name => "DECIMAL" },
466
- :datetime => { :name => "DATE" },
500
+ #TODO: Needs to consider to support :datetime as DATE
501
+ :datetime => { :name => "TIMESTAMP" },
467
502
  # changed to native TIMESTAMP type
468
503
  # :timestamp => { :name => "DATE" },
469
504
  :timestamp => { :name => "TIMESTAMP" },
470
- :time => { :name => "DATE" },
505
+ :time => { :name => "TIMESTAMP" },
471
506
  :date => { :name => "DATE" },
472
507
  :binary => { :name => "BLOB" },
473
508
  :boolean => { :name => "NUMBER", :limit => 1 },
@@ -528,178 +563,6 @@ module ActiveRecord
528
563
  end
529
564
  alias ids_in_list_limit in_clause_length
530
565
 
531
- # QUOTING ==================================================
532
- #
533
- # see: abstract/quoting.rb
534
-
535
- def quote_column_name(name) #:nodoc:
536
- name = name.to_s
537
- @quoted_column_names[name] ||= begin
538
- # if only valid lowercase column characters in name
539
- if name =~ /\A[a-z][a-z_0-9\$#]*\Z/
540
- "\"#{name.upcase}\""
541
- else
542
- # remove double quotes which cannot be used inside quoted identifier
543
- "\"#{name.gsub('"', '')}\""
544
- end
545
- end
546
- end
547
-
548
- # This method is used in add_index to identify either column name (which is quoted)
549
- # or function based index (in which case function expression is not quoted)
550
- def quote_column_name_or_expression(name) #:nodoc:
551
- name = name.to_s
552
- case name
553
- # if only valid lowercase column characters in name
554
- when /^[a-z][a-z_0-9\$#]*$/
555
- "\"#{name.upcase}\""
556
- when /^[a-z][a-z_0-9\$#\-]*$/i
557
- "\"#{name}\""
558
- # if other characters present then assume that it is expression
559
- # which should not be quoted
560
- else
561
- name
562
- end
563
- end
564
-
565
- # Used only for quoting database links as the naming rules for links
566
- # differ from the rules for column names. Specifically, link names may
567
- # include periods.
568
- def quote_database_link(name)
569
- case name
570
- when NONQUOTED_DATABASE_LINK
571
- %Q("#{name.upcase}")
572
- else
573
- name
574
- end
575
- end
576
-
577
- # Names must be from 1 to 30 bytes long with these exceptions:
578
- # * Names of databases are limited to 8 bytes.
579
- # * Names of database links can be as long as 128 bytes.
580
- #
581
- # Nonquoted identifiers cannot be Oracle Database reserved words
582
- #
583
- # Nonquoted identifiers must begin with an alphabetic character from
584
- # your database character set
585
- #
586
- # Nonquoted identifiers can contain only alphanumeric characters from
587
- # your database character set and the underscore (_), dollar sign ($),
588
- # and pound sign (#). Database links can also contain periods (.) and
589
- # "at" signs (@). Oracle strongly discourages you from using $ and # in
590
- # nonquoted identifiers.
591
- NONQUOTED_OBJECT_NAME = /[A-Za-z][A-z0-9$#]{0,29}/
592
- NONQUOTED_DATABASE_LINK = /[A-Za-z][A-z0-9$#\.@]{0,127}/
593
- VALID_TABLE_NAME = /\A(?:#{NONQUOTED_OBJECT_NAME}\.)?#{NONQUOTED_OBJECT_NAME}(?:@#{NONQUOTED_DATABASE_LINK})?\Z/
594
-
595
- # unescaped table name should start with letter and
596
- # contain letters, digits, _, $ or #
597
- # can be prefixed with schema name
598
- # CamelCase table names should be quoted
599
- def self.valid_table_name?(name) #:nodoc:
600
- name = name.to_s
601
- name =~ VALID_TABLE_NAME && !(name =~ /[A-Z]/ && name =~ /[a-z]/) ? true : false
602
- end
603
-
604
- def quote_table_name(name) #:nodoc:
605
- name, link = name.to_s.split('@')
606
- @quoted_table_names[name] ||= [name.split('.').map{|n| quote_column_name(n)}.join('.'), quote_database_link(link)].compact.join('@')
607
- end
608
-
609
- def quote_string(s) #:nodoc:
610
- s.gsub(/'/, "''")
611
- end
612
-
613
- def quote(value, column = nil) #:nodoc:
614
- if value && column
615
- case column.type
616
- when :text, :binary
617
- %Q{empty_#{ type_to_sql(column.type.to_sym).downcase rescue 'blob' }()}
618
- # NLS_DATE_FORMAT independent TIMESTAMP support
619
- when :timestamp
620
- quote_timestamp_with_to_timestamp(value)
621
- # NLS_DATE_FORMAT independent DATE support
622
- when :date, :time, :datetime
623
- quote_date_with_to_date(value)
624
- when :raw
625
- quote_raw(value)
626
- when :string
627
- # NCHAR and NVARCHAR2 literals should be quoted with N'...'.
628
- # Read directly instance variable as otherwise migrations with table column default values are failing
629
- # as migrations pass ColumnDefinition object to this method.
630
- # Check if instance variable is defined to avoid warnings about accessing undefined instance variable.
631
- column.instance_variable_defined?('@nchar') && column.instance_variable_get('@nchar') ? 'N' << super : super
632
- else
633
- super
634
- end
635
- elsif value.acts_like?(:date)
636
- quote_date_with_to_date(value)
637
- elsif value.acts_like?(:time)
638
- value.to_i == value.to_f ? quote_date_with_to_date(value) : quote_timestamp_with_to_timestamp(value)
639
- else
640
- super
641
- end
642
- end
643
-
644
- def quoted_true #:nodoc:
645
- return "'#{self.class.boolean_to_string(true)}'" if emulate_booleans_from_strings
646
- "1"
647
- end
648
-
649
- def quoted_false #:nodoc:
650
- return "'#{self.class.boolean_to_string(false)}'" if emulate_booleans_from_strings
651
- "0"
652
- end
653
-
654
- def quote_date_with_to_date(value) #:nodoc:
655
- # should support that composite_primary_keys gem will pass date as string
656
- value = quoted_date(value) if value.acts_like?(:date) || value.acts_like?(:time)
657
- "TO_DATE('#{value}','YYYY-MM-DD HH24:MI:SS')"
658
- end
659
-
660
- # Encode a string or byte array as string of hex codes
661
- def self.encode_raw(value)
662
- # When given a string, convert to a byte array.
663
- value = value.unpack('C*') if value.is_a?(String)
664
- value.map { |x| "%02X" % x }.join
665
- end
666
-
667
- # quote encoded raw value
668
- def quote_raw(value) #:nodoc:
669
- "'#{self.class.encode_raw(value)}'"
670
- end
671
-
672
- def quote_timestamp_with_to_timestamp(value) #:nodoc:
673
- # add up to 9 digits of fractional seconds to inserted time
674
- value = "#{quoted_date(value)}:#{("%.6f"%value.to_f).split('.')[1]}" if value.acts_like?(:time)
675
- "TO_TIMESTAMP('#{value}','YYYY-MM-DD HH24:MI:SS:FF6')"
676
- end
677
-
678
- # Cast a +value+ to a type that the database understands.
679
- def type_cast(value, column)
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
697
- else
698
- super
699
- end
700
- end
701
- end
702
-
703
566
  # CONNECTION MANAGEMENT ====================================
704
567
  #
705
568
 
@@ -785,7 +648,7 @@ module ActiveRecord
785
648
  end
786
649
 
787
650
  def reset_pk_sequence!(table_name, primary_key = nil, sequence_name = nil) #:nodoc:
788
- return nil unless table_exists?(table_name)
651
+ return nil unless data_source_exists?(table_name)
789
652
  unless primary_key and sequence_name
790
653
  # *Note*: Only primary key is implemented - sequence will be nil.
791
654
  primary_key, sequence_name = pk_and_sequence_for(table_name)
@@ -825,8 +688,10 @@ module ActiveRecord
825
688
  columns.each do |col|
826
689
  value = attributes[col.name]
827
690
  # changed sequence of next two lines - should check if value is nil before converting to yaml
828
- next if value.blank?
829
- value = col.cast_type.type_cast_for_database(value)
691
+ next if value.nil? || (value == '')
692
+ if klass.attribute_types[col.name].is_a? ActiveRecord::Type::Serialized
693
+ value = klass.attribute_types[col.name].serialize(value)
694
+ end
830
695
  uncached do
831
696
  sql = is_with_cpk ? "SELECT #{quote_column_name(col.name)} FROM #{quote_table_name(table_name)} WHERE #{klass.composite_where_clause(id)} FOR UPDATE" :
832
697
  "SELECT #{quote_column_name(col.name)} FROM #{quote_table_name(table_name)} WHERE #{quote_column_name(klass.primary_key)} = #{id} FOR UPDATE"
@@ -860,19 +725,51 @@ module ActiveRecord
860
725
  end
861
726
 
862
727
  def tables(name = nil) #:nodoc:
728
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
729
+ #tables currently returns both tables and views.
730
+ This behavior is deprecated and will be changed with Rails 5.1 to only return tables.
731
+ Use #data_sources instead.
732
+ MSG
733
+
734
+ if name
735
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
736
+ Passing arguments to #tables is deprecated without replacement.
737
+ MSG
738
+ end
739
+
740
+ data_sources
741
+ end
742
+
743
+
744
+ def data_sources
863
745
  select_values(
864
746
  "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'",
865
- name)
747
+ 'SCHEMA')
866
748
  end
867
749
 
868
- # Will return true if database object exists (to be able to use also views and synonyms for ActiveRecord models)
869
750
  def table_exists?(table_name)
751
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
752
+ #table_exists? currently checks both tables and views.
753
+ This behavior is deprecated and will be changed with Rails 5.1 to only check tables.
754
+ Use #data_source_exists? instead.
755
+ MSG
756
+
757
+ data_source_exists?(table_name)
758
+ end
759
+
760
+ # Will return true if database object exists (to be able to use also views and synonyms for ActiveRecord models)
761
+ # Needs to consider how to support synonyms in Rails 5.1
762
+ def data_source_exists?(table_name)
870
763
  (_owner, table_name, _db_link) = @connection.describe(table_name)
871
764
  true
872
765
  rescue
873
766
  false
874
767
  end
875
768
 
769
+ def views # :nodoc:
770
+ select_values("SELECT LOWER(view_name) FROM all_views WHERE owner = SYS_CONTEXT('userenv', 'session_user')")
771
+ end
772
+
876
773
  def materialized_views #:nodoc:
877
774
  select_values("SELECT LOWER(mview_name) FROM all_mviews WHERE owner = SYS_CONTEXT('userenv', 'current_schema')")
878
775
  end
@@ -925,10 +822,19 @@ module ActiveRecord
925
822
  statement_parameters = $1
926
823
  end
927
824
  end
928
- all_schema_indexes << OracleEnhanced::IndexDefinition.new(row['table_name'], row['index_name'],
929
- row['uniqueness'] == "UNIQUE", row['index_type'] == 'DOMAIN' ? "#{row['ityp_owner']}.#{row['ityp_name']}" : nil,
930
- row['parameters'], statement_parameters,
931
- row['tablespace_name'] == default_tablespace_name ? nil : row['tablespace_name'], [])
825
+ all_schema_indexes << OracleEnhanced::IndexDefinition.new(
826
+ row['table_name'],
827
+ row['index_name'],
828
+ row['uniqueness'] == "UNIQUE",
829
+ [],
830
+ nil,
831
+ nil,
832
+ nil,
833
+ row['index_type'] == 'DOMAIN' ? "#{row['ityp_owner']}.#{row['ityp_name']}" : nil,
834
+ nil,
835
+ row['parameters'],
836
+ statement_parameters,
837
+ row['tablespace_name'] == default_tablespace_name ? nil : row['tablespace_name'])
932
838
  current_index = row['index_name']
933
839
  end
934
840
 
@@ -953,6 +859,10 @@ module ActiveRecord
953
859
 
954
860
  # set ignored columns for table
955
861
  def ignore_table_columns(table_name, *args) #:nodoc:
862
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
863
+ `ignore_table_columns` will be deprecated in next version of Oracle enhanced adapter
864
+ since Rails 5 introduces `ignored_columns`. Use `ignored_columns` instead of `ignore_table_columns`.
865
+ MSG
956
866
  @@ignore_table_columns ||= {}
957
867
  @@ignore_table_columns[table_name] ||= []
958
868
  @@ignore_table_columns[table_name] += args.map{|a| a.to_s.downcase}
@@ -1036,19 +946,25 @@ module ActiveRecord
1036
946
  @@do_not_prefetch_primary_key[table_name] = nil
1037
947
 
1038
948
  table_cols = <<-SQL.strip.gsub(/\s+/, ' ')
1039
- SELECT column_name AS name, data_type AS sql_type, data_default, nullable, virtual_column, hidden_column, data_type_owner AS sql_type_owner,
1040
- DECODE(data_type, 'NUMBER', data_precision,
949
+ SELECT cols.column_name AS name, cols.data_type AS sql_type,
950
+ cols.data_default, cols.nullable, cols.virtual_column, cols.hidden_column,
951
+ cols.data_type_owner AS sql_type_owner,
952
+ DECODE(cols.data_type, 'NUMBER', data_precision,
1041
953
  'FLOAT', data_precision,
1042
954
  'VARCHAR2', DECODE(char_used, 'C', char_length, data_length),
1043
955
  'RAW', DECODE(char_used, 'C', char_length, data_length),
1044
956
  'CHAR', DECODE(char_used, 'C', char_length, data_length),
1045
957
  NULL) AS limit,
1046
- DECODE(data_type, 'NUMBER', data_scale, NULL) AS scale
1047
- FROM all_tab_cols#{db_link}
1048
- WHERE owner = '#{owner}'
1049
- AND table_name = '#{desc_table_name}'
1050
- AND hidden_column = 'NO'
1051
- ORDER BY column_id
958
+ DECODE(data_type, 'NUMBER', data_scale, NULL) AS scale,
959
+ comments.comments as column_comment
960
+ FROM all_tab_cols#{db_link} cols, all_col_comments#{db_link} comments
961
+ WHERE cols.owner = '#{owner}'
962
+ AND cols.table_name = '#{desc_table_name}'
963
+ AND cols.hidden_column = 'NO'
964
+ AND cols.owner = comments.owner
965
+ AND cols.table_name = comments.table_name
966
+ AND cols.column_name = comments.column_name
967
+ ORDER BY cols.column_id
1052
968
  SQL
1053
969
 
1054
970
  # added deletion of ignored columns
@@ -1078,55 +994,21 @@ module ActiveRecord
1078
994
  row['data_default'] = false if (row['data_default'] == "N" && OracleEnhancedAdapter.emulate_booleans_from_strings)
1079
995
  end
1080
996
 
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
-
997
+ type_metadata = fetch_type_metadata(row['sql_type'])
1117
998
  new_column(oracle_downcase(row['name']),
1118
999
  row['data_default'],
1119
- cast_type,
1120
- row['sql_type'],
1000
+ type_metadata,
1121
1001
  row['nullable'] == 'Y',
1122
1002
  table_name,
1123
1003
  is_virtual,
1124
- false )
1004
+ false,
1005
+ row['column_comment']
1006
+ )
1125
1007
  end
1126
1008
  end
1127
1009
 
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)
1010
+ def new_column(name, default, sql_type_metadata = nil, null = true, table_name = nil, virtual = false, returning_id = false,comment = nil) # :nodoc:
1011
+ OracleEnhancedColumn.new(name, default, sql_type_metadata, null, table_name, virtual, returning_id, comment)
1130
1012
  end
1131
1013
 
1132
1014
  # used just in tests to clear column cache
@@ -1187,6 +1069,12 @@ module ActiveRecord
1187
1069
  AND cc.constraint_name = c.constraint_name
1188
1070
  SQL
1189
1071
 
1072
+ warn <<-WARNING.strip_heredoc if pks.count > 1
1073
+ WARNING: Rails does not support composite primary key.
1074
+
1075
+ #{table_name} has composite primary key. Composite primary key is ignored.
1076
+ WARNING
1077
+
1190
1078
  # only support single column keys
1191
1079
  pks.size == 1 ? [oracle_downcase(pks.first),
1192
1080
  oracle_downcase(seqs.first)] : nil
@@ -1202,31 +1090,20 @@ module ActiveRecord
1202
1090
  !pk_and_sequence_for(table_name, owner, desc_table_name, db_link).nil?
1203
1091
  end
1204
1092
 
1205
- # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
1206
- #
1207
- # Oracle requires the ORDER BY columns to be in the SELECT list for DISTINCT
1208
- # queries. However, with those columns included in the SELECT DISTINCT list, you
1209
- # won't actually get a distinct list of the column you want (presuming the column
1210
- # has duplicates with multiple values for the ordered-by columns. So we use the
1211
- # FIRST_VALUE function to get a single (first) value for each column, effectively
1212
- # making every row the same.
1213
- #
1214
- # distinct("posts.id", "posts.created_at desc")
1215
- def distinct(columns, orders) #:nodoc:
1216
- # To support Rails 4.0.0 and future releases
1217
- # because `columns_for_distinct method introduced after Rails 4.0.0 released
1218
- if super.respond_to?(:columns_for_distinct)
1219
- super
1220
- else
1221
- order_columns = orders.map { |c|
1222
- c = c.to_sql unless c.is_a?(String)
1223
- # remove any ASC/DESC modifiers
1224
- c.gsub(/\s+(ASC|DESC)\s*?/i, '')
1225
- }.reject(&:blank?).map.with_index { |c,i|
1226
- "FIRST_VALUE(#{c}) OVER (PARTITION BY #{columns} ORDER BY #{c}) AS alias_#{i}__"
1227
- }
1228
- [super].concat(order_columns).join(', ')
1229
- end
1093
+ def primary_keys(table_name) # :nodoc:
1094
+ (owner, desc_table_name, db_link) = @connection.describe(table_name) unless owner
1095
+
1096
+ pks = select_values(<<-SQL.strip_heredoc, 'Primary Keys')
1097
+ SELECT cc.column_name
1098
+ FROM all_constraints#{db_link} c, all_cons_columns#{db_link} cc
1099
+ WHERE c.owner = '#{owner}'
1100
+ AND c.table_name = '#{desc_table_name}'
1101
+ AND c.constraint_type = 'P'
1102
+ AND cc.owner = c.owner
1103
+ AND cc.constraint_name = c.constraint_name
1104
+ order by cc.position
1105
+ SQL
1106
+ pks.map {|pk| oracle_downcase(pk)}
1230
1107
  end
1231
1108
 
1232
1109
  def columns_for_distinct(columns, orders) #:nodoc:
@@ -1246,24 +1123,37 @@ module ActiveRecord
1246
1123
  end
1247
1124
 
1248
1125
  def temporary_table?(table_name) #:nodoc:
1249
- select_value("SELECT temporary FROM user_tables WHERE table_name = '#{table_name.upcase}'") == 'Y'
1126
+ select_value("SELECT temporary FROM all_tables WHERE table_name = '#{table_name.upcase}' and owner = SYS_CONTEXT('userenv', 'session_user')") == 'Y'
1250
1127
  end
1251
1128
 
1252
1129
  # construct additional wrapper subquery if select.offset is used to avoid generation of invalid subquery
1253
1130
  # ... IN ( SELECT * FROM ( SELECT raw_sql_.*, rownum raw_rnum_ FROM ( ... ) raw_sql_ ) WHERE raw_rnum_ > ... )
1254
- def join_to_update(update, select) #:nodoc:
1255
- if select.offset
1256
- subsubselect = select.clone
1257
- subsubselect.projections = [update.key]
1131
+ def join_to_update(update, select, key) #:nodoc:
1132
+ #TODO: Need to validate if we can remove join_to_update from Oracle enhanced adapter after testing
1133
+ # older version of Oracle 11gR2
1134
+ super
1135
+ end
1258
1136
 
1259
- subselect = Arel::SelectManager.new(select.engine)
1260
- subselect.project Arel.sql(quote_column_name update.key.name)
1261
- subselect.from subsubselect.as('alias_join_to_update')
1137
+ def valid_type?(type)
1138
+ !native_database_types[type].nil?
1139
+ end
1262
1140
 
1263
- update.where update.key.in(subselect)
1264
- else
1265
- super
1141
+ def combine_bind_parameters(
1142
+ from_clause: [],
1143
+ join_clause: [],
1144
+ where_clause: [],
1145
+ having_clause: [],
1146
+ limit: nil,
1147
+ offset: nil
1148
+ ) # :nodoc:
1149
+ result = from_clause + join_clause + where_clause + having_clause
1150
+ if offset
1151
+ result << offset
1152
+ end
1153
+ if limit
1154
+ result << limit
1266
1155
  end
1156
+ result
1267
1157
  end
1268
1158
 
1269
1159
  protected
@@ -1271,9 +1161,12 @@ module ActiveRecord
1271
1161
  def initialize_type_map(m)
1272
1162
  super
1273
1163
  # oracle
1274
- register_class_with_limit m, %r(date)i, Type::DateTime
1275
1164
  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
1165
+ register_class_with_limit m, %r(char)i, ActiveRecord::OracleEnhanced::Type::String
1166
+ register_class_with_limit m, %r(clob)i, ActiveRecord::OracleEnhanced::Type::Text
1167
+
1168
+ m.register_type 'NCHAR', ActiveRecord::OracleEnhanced::Type::NationalCharacterString.new
1169
+ m.alias_type %r(NVARCHAR2)i, 'NCHAR'
1277
1170
 
1278
1171
  m.register_type(%r(NUMBER)i) do |sql_type|
1279
1172
  scale = extract_scale(sql_type)
@@ -1285,6 +1178,8 @@ module ActiveRecord
1285
1178
  Type::Decimal.new(precision: precision, scale: scale)
1286
1179
  end
1287
1180
  end
1181
+
1182
+ m.register_type %r(^NUMBER\(1\))i, Type::Boolean.new if OracleEnhancedAdapter.emulate_booleans
1288
1183
  end
1289
1184
 
1290
1185
  def extract_limit(sql_type) #:nodoc:
@@ -1299,9 +1194,11 @@ module ActiveRecord
1299
1194
  def translate_exception(exception, message) #:nodoc:
1300
1195
  case @connection.error_code(exception)
1301
1196
  when 1
1302
- RecordNotUnique.new(message, exception)
1197
+ RecordNotUnique.new(message)
1303
1198
  when 2291
1304
- InvalidForeignKey.new(message, exception)
1199
+ InvalidForeignKey.new(message)
1200
+ when 12899
1201
+ ValueTooLong.new(message)
1305
1202
  else
1306
1203
  super
1307
1204
  end
@@ -1309,10 +1206,6 @@ module ActiveRecord
1309
1206
 
1310
1207
  private
1311
1208
 
1312
- def select(sql, name = nil, binds = [])
1313
- exec_query(sql, name, binds)
1314
- end
1315
-
1316
1209
  def oracle_downcase(column_name)
1317
1210
  @connection.oracle_downcase(column_name)
1318
1211
  end
@@ -1390,9 +1283,6 @@ require 'active_record/connection_adapters/oracle_enhanced/context_index'
1390
1283
  # Load additional methods for composite_primary_keys support
1391
1284
  require 'active_record/connection_adapters/oracle_enhanced/cpk'
1392
1285
 
1393
- # Load patch for dirty tracking methods
1394
- require 'active_record/connection_adapters/oracle_enhanced/dirty'
1395
-
1396
1286
  # Patches and enhancements for schema dumper
1397
1287
  require 'active_record/connection_adapters/oracle_enhanced/schema_dumper'
1398
1288
 
@@ -1417,8 +1307,14 @@ require 'active_record/connection_adapters/oracle_enhanced/database_statements'
1417
1307
  # Add Type:Raw
1418
1308
  require 'active_record/oracle_enhanced/type/raw'
1419
1309
 
1420
- # Add Type:Timestamp
1421
- require 'active_record/oracle_enhanced/type/timestamp'
1422
-
1423
1310
  # Add OracleEnhanced::Type::Integer
1424
1311
  require 'active_record/oracle_enhanced/type/integer'
1312
+
1313
+ # Add OracleEnhanced::Type::String
1314
+ require 'active_record/oracle_enhanced/type/string'
1315
+
1316
+ # Add OracleEnhanced::Type::NationalCharacterString
1317
+ require 'active_record/oracle_enhanced/type/national_character_string'
1318
+
1319
+ # Add OracleEnhanced::Type::Text
1320
+ require 'active_record/oracle_enhanced/type/text'