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
@@ -0,0 +1,36 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module OracleEnhanced
4
+ module OCIQuoting
5
+ def _type_cast(value)
6
+ case value
7
+ when ActiveModel::Type::Binary::Data
8
+ lob_value = value == '' ? ' ' : value
9
+ bind_type = OCI8::BLOB
10
+ ora_value = bind_type.new(@connection.raw_oci_connection, lob_value)
11
+ ora_value.size = 0 if value == ''
12
+ ora_value
13
+ when ActiveRecord::OracleEnhanced::Type::Text::Data
14
+ lob_value = value.to_s == '' ? ' ' : value.to_s
15
+ bind_type = OCI8::CLOB
16
+ ora_value = bind_type.new(@connection.raw_oci_connection, lob_value)
17
+ ora_value.size = 0 if value.to_s == ''
18
+ ora_value
19
+ else
20
+ super
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ module ActiveRecord
29
+ module ConnectionAdapters
30
+ module OracleEnhanced
31
+ module Quoting
32
+ prepend OCIQuoting
33
+ end
34
+ end
35
+ end
36
+ end
@@ -152,7 +152,7 @@ module ActiveRecord #:nodoc:
152
152
  if partial_writes?
153
153
  # Serialized attributes should always be written in case they've been
154
154
  # changed in place.
155
- update_using_custom_method(changed | (attributes.keys & self.class.columns.select {|column| column.cast_type.is_a?(Type::Serialized)}))
155
+ update_using_custom_method(changed | (attributes.keys & self.class.columns.select {|column| column.is_a?(Type::Serialized)}))
156
156
  else
157
157
  update_using_custom_method(attribute_names)
158
158
  end
@@ -0,0 +1,174 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module OracleEnhanced
4
+ module Quoting
5
+ # QUOTING ==================================================
6
+ #
7
+ # see: abstract/quoting.rb
8
+
9
+ def quote_column_name(name) #:nodoc:
10
+ name = name.to_s
11
+ @quoted_column_names[name] ||= begin
12
+ # if only valid lowercase column characters in name
13
+ if name =~ /\A[a-z][a-z_0-9\$#]*\Z/
14
+ "\"#{name.upcase}\""
15
+ else
16
+ # remove double quotes which cannot be used inside quoted identifier
17
+ "\"#{name.gsub('"', '')}\""
18
+ end
19
+ end
20
+ end
21
+
22
+ # This method is used in add_index to identify either column name (which is quoted)
23
+ # or function based index (in which case function expression is not quoted)
24
+ def quote_column_name_or_expression(name) #:nodoc:
25
+ name = name.to_s
26
+ case name
27
+ # if only valid lowercase column characters in name
28
+ when /^[a-z][a-z_0-9\$#]*$/
29
+ "\"#{name.upcase}\""
30
+ when /^[a-z][a-z_0-9\$#\-]*$/i
31
+ "\"#{name}\""
32
+ # if other characters present then assume that it is expression
33
+ # which should not be quoted
34
+ else
35
+ name
36
+ end
37
+ end
38
+
39
+ # Used only for quoting database links as the naming rules for links
40
+ # differ from the rules for column names. Specifically, link names may
41
+ # include periods.
42
+ def quote_database_link(name)
43
+ case name
44
+ when NONQUOTED_DATABASE_LINK
45
+ %Q("#{name.upcase}")
46
+ else
47
+ name
48
+ end
49
+ end
50
+
51
+ # Names must be from 1 to 30 bytes long with these exceptions:
52
+ # * Names of databases are limited to 8 bytes.
53
+ # * Names of database links can be as long as 128 bytes.
54
+ #
55
+ # Nonquoted identifiers cannot be Oracle Database reserved words
56
+ #
57
+ # Nonquoted identifiers must begin with an alphabetic character from
58
+ # your database character set
59
+ #
60
+ # Nonquoted identifiers can contain only alphanumeric characters from
61
+ # your database character set and the underscore (_), dollar sign ($),
62
+ # and pound sign (#). Database links can also contain periods (.) and
63
+ # "at" signs (@). Oracle strongly discourages you from using $ and # in
64
+ # nonquoted identifiers.
65
+ NONQUOTED_OBJECT_NAME = /[A-Za-z][A-z0-9$#]{0,29}/
66
+ NONQUOTED_DATABASE_LINK = /[A-Za-z][A-z0-9$#\.@]{0,127}/
67
+ VALID_TABLE_NAME = /\A(?:#{NONQUOTED_OBJECT_NAME}\.)?#{NONQUOTED_OBJECT_NAME}(?:@#{NONQUOTED_DATABASE_LINK})?\Z/
68
+
69
+ # unescaped table name should start with letter and
70
+ # contain letters, digits, _, $ or #
71
+ # can be prefixed with schema name
72
+ # CamelCase table names should be quoted
73
+ def self.valid_table_name?(name) #:nodoc:
74
+ name = name.to_s
75
+ name =~ VALID_TABLE_NAME && !(name =~ /[A-Z]/ && name =~ /[a-z]/) ? true : false
76
+ end
77
+
78
+ def quote_table_name(name) #:nodoc:
79
+ name, link = name.to_s.split('@')
80
+ @quoted_table_names[name] ||= [name.split('.').map{|n| quote_column_name(n)}.join('.'), quote_database_link(link)].compact.join('@')
81
+ end
82
+
83
+ def quote_string(s) #:nodoc:
84
+ s.gsub(/'/, "''")
85
+ end
86
+
87
+ def quote(value, column = nil) #:nodoc:
88
+ super
89
+ end
90
+
91
+ def _quote(value) #:nodoc:
92
+ case value
93
+ when ActiveRecord::OracleEnhanced::Type::NationalCharacterString::Data then
94
+ "N" << "'#{quote_string(value.to_s)}'"
95
+ when ActiveModel::Type::Binary::Data then
96
+ %Q{empty_#{ type_to_sql(column.type.to_sym).downcase rescue 'blob' }()}
97
+ when ActiveRecord::OracleEnhanced::Type::Text::Data then
98
+ %Q{empty_#{ type_to_sql(column.type.to_sym).downcase rescue 'clob' }()}
99
+ else
100
+ super
101
+ end
102
+ end
103
+
104
+ def quoted_true #:nodoc:
105
+ return "'#{self.class.boolean_to_string(true)}'" if emulate_booleans_from_strings
106
+ "1"
107
+ end
108
+
109
+ def quoted_false #:nodoc:
110
+ return "'#{self.class.boolean_to_string(false)}'" if emulate_booleans_from_strings
111
+ "0"
112
+ end
113
+
114
+ def quote_date_with_to_date(value) #:nodoc:
115
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
116
+ `quote_date_with_to_date` will be deprecated in future version of Oracle enhanced adapter.
117
+ Also this method should not be called directly. Let Abstract adapter `_quote` method handle it.
118
+ MSG
119
+ # should support that composite_primary_keys gem will pass date as string
120
+ value = quoted_date(value) if value.acts_like?(:date) || value.acts_like?(:time)
121
+ "TO_DATE('#{value}','YYYY-MM-DD HH24:MI:SS')"
122
+ end
123
+
124
+ def quote_timestamp_with_to_timestamp(value) #:nodoc:
125
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
126
+ `quote_timestamp_with_to_timestamp` will be deprecated in future version of Oracle enhanced adapter.
127
+ Also this method should not be called directly. Let Abstract adapter `_quote` method handle it.
128
+ MSG
129
+ # add up to 9 digits of fractional seconds to inserted time
130
+ value = "#{quoted_date(value)}:#{("%.6f"%value.to_f).split('.')[1]}" if value.acts_like?(:time)
131
+ "TO_TIMESTAMP('#{value}','YYYY-MM-DD HH24:MI:SS:FF6')"
132
+ end
133
+
134
+ # Cast a +value+ to a type that the database understands.
135
+ def type_cast(value, column = nil)
136
+ super
137
+ end
138
+
139
+ def _type_cast(value)
140
+ case value
141
+ when true, false
142
+ #if emulate_booleans_from_strings || column && column.type == :string
143
+ if emulate_booleans_from_strings
144
+ self.class.boolean_to_string(value)
145
+ else
146
+ value ? 1 : 0
147
+ end
148
+ when Date, Time
149
+ if value.acts_like?(:time)
150
+ zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
151
+ value.respond_to?(zone_conversion_method) ? value.send(zone_conversion_method) : value
152
+ else
153
+ value
154
+ end
155
+ when ActiveRecord::OracleEnhanced::Type::NationalCharacterString::Data
156
+ value.to_s
157
+ else
158
+ super
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
165
+
166
+ # if MRI or YARV
167
+ if !defined?(RUBY_ENGINE) || RUBY_ENGINE == 'ruby'
168
+ require 'active_record/connection_adapters/oracle_enhanced/oci_quoting'
169
+ # if JRuby
170
+ elsif RUBY_ENGINE == 'jruby'
171
+ require 'active_record/connection_adapters/oracle_enhanced/jdbc_quoting'
172
+ else
173
+ raise "Unsupported Ruby engine #{RUBY_ENGINE}"
174
+ end
@@ -19,21 +19,27 @@ module ActiveRecord
19
19
  end
20
20
 
21
21
  def visit_TableDefinition(o)
22
- create_sql = "CREATE#{' GLOBAL TEMPORARY' if o.temporary} TABLE "
23
- create_sql << "#{quote_table_name(o.name)} ("
24
- create_sql << o.columns.map { |c| accept c }.join(', ')
25
- create_sql << ")"
22
+ create_sql = "CREATE#{' GLOBAL TEMPORARY' if o.temporary} TABLE #{quote_table_name(o.name)} "
23
+ statements = o.columns.map { |c| accept c }
24
+ statements << accept(o.primary_keys) if o.primary_keys
25
+
26
+ if supports_foreign_keys?
27
+ statements.concat(o.foreign_keys.map { |to_table, options| foreign_key_in_create(o.name, to_table, options) })
28
+ end
29
+
30
+ create_sql << "(#{statements.join(', ')})" if statements.present?
26
31
 
27
32
  unless o.temporary
28
33
  @lob_tablespaces.each do |lob_column, tablespace|
29
34
  create_sql << " LOB (#{quote_column_name(lob_column)}) STORE AS (TABLESPACE #{tablespace}) \n"
30
35
  end if defined?(@lob_tablespaces)
31
- create_sql << " ORGANIZATION #{o.options[:organization]}" if o.options[:organization]
32
- if (tablespace = o.options[:tablespace] || default_tablespace_for(:table))
36
+ create_sql << " ORGANIZATION #{o.organization}" if o.organization
37
+ if (tablespace = o.tablespace || default_tablespace_for(:table))
33
38
  create_sql << " TABLESPACE #{tablespace}"
34
39
  end
35
40
  end
36
- create_sql << " #{o.options[:options]}"
41
+ add_table_options!(create_sql, table_options(o))
42
+ create_sql << " AS #{@conn.to_sql(o.as)}" if o.as
37
43
  create_sql
38
44
  end
39
45
 
@@ -50,7 +56,7 @@ module ActiveRecord
50
56
  if type == :text
51
57
  sql << " DEFAULT #{@conn.quote(options[:default])}"
52
58
  else
53
- sql << " DEFAULT #{quote_value(options[:default], options[:column])}"
59
+ sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}"
54
60
  end
55
61
  end
56
62
  # must explicitly add NULL or NOT NULL to allow change_column to work on migrations
@@ -63,6 +69,9 @@ module ActiveRecord
63
69
  if options[:as].present?
64
70
  sql << " AS (#{options[:as]})"
65
71
  end
72
+ if options[:primary_key] == true
73
+ sql << " PRIMARY KEY"
74
+ end
66
75
  end
67
76
 
68
77
  def action_sql(action, dependency)
@@ -26,25 +26,26 @@ module ActiveRecord
26
26
  end
27
27
 
28
28
  class IndexDefinition < ActiveRecord::ConnectionAdapters::IndexDefinition
29
- attr_accessor :table, :name, :unique, :type, :parameters, :statement_parameters, :tablespace, :columns
29
+ attr_accessor :parameters, :statement_parameters, :tablespace
30
30
 
31
- def initialize(table, name, unique, type, parameters, statement_parameters, tablespace, columns)
32
- @table = table
33
- @name = name
34
- @unique = unique
35
- @type = type
31
+ def initialize(table, name, unique, columns, lengths, orders, where, type, using, parameters, statement_parameters, tablespace)
36
32
  @parameters = parameters
37
33
  @statement_parameters = statement_parameters
38
34
  @tablespace = tablespace
39
- @columns = columns
40
- super(table, name, unique, columns, nil, nil, nil, nil)
35
+ super(table, name, unique, columns, lengths, orders, where, type, using)
41
36
  end
42
37
  end
43
38
 
44
- class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
39
+ class ColumnDefinition < ActiveRecord::ConnectionAdapters::ColumnDefinition
40
+
41
+ end
45
42
 
46
- def raw(name, options={})
47
- column(name, :raw, options)
43
+ class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
44
+ attr_accessor :tablespace, :organization
45
+ def initialize(name, temporary = false, options = nil, as = nil, tablespace = nil, organization = nil, comment: nil)
46
+ @tablespace = tablespace
47
+ @organization = organization
48
+ super(name, temporary, options, as, comment: comment)
48
49
  end
49
50
 
50
51
  def virtual(* args)
@@ -69,6 +70,11 @@ module ActiveRecord
69
70
  super(name, type, options)
70
71
  end
71
72
 
73
+ private
74
+ def create_column_definition(name, type)
75
+ OracleEnhanced::ColumnDefinition.new name, type
76
+ end
77
+
72
78
  end
73
79
 
74
80
  class AlterTable < ActiveRecord::ConnectionAdapters::AlterTable
@@ -1,225 +1,207 @@
1
1
  module ActiveRecord #:nodoc:
2
2
  module ConnectionAdapters #:nodoc:
3
- module OracleEnhancedSchemaDumper #:nodoc:
3
+ module OracleEnhanced #:nodoc:
4
+ module SchemaDumper #:nodoc:
4
5
 
5
- def self.included(base) #:nodoc:
6
- base.class_eval do
7
- private
8
- alias_method_chain :tables, :oracle_enhanced
9
- alias_method_chain :indexes, :oracle_enhanced
10
- alias_method_chain :foreign_keys, :oracle_enhanced
11
- end
12
- end
13
-
14
- private
6
+ private
15
7
 
16
- def ignore_table?(table)
17
- ['schema_migrations', ignore_tables].flatten.any? do |ignored|
18
- case ignored
19
- when String; remove_prefix_and_suffix(table) == ignored
20
- when Regexp; remove_prefix_and_suffix(table) =~ ignored
21
- else
22
- raise StandardError, 'ActiveRecord::SchemaDumper.ignore_tables accepts an array of String and / or Regexp values.'
8
+ def tables(stream)
9
+ # do not include materialized views in schema dump - they should be created separately after schema creation
10
+ sorted_tables = (@connection.data_sources - @connection.materialized_views).sort
11
+ sorted_tables.each do |tbl|
12
+ # add table prefix or suffix for schema_migrations
13
+ next if ignored? tbl
14
+ # change table name inspect method
15
+ tbl.extend TableInspect
16
+ table(tbl, stream)
17
+ # add primary key trigger if table has it
18
+ primary_key_trigger(tbl, stream)
19
+ end
20
+ # following table definitions
21
+ # add foreign keys if table has them
22
+ sorted_tables.each do |tbl|
23
+ next if ignored? tbl
24
+ foreign_keys(tbl, stream)
23
25
  end
24
- end
25
- end
26
26
 
27
- def tables_with_oracle_enhanced(stream)
28
- return tables_without_oracle_enhanced(stream) unless @connection.respond_to?(:materialized_views)
29
- # do not include materialized views in schema dump - they should be created separately after schema creation
30
- sorted_tables = (@connection.tables - @connection.materialized_views).sort
31
- sorted_tables.each do |tbl|
32
- # add table prefix or suffix for schema_migrations
33
- next if ignore_table? tbl
34
- # change table name inspect method
35
- tbl.extend TableInspect
36
- oracle_enhanced_table(tbl, stream)
37
- # add primary key trigger if table has it
38
- primary_key_trigger(tbl, stream)
39
- end
40
- # following table definitions
41
- # add foreign keys if table has them
42
- sorted_tables.each do |tbl|
43
- next if ignore_table? tbl
44
- foreign_keys(tbl, stream)
27
+ # add synonyms in local schema
28
+ synonyms(stream)
45
29
  end
46
30
 
47
- # add synonyms in local schema
48
- synonyms(stream)
49
- end
50
-
51
- def primary_key_trigger(table_name, stream)
52
- if @connection.respond_to?(:has_primary_key_trigger?) && @connection.has_primary_key_trigger?(table_name)
53
- pk, _pk_seq = @connection.pk_and_sequence_for(table_name)
54
- stream.print " add_primary_key_trigger #{table_name.inspect}"
55
- stream.print ", primary_key: \"#{pk}\"" if pk != 'id'
56
- stream.print "\n\n"
31
+ def primary_key_trigger(table_name, stream)
32
+ if @connection.respond_to?(:has_primary_key_trigger?) && @connection.has_primary_key_trigger?(table_name)
33
+ pk, _pk_seq = @connection.pk_and_sequence_for(table_name)
34
+ stream.print " add_primary_key_trigger #{table_name.inspect}"
35
+ stream.print ", primary_key: \"#{pk}\"" if pk != 'id'
36
+ stream.print "\n\n"
37
+ end
57
38
  end
58
- end
59
39
 
60
- def foreign_keys_with_oracle_enhanced(table_name, stream)
61
- return foreign_keys_without_oracle_enhanced(table_name, stream)
62
- end
63
-
64
- def synonyms(stream)
65
- if @connection.respond_to?(:synonyms)
66
- syns = @connection.synonyms
67
- syns.each do |syn|
68
- next if ignore_table? syn.name
69
- table_name = syn.table_name
70
- table_name = "#{syn.table_owner}.#{table_name}" if syn.table_owner
71
- table_name = "#{table_name}@#{syn.db_link}" if syn.db_link
72
- stream.print " add_synonym #{syn.name.inspect}, #{table_name.inspect}, force: true"
73
- stream.puts
40
+ def synonyms(stream)
41
+ if @connection.respond_to?(:synonyms)
42
+ syns = @connection.synonyms
43
+ syns.each do |syn|
44
+ next if ignored? syn.name
45
+ table_name = syn.table_name
46
+ table_name = "#{syn.table_owner}.#{table_name}" if syn.table_owner
47
+ table_name = "#{table_name}@#{syn.db_link}" if syn.db_link
48
+ stream.print " add_synonym #{syn.name.inspect}, #{table_name.inspect}, force: true"
49
+ stream.puts
50
+ end
51
+ stream.puts unless syns.empty?
74
52
  end
75
- stream.puts unless syns.empty?
76
53
  end
77
- end
78
54
 
79
- def indexes_with_oracle_enhanced(table, stream)
80
- # return original method if not using oracle_enhanced
81
- if (rails_env = defined?(Rails.env) ? Rails.env : (defined?(RAILS_ENV) ? RAILS_ENV : nil)) &&
82
- ActiveRecord::Base.configurations[rails_env] &&
83
- ActiveRecord::Base.configurations[rails_env]['adapter'] != 'oracle_enhanced'
84
- return indexes_without_oracle_enhanced(table, stream)
85
- end
86
- if (indexes = @connection.indexes(table)).any?
87
- add_index_statements = indexes.map do |index|
88
- case index.type
89
- when nil
90
- # use table.inspect as it will remove prefix and suffix
91
- statement_parts = [ ('add_index ' + table.inspect) ]
92
- statement_parts << index.columns.inspect
93
- statement_parts << ('name: ' + index.name.inspect)
94
- statement_parts << 'unique: true' if index.unique
95
- statement_parts << 'tablespace: ' + index.tablespace.inspect if index.tablespace
96
- when 'CTXSYS.CONTEXT'
97
- if index.statement_parameters
98
- statement_parts = [ ('add_context_index ' + table.inspect) ]
99
- statement_parts << index.statement_parameters
100
- else
101
- statement_parts = [ ('add_context_index ' + table.inspect) ]
55
+ def indexes(table, stream)
56
+ if (indexes = @connection.indexes(table)).any?
57
+ add_index_statements = indexes.map do |index|
58
+ case index.type
59
+ when nil
60
+ # use table.inspect as it will remove prefix and suffix
61
+ statement_parts = [ ('add_index ' + table.inspect) ]
102
62
  statement_parts << index.columns.inspect
103
63
  statement_parts << ('name: ' + index.name.inspect)
64
+ statement_parts << 'unique: true' if index.unique
65
+ statement_parts << 'tablespace: ' + index.tablespace.inspect if index.tablespace
66
+ when 'CTXSYS.CONTEXT'
67
+ if index.statement_parameters
68
+ statement_parts = [ ('add_context_index ' + table.inspect) ]
69
+ statement_parts << index.statement_parameters
70
+ else
71
+ statement_parts = [ ('add_context_index ' + table.inspect) ]
72
+ statement_parts << index.columns.inspect
73
+ statement_parts << ('name: ' + index.name.inspect)
74
+ end
75
+ else
76
+ # unrecognized index type
77
+ statement_parts = ["# unrecognized index #{index.name.inspect} with type #{index.type.inspect}"]
104
78
  end
105
- else
106
- # unrecognized index type
107
- statement_parts = ["# unrecognized index #{index.name.inspect} with type #{index.type.inspect}"]
79
+ ' ' + statement_parts.join(', ')
108
80
  end
109
- ' ' + statement_parts.join(', ')
110
- end
111
81
 
112
- stream.puts add_index_statements.sort.join("\n")
113
- stream.puts
82
+ stream.puts add_index_statements.sort.join("\n")
83
+ stream.puts
84
+ end
114
85
  end
115
- end
116
86
 
117
- def oracle_enhanced_table(table, stream)
118
- columns = @connection.columns(table)
119
- begin
120
- tbl = StringIO.new
87
+ def table(table, stream)
88
+ columns = @connection.columns(table)
89
+ begin
90
+ tbl = StringIO.new
121
91
 
122
- # first dump primary key column
123
- if @connection.respond_to?(:pk_and_sequence_for)
124
- pk, _pk_seq = @connection.pk_and_sequence_for(table)
125
- elsif @connection.respond_to?(:primary_key)
126
- pk = @connection.primary_key(table)
127
- end
92
+ # first dump primary key column
93
+ if @connection.respond_to?(:primary_keys)
94
+ pk = @connection.primary_keys(table)
95
+ pk = pk.first unless pk.size > 1
96
+ else
97
+ pk = @connection.primary_key(table)
98
+ end
128
99
 
129
- tbl.print " create_table #{table.inspect}"
100
+ tbl.print " create_table #{table.inspect}"
130
101
 
131
- # addition to make temporary option work
132
- tbl.print ", temporary: true" if @connection.temporary_table?(table)
102
+ # addition to make temporary option work
103
+ tbl.print ", temporary: true" if @connection.temporary_table?(table)
133
104
 
134
- table_comments = @connection.table_comment(table)
135
- unless table_comments.nil?
136
- tbl.print ", comment: #{table_comments.inspect}"
137
- end
105
+ table_comments = @connection.table_comment(table)
106
+ unless table_comments.nil?
107
+ tbl.print ", comment: #{table_comments.inspect}"
108
+ end
138
109
 
139
- if columns.detect { |c| c.name == pk }
140
- if pk != 'id'
141
- tbl.print %Q(, primary_key: "#{pk}")
110
+ case pk
111
+ when String
112
+ tbl.print ", primary_key: #{pk.inspect}" unless pk == 'id'
113
+ pkcol = columns.detect { |c| c.name == pk }
114
+ pkcolspec = @connection.column_spec_for_primary_key(pkcol)
115
+ if pkcolspec.present?
116
+ pkcolspec.each do |key, value|
117
+ tbl.print ", #{key}: #{value}"
118
+ end
119
+ end
120
+ when Array
121
+ tbl.print ", primary_key: #{pk.inspect}"
122
+ else
123
+ tbl.print ", id: false"
142
124
  end
143
- else
144
- tbl.print ", id: false"
145
- end
146
- tbl.print ", force: :cascade"
147
- tbl.puts " do |t|"
148
125
 
149
- # then dump all non-primary key columns
150
- column_specs = columns.map do |column|
151
- raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" if @types[column.type].nil?
152
- next if column.name == pk
153
- @connection.column_spec(column, @types)
154
- end.compact
126
+ tbl.print ", force: :cascade"
127
+ tbl.puts " do |t|"
128
+
129
+ # then dump all non-primary key columns
130
+ column_specs = columns.map do |column|
131
+ raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" unless @connection.valid_type?(column.type)
132
+ next if column.name == pk
133
+ @connection.column_spec(column)
134
+ end.compact
135
+
136
+ # find all migration keys used in this table
137
+ #
138
+ # TODO `& column_specs.map(&:keys).flatten` should be executed
139
+ # in migration_keys_with_oracle_enhanced
140
+ keys = @connection.migration_keys & column_specs.map(&:keys).flatten
155
141
 
156
- # find all migration keys used in this table
157
- #
158
- # TODO `& column_specs.map(&:keys).flatten` should be executed
159
- # in migration_keys_with_oracle_enhanced
160
- keys = @connection.migration_keys & column_specs.map(&:keys).flatten
142
+ # figure out the lengths for each column based on above keys
143
+ lengths = keys.map{ |key| column_specs.map{ |spec| spec[key] ? spec[key].length + 2 : 0 }.max }
161
144
 
162
- # figure out the lengths for each column based on above keys
163
- lengths = keys.map{ |key| column_specs.map{ |spec| spec[key] ? spec[key].length + 2 : 0 }.max }
145
+ # the string we're going to sprintf our values against, with standardized column widths
146
+ format_string = lengths.map{ |len| "%-#{len}s" }
164
147
 
165
- # the string we're going to sprintf our values against, with standardized column widths
166
- format_string = lengths.map{ |len| "%-#{len}s" }
148
+ # find the max length for the 'type' column, which is special
149
+ type_length = column_specs.map{ |column| column[:type].length }.max
167
150
 
168
- # find the max length for the 'type' column, which is special
169
- type_length = column_specs.map{ |column| column[:type].length }.max
151
+ # add column type definition to our format string
152
+ format_string.unshift " t.%-#{type_length}s "
170
153
 
171
- # add column type definition to our format string
172
- format_string.unshift " t.%-#{type_length}s "
154
+ format_string *= ''
173
155
 
174
- format_string *= ''
156
+ # dirty hack to replace virtual_type: with type:
157
+ column_specs.each do |colspec|
158
+ values = keys.zip(lengths).map{ |key, len| colspec.key?(key) ? colspec[key] + ", " : " " * len }
159
+ values.unshift colspec[:type]
160
+ tbl.print((format_string % values).gsub(/,\s*$/, '').gsub(/virtual_type:/, "type:"))
161
+ tbl.puts
162
+ end
175
163
 
176
- column_specs.each do |colspec|
177
- values = keys.zip(lengths).map{ |key, len| colspec.key?(key) ? colspec[key] + ", " : " " * len }
178
- values.unshift colspec[:type]
179
- tbl.print((format_string % values).gsub(/,\s*$/, ''))
164
+ tbl.puts " end"
180
165
  tbl.puts
166
+
167
+ indexes(table, tbl)
168
+
169
+ tbl.rewind
170
+ stream.print tbl.read
171
+ rescue => e
172
+ stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}"
173
+ stream.puts "# #{e.message}"
174
+ stream.puts
181
175
  end
182
176
 
183
- tbl.puts " end"
184
- tbl.puts
185
-
186
- indexes(table, tbl)
187
-
188
- tbl.rewind
189
- stream.print tbl.read
190
- rescue => e
191
- stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}"
192
- stream.puts "# #{e.message}"
193
- stream.puts
177
+ stream
194
178
  end
195
-
196
- stream
197
- end
198
179
 
199
- def remove_prefix_and_suffix(table)
200
- table.gsub(/^(#{ActiveRecord::Base.table_name_prefix})(.+)(#{ActiveRecord::Base.table_name_suffix})$/, "\\2")
201
- end
202
-
203
- # remove table name prefix and suffix when doing #inspect (which is used in tables method)
204
- module TableInspect #:nodoc:
205
- def inspect
206
- remove_prefix_and_suffix(self)
180
+ def remove_prefix_and_suffix(table)
181
+ table.gsub(/^(#{ActiveRecord::Base.table_name_prefix})(.+)(#{ActiveRecord::Base.table_name_suffix})$/, "\\2")
207
182
  end
208
-
209
- private
210
- def remove_prefix_and_suffix(table_name)
211
- if table_name =~ /\A#{ActiveRecord::Base.table_name_prefix.to_s.gsub('$','\$')}(.*)#{ActiveRecord::Base.table_name_suffix.to_s.gsub('$','\$')}\Z/
212
- "\"#{$1}\""
213
- else
214
- "\"#{table_name}\""
183
+
184
+ # remove table name prefix and suffix when doing #inspect (which is used in tables method)
185
+ module TableInspect #:nodoc:
186
+ def inspect
187
+ remove_prefix_and_suffix(self)
188
+ end
189
+
190
+ private
191
+ def remove_prefix_and_suffix(table_name)
192
+ if table_name =~ /\A#{ActiveRecord::Base.table_name_prefix.to_s.gsub('$','\$')}(.*)#{ActiveRecord::Base.table_name_suffix.to_s.gsub('$','\$')}\Z/
193
+ "\"#{$1}\""
194
+ else
195
+ "\"#{table_name}\""
196
+ end
215
197
  end
216
198
  end
217
- end
218
199
 
200
+ end
219
201
  end
220
202
  end
221
- end
222
203
 
223
- ActiveRecord::SchemaDumper.class_eval do
224
- include ActiveRecord::ConnectionAdapters::OracleEnhancedSchemaDumper
204
+ class SchemaDumper #:nodoc:
205
+ prepend ConnectionAdapters::OracleEnhanced::SchemaDumper
206
+ end
225
207
  end