activerecord-oracle_enhanced-adapter 1.6.9 → 1.7.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
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'