pmacs-activerecord-oracle_enhanced-adapter 1.4.2.rc1 → 1.5.5.1

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 (44) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +11 -40
  3. data/History.md +170 -0
  4. data/README.md +61 -5
  5. data/Rakefile +1 -0
  6. data/VERSION +1 -1
  7. data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +330 -161
  8. data/lib/active_record/connection_adapters/oracle_enhanced_column.rb +48 -8
  9. data/lib/active_record/connection_adapters/oracle_enhanced_column_dumper.rb +77 -0
  10. data/lib/active_record/connection_adapters/oracle_enhanced_connection.rb +8 -24
  11. data/lib/active_record/connection_adapters/oracle_enhanced_context_index.rb +4 -13
  12. data/lib/active_record/connection_adapters/oracle_enhanced_database_tasks.rb +61 -0
  13. data/lib/active_record/connection_adapters/oracle_enhanced_dirty.rb +13 -12
  14. data/lib/active_record/connection_adapters/oracle_enhanced_jdbc_connection.rb +42 -19
  15. data/lib/active_record/connection_adapters/oracle_enhanced_oci_connection.rb +28 -74
  16. data/lib/active_record/connection_adapters/oracle_enhanced_procedures.rb +165 -231
  17. data/lib/active_record/connection_adapters/oracle_enhanced_schema_creation.rb +89 -0
  18. data/lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb +16 -24
  19. data/lib/active_record/connection_adapters/oracle_enhanced_schema_dumper.rb +29 -38
  20. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb +93 -42
  21. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements_ext.rb +5 -3
  22. data/lib/active_record/connection_adapters/oracle_enhanced_structure_dump.rb +7 -7
  23. data/lib/active_record/connection_adapters/oracle_enhanced_version.rb +1 -1
  24. data/lib/pmacs-activerecord-oracle_enhanced-adapter.rb +2 -2
  25. data/pmacs-activerecord-oracle_enhanced-adapter.gemspec +19 -17
  26. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +35 -99
  27. data/spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb +17 -3
  28. data/spec/active_record/connection_adapters/oracle_enhanced_context_index_spec.rb +105 -98
  29. data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +74 -44
  30. data/spec/active_record/connection_adapters/oracle_enhanced_database_tasks_spec.rb +89 -0
  31. data/spec/active_record/connection_adapters/oracle_enhanced_dbms_output_spec.rb +3 -3
  32. data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +13 -2
  33. data/spec/active_record/connection_adapters/oracle_enhanced_procedures_spec.rb +11 -12
  34. data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +252 -60
  35. data/spec/active_record/connection_adapters/oracle_enhanced_schema_statements_spec.rb +170 -40
  36. data/spec/active_record/connection_adapters/oracle_enhanced_structure_dump_spec.rb +14 -8
  37. data/spec/spec_helper.rb +25 -54
  38. metadata +41 -72
  39. data/lib/active_record/connection_adapters/oracle_enhanced.rake +0 -105
  40. data/lib/active_record/connection_adapters/oracle_enhanced_activerecord_patches.rb +0 -41
  41. data/lib/active_record/connection_adapters/oracle_enhanced_base_ext.rb +0 -118
  42. data/lib/active_record/connection_adapters/oracle_enhanced_core_ext.rb +0 -25
  43. data/lib/active_record/connection_adapters/oracle_enhanced_tasks.rb +0 -17
  44. data/spec/active_record/connection_adapters/oracle_enhanced_core_ext_spec.rb +0 -19
@@ -0,0 +1,89 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ class OracleEnhancedAdapter < AbstractAdapter
4
+ class SchemaCreation < AbstractAdapter::SchemaCreation
5
+ private
6
+
7
+ def visit_ColumnDefinition(o)
8
+ if o.type.to_sym == :virtual
9
+ sql_type = type_to_sql(o.default[:type], o.limit, o.precision, o.scale) if o.default[:type]
10
+ "#{quote_column_name(o.name)} #{sql_type} AS (#{o.default[:as]})"
11
+ else
12
+ super
13
+ end
14
+ end
15
+
16
+ def visit_TableDefinition(o)
17
+ tablespace = tablespace_for(:table, o.options[:tablespace])
18
+ create_sql = "CREATE#{' GLOBAL TEMPORARY' if o.temporary} TABLE "
19
+ create_sql << "#{quote_table_name(o.name)} ("
20
+ create_sql << o.columns.map { |c| accept c }.join(', ')
21
+ create_sql << ")"
22
+ unless o.temporary
23
+ create_sql << " ORGANIZATION #{o.options[:organization]}" if o.options[:organization]
24
+ create_sql << "#{tablespace}"
25
+ end
26
+ create_sql << " #{o.options[:options]}"
27
+ create_sql
28
+ end
29
+
30
+ def tablespace_for(obj_type, tablespace_option, table_name=nil, column_name=nil)
31
+ tablespace_sql = ''
32
+ if tablespace = (tablespace_option || default_tablespace_for(obj_type))
33
+ tablespace_sql << if [:blob, :clob].include?(obj_type.to_sym)
34
+ " LOB (#{quote_column_name(column_name)}) STORE AS #{column_name.to_s[0..10]}_#{table_name.to_s[0..14]}_ls (TABLESPACE #{tablespace})"
35
+ else
36
+ " TABLESPACE #{tablespace}"
37
+ end
38
+ end
39
+ tablespace_sql
40
+ end
41
+
42
+ def default_tablespace_for(type)
43
+ (ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_tablespaces[type] ||
44
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_tablespaces[native_database_types[type][:name]]) rescue nil
45
+ end
46
+
47
+ def foreign_key_definition(to_table, options = {})
48
+ @conn.foreign_key_definition(to_table, options)
49
+ end
50
+
51
+ def add_column_options!(sql, options)
52
+ type = options[:type] || ((column = options[:column]) && column.type)
53
+ type = type && type.to_sym
54
+ # handle case of defaults for CLOB columns, which would otherwise get "quoted" incorrectly
55
+ if options_include_default?(options)
56
+ if type == :text
57
+ sql << " DEFAULT #{@conn.quote(options[:default])}"
58
+ else
59
+ # from abstract adapter
60
+ sql << " DEFAULT #{@conn.quote(options[:default], options[:column])}"
61
+ end
62
+ end
63
+ # must explicitly add NULL or NOT NULL to allow change_column to work on migrations
64
+ if options[:null] == false
65
+ sql << " NOT NULL"
66
+ elsif options[:null] == true
67
+ sql << " NULL" unless type == :primary_key
68
+ end
69
+ # add AS expression for virtual columns
70
+ if options[:as].present?
71
+ sql << " AS (#{options[:as]})"
72
+ end
73
+ end
74
+
75
+ # This method does not exist in SchemaCreation at Rails 4.0
76
+ # It can be removed only when Oracle enhanced adapter supports Rails 4.1 and higher
77
+ def options_include_default?(options)
78
+ options.include?(:default) && !(options[:null] == false && options[:default].nil?)
79
+ end
80
+
81
+ end
82
+
83
+ def schema_creation
84
+ SchemaCreation.new self
85
+ end
86
+
87
+ end
88
+ end
89
+ end
@@ -13,8 +13,8 @@ module ActiveRecord
13
13
  module OracleEnhancedColumnDefinition
14
14
  def self.included(base) #:nodoc:
15
15
  base.class_eval do
16
- alias_method_chain :to_sql, :virtual_columns
17
- alias to_s :to_sql
16
+ #alias_method_chain :to_sql, :virtual_columns
17
+ #alias to_s :to_sql
18
18
  end
19
19
  end
20
20
 
@@ -42,10 +42,6 @@ module ActiveRecord
42
42
  include OracleEnhancedTableDefinition
43
43
  end
44
44
 
45
- base::ColumnDefinition.class_eval do
46
- include OracleEnhancedColumnDefinition
47
- end
48
-
49
45
  # Available starting from ActiveRecord 2.1
50
46
  base::Table.class_eval do
51
47
  include OracleEnhancedTable
@@ -64,8 +60,6 @@ module ActiveRecord
64
60
  def self.included(base) #:nodoc:
65
61
  base.class_eval do
66
62
  alias_method_chain :references, :foreign_keys
67
- alias_method_chain :to_sql, :foreign_keys
68
-
69
63
  alias_method_chain :column, :virtual_columns
70
64
  end
71
65
  end
@@ -110,11 +104,15 @@ module ActiveRecord
110
104
  # Note: If no name is specified, the database driver creates one for you!
111
105
  def references_with_foreign_keys(*args)
112
106
  options = args.extract_options!
107
+ index_options = options[:index]
113
108
  fk_options = options.delete(:foreign_key)
114
109
 
115
110
  if fk_options && !options[:polymorphic]
116
111
  fk_options = {} if fk_options == true
117
- args.each { |to_table| foreign_key(to_table, fk_options) }
112
+ args.each do |to_table|
113
+ foreign_key(to_table, fk_options)
114
+ add_index(to_table, "#{to_table}_id", index_options.is_a?(Hash) ? index_options : nil) if index_options
115
+ end
118
116
  end
119
117
 
120
118
  references_without_foreign_keys(*(args << options))
@@ -133,7 +131,8 @@ module ActiveRecord
133
131
  # ====== Defining the column of the +to_table+.
134
132
  # t.foreign_key(:people, :column => :sender_id, :primary_key => :person_id)
135
133
  def foreign_key(to_table, options = {})
136
- if @base.respond_to?(:supports_foreign_keys?) && @base.supports_foreign_keys?
134
+ #TODO
135
+ if ActiveRecord::Base.connection.supports_foreign_keys?
137
136
  to_table = to_table.to_s.pluralize if ActiveRecord::Base.pluralize_table_names
138
137
  foreign_keys << ForeignKey.new(@base, to_table, options)
139
138
  else
@@ -141,20 +140,9 @@ module ActiveRecord
141
140
  end
142
141
  end
143
142
 
144
- def to_sql_with_foreign_keys #:nodoc:
145
- sql = to_sql_without_foreign_keys
146
- sql << ', ' << (foreign_keys * ', ') unless foreign_keys.blank?
147
- sql
143
+ def foreign_keys
144
+ @foreign_keys ||= []
148
145
  end
149
-
150
- def lob_columns
151
- columns.select(&:lob?)
152
- end
153
-
154
- private
155
- def foreign_keys
156
- @foreign_keys ||= []
157
- end
158
146
  end
159
147
 
160
148
  module OracleEnhancedTable
@@ -212,6 +200,7 @@ module ActiveRecord
212
200
  def references_with_foreign_keys(*args)
213
201
  options = args.extract_options!
214
202
  polymorphic = options[:polymorphic]
203
+ index_options = options[:index]
215
204
  fk_options = options.delete(:foreign_key)
216
205
 
217
206
  references_without_foreign_keys(*(args << options))
@@ -219,7 +208,10 @@ module ActiveRecord
219
208
  args.extract_options!
220
209
  if fk_options && !polymorphic
221
210
  fk_options = {} if fk_options == true
222
- args.each { |to_table| foreign_key(to_table, fk_options) }
211
+ args.each do |to_table|
212
+ foreign_key(to_table, fk_options)
213
+ add_index(to_table, "#{to_table}_id", index_options.is_a?(Hash) ? index_options : nil) if index_options
214
+ end
223
215
  end
224
216
  end
225
217
  end
@@ -13,10 +13,10 @@ module ActiveRecord #:nodoc:
13
13
  private
14
14
 
15
15
  def ignore_table?(table)
16
- [ActiveRecord::Migrator.proper_table_name('schema_migrations'), ignore_tables].flatten.any? do |ignored|
16
+ ['schema_migrations', ignore_tables].flatten.any? do |ignored|
17
17
  case ignored
18
- when String; table == ignored
19
- when Regexp; table =~ ignored
18
+ when String; remove_prefix_and_suffix(table) == ignored
19
+ when Regexp; remove_prefix_and_suffix(table) =~ ignored
20
20
  else
21
21
  raise StandardError, 'ActiveRecord::SchemaDumper.ignore_tables accepts an array of String and / or Regexp values.'
22
22
  end
@@ -51,7 +51,7 @@ module ActiveRecord #:nodoc:
51
51
  if @connection.respond_to?(:has_primary_key_trigger?) && @connection.has_primary_key_trigger?(table_name)
52
52
  pk, pk_seq = @connection.pk_and_sequence_for(table_name)
53
53
  stream.print " add_primary_key_trigger #{table_name.inspect}"
54
- stream.print ", :primary_key => \"#{pk}\"" if pk != 'id'
54
+ stream.print ", primary_key: \"#{pk}\"" if pk != 'id'
55
55
  stream.print "\n\n"
56
56
  end
57
57
  end
@@ -61,24 +61,24 @@ module ActiveRecord #:nodoc:
61
61
  add_foreign_key_statements = foreign_keys.map do |foreign_key|
62
62
  statement_parts = [ ('add_foreign_key ' + foreign_key.from_table.inspect) ]
63
63
  statement_parts << foreign_key.to_table.inspect
64
-
64
+
65
65
  if foreign_key.options[:columns].size == 1
66
66
  column = foreign_key.options[:columns].first
67
67
  if column != "#{foreign_key.to_table.singularize}_id"
68
- statement_parts << (':column => ' + column.inspect)
68
+ statement_parts << ('column: ' + column.inspect)
69
69
  end
70
-
70
+
71
71
  if foreign_key.options[:references].first != 'id'
72
- statement_parts << (':primary_key => ' + foreign_key.options[:primary_key].inspect)
72
+ statement_parts << ('primary_key: ' + foreign_key.options[:references].first.inspect)
73
73
  end
74
74
  else
75
- statement_parts << (':columns => ' + foreign_key.options[:columns].inspect)
75
+ statement_parts << ('columns: ' + foreign_key.options[:columns].inspect)
76
76
  end
77
77
 
78
- statement_parts << (':name => ' + foreign_key.options[:name].inspect)
78
+ statement_parts << ('name: ' + foreign_key.options[:name].inspect)
79
79
 
80
80
  unless foreign_key.options[:dependent].blank?
81
- statement_parts << (':dependent => ' + foreign_key.options[:dependent].inspect)
81
+ statement_parts << ('dependent: ' + foreign_key.options[:dependent].inspect)
82
82
  end
83
83
 
84
84
  ' ' + statement_parts.join(', ')
@@ -97,7 +97,7 @@ module ActiveRecord #:nodoc:
97
97
  table_name = syn.table_name
98
98
  table_name = "#{syn.table_owner}.#{table_name}" if syn.table_owner
99
99
  table_name = "#{table_name}@#{syn.db_link}" if syn.db_link
100
- stream.print " add_synonym #{syn.name.inspect}, #{table_name.inspect}, :force => true"
100
+ stream.print " add_synonym #{syn.name.inspect}, #{table_name.inspect}, force: true"
101
101
  stream.puts
102
102
  end
103
103
  stream.puts unless syns.empty?
@@ -118,9 +118,9 @@ module ActiveRecord #:nodoc:
118
118
  # use table.inspect as it will remove prefix and suffix
119
119
  statement_parts = [ ('add_index ' + table.inspect) ]
120
120
  statement_parts << index.columns.inspect
121
- statement_parts << (':name => ' + index.name.inspect)
122
- statement_parts << ':unique => true' if index.unique
123
- statement_parts << ':tablespace => ' + index.tablespace.inspect if index.tablespace
121
+ statement_parts << ('name: ' + index.name.inspect)
122
+ statement_parts << 'unique: true' if index.unique
123
+ statement_parts << 'tablespace: ' + index.tablespace.inspect if index.tablespace
124
124
  when 'CTXSYS.CONTEXT'
125
125
  if index.statement_parameters
126
126
  statement_parts = [ ('add_context_index ' + table.inspect) ]
@@ -128,7 +128,7 @@ module ActiveRecord #:nodoc:
128
128
  else
129
129
  statement_parts = [ ('add_context_index ' + table.inspect) ]
130
130
  statement_parts << index.columns.inspect
131
- statement_parts << (':name => ' + index.name.inspect)
131
+ statement_parts << ('name: ' + index.name.inspect)
132
132
  end
133
133
  else
134
134
  # unrecognized index type
@@ -157,42 +157,30 @@ module ActiveRecord #:nodoc:
157
157
  tbl.print " create_table #{table.inspect}"
158
158
 
159
159
  # addition to make temporary option work
160
- tbl.print ", :temporary => true" if @connection.temporary_table?(table)
160
+ tbl.print ", temporary: true" if @connection.temporary_table?(table)
161
161
 
162
162
  if columns.detect { |c| c.name == pk }
163
163
  if pk != 'id'
164
- tbl.print %Q(, :primary_key => "#{pk}")
164
+ tbl.print %Q(, primary_key: "#{pk}")
165
165
  end
166
166
  else
167
- tbl.print ", :id => false"
167
+ tbl.print ", id: false"
168
168
  end
169
- tbl.print ", :force => true"
169
+ tbl.print ", force: true"
170
170
  tbl.puts " do |t|"
171
171
 
172
172
  # then dump all non-primary key columns
173
173
  column_specs = columns.map do |column|
174
174
  raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" if @types[column.type].nil?
175
175
  next if column.name == pk
176
- spec = {}
177
- spec[:name] = column.name.inspect
178
- spec[:type] = column.virtual? ? 'virtual' : column.type.to_s
179
- spec[:type] = column.virtual? ? 'virtual' : column.type.to_s
180
- spec[:virtual_type] = column.type.inspect if column.virtual?
181
- spec[:limit] = column.limit.inspect if column.limit != @types[column.type][:limit] && column.type != :decimal
182
- spec[:precision] = column.precision.inspect if !column.precision.nil?
183
- spec[:scale] = column.scale.inspect if !column.scale.nil?
184
- spec[:null] = 'false' if !column.null
185
- spec[:as] = column.virtual_column_data_default if column.virtual?
186
- spec[:default] = default_string(column.default) if column.has_default? && !column.virtual?
187
- (spec.keys - [:name, :type]).each do |k|
188
- key_s = (k == :virtual_type ? ":type => " : "#{k.inspect} => ")
189
- spec[k] = key_s + spec[k]
190
- end
191
- spec
176
+ @connection.column_spec(column, @types)
192
177
  end.compact
193
178
 
194
179
  # find all migration keys used in this table
195
- keys = [:name, :limit, :precision, :scale, :default, :null, :as, :virtual_type] & column_specs.map(&:keys).flatten
180
+ #
181
+ # TODO `& column_specs.map(&:keys).flatten` should be executed
182
+ # in migration_keys_with_oracle_enhanced
183
+ keys = @connection.migration_keys & column_specs.map(&:keys).flatten
196
184
 
197
185
  # figure out the lengths for each column based on above keys
198
186
  lengths = keys.map{ |key| column_specs.map{ |spec| spec[key] ? spec[key].length + 2 : 0 }.max }
@@ -230,7 +218,10 @@ module ActiveRecord #:nodoc:
230
218
 
231
219
  stream
232
220
  end
233
-
221
+
222
+ def remove_prefix_and_suffix(table)
223
+ table.gsub(/^(#{ActiveRecord::Base.table_name_prefix})(.+)(#{ActiveRecord::Base.table_name_suffix})$/, "\\2")
224
+ end
234
225
 
235
226
  # remove table name prefix and suffix when doing #inspect (which is used in tables method)
236
227
  module TableInspect #:nodoc:
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  require 'digest/sha1'
2
3
 
3
4
  module ActiveRecord
@@ -22,11 +23,11 @@ module ActiveRecord
22
23
  # end
23
24
  #
24
25
  # Create primary key trigger (so that you can skip primary key value in INSERT statement).
25
- # By default trigger name will be "table_name_pkt", you can override the name with
26
+ # By default trigger name will be "table_name_pkt", you can override the name with
26
27
  # :trigger_name option (but it is not recommended to override it as then this trigger will
27
28
  # not be detected by ActiveRecord model and it will still do prefetching of sequence value).
28
29
  # Example:
29
- #
30
+ #
30
31
  # create_table :users, :primary_key_trigger => true do |t|
31
32
  # # ...
32
33
  # end
@@ -37,12 +38,13 @@ module ActiveRecord
37
38
  # t.string :first_name, :comment => “Given name”
38
39
  # t.string :last_name, :comment => “Surname”
39
40
  # end
40
-
41
- def create_table(name, options = {}, &block)
41
+
42
+ def create_table(name, options = {})
42
43
  create_sequence = options[:id] != false
43
44
  column_comments = {}
44
-
45
- table_definition = TableDefinition.new(self)
45
+ temporary = options.delete(:temporary)
46
+ additional_options = options
47
+ table_definition = create_table_definition name, temporary, additional_options
46
48
  table_definition.primary_key(options[:primary_key] || Base.get_primary_key(name.to_s.singularize)) unless options[:id] == false
47
49
 
48
50
  # store that primary key was defined in create_table block
@@ -68,7 +70,7 @@ module ActiveRecord
68
70
  end
69
71
  end
70
72
 
71
- result = block.call(table_definition) if block
73
+ yield table_definition if block_given?
72
74
  create_sequence = create_sequence || table_definition.create_sequence
73
75
  column_comments = table_definition.column_comments if table_definition.column_comments
74
76
  tablespace = tablespace_for(:table, options[:tablespace])
@@ -77,35 +79,38 @@ module ActiveRecord
77
79
  drop_table(name, options)
78
80
  end
79
81
 
80
- create_sql = "CREATE#{' GLOBAL TEMPORARY' if options[:temporary]} TABLE "
81
- create_sql << quote_table_name(name)
82
- create_sql << " (#{table_definition.to_sql})"
83
- unless options[:temporary]
84
- create_sql << " ORGANIZATION #{options[:organization]}" if options[:organization]
85
- create_sql << tablespace
86
- table_definition.lob_columns.each{|cd| create_sql << tablespace_for(cd.sql_type.downcase.to_sym, nil, name, cd.name)}
87
- end
88
- create_sql << " #{options[:options]}"
89
- execute create_sql
90
-
82
+ execute schema_creation.accept table_definition
83
+
91
84
  create_sequence_and_trigger(name, options) if create_sequence
92
-
85
+
93
86
  add_table_comment name, options[:comment]
94
87
  column_comments.each do |column_name, comment|
95
88
  add_comment name, column_name, comment
96
89
  end
97
-
90
+ table_definition.indexes.each_pair { |c,o| add_index name, c, o }
91
+
92
+ unless table_definition.foreign_keys.nil?
93
+ table_definition.foreign_keys.each do |foreign_key|
94
+ add_foreign_key(table_definition.name, foreign_key.to_table, foreign_key.options)
95
+ end
96
+ end
97
+ end
98
+
99
+ def create_table_definition(name, temporary, options)
100
+ TableDefinition.new native_database_types, name, temporary, options
98
101
  end
99
102
 
100
- def rename_table(name, new_name) #:nodoc:
103
+ def rename_table(table_name, new_name) #:nodoc:
101
104
  if new_name.to_s.length > table_name_length
102
105
  raise ArgumentError, "New table name '#{new_name}' is too long; the limit is #{table_name_length} characters"
103
106
  end
104
107
  if "#{new_name}_seq".to_s.length > sequence_name_length
105
108
  raise ArgumentError, "New sequence name '#{new_name}_seq' is too long; the limit is #{sequence_name_length} characters"
106
109
  end
107
- execute "RENAME #{quote_table_name(name)} TO #{quote_table_name(new_name)}"
108
- execute "RENAME #{quote_table_name("#{name}_seq")} TO #{quote_table_name("#{new_name}_seq")}"
110
+ execute "RENAME #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
111
+ execute "RENAME #{quote_table_name("#{table_name}_seq")} TO #{quote_table_name("#{new_name}_seq")}" rescue nil
112
+
113
+ rename_table_indexes(table_name, new_name)
109
114
  end
110
115
 
111
116
  def drop_table(name, options = {}) #:nodoc:
@@ -114,30 +119,74 @@ module ActiveRecord
114
119
  execute "DROP SEQUENCE #{quote_table_name(seq_name)}" rescue nil
115
120
  ensure
116
121
  clear_table_columns_cache(name)
122
+ self.all_schema_indexes = nil
123
+ end
124
+
125
+ def initialize_schema_migrations_table
126
+ sm_table = ActiveRecord::Migrator.schema_migrations_table_name
127
+
128
+ unless table_exists?(sm_table)
129
+ index_name = "#{Base.table_name_prefix}unique_schema_migrations#{Base.table_name_suffix}"
130
+ if index_name.length > index_name_length
131
+ truncate_to = index_name_length - index_name.to_s.length - 1
132
+ truncated_name = "unique_schema_migrations"[0..truncate_to]
133
+ index_name = "#{Base.table_name_prefix}#{truncated_name}#{Base.table_name_suffix}"
134
+ end
135
+
136
+ create_table(sm_table, :id => false) do |schema_migrations_table|
137
+ schema_migrations_table.column :version, :string, :null => false
138
+ end
139
+ add_index sm_table, :version, :unique => true, :name => index_name
140
+
141
+ # Backwards-compatibility: if we find schema_info, assume we've
142
+ # migrated up to that point:
143
+ si_table = Base.table_name_prefix + 'schema_info' + Base.table_name_suffix
144
+ if table_exists?(si_table)
145
+ ActiveSupport::Deprecation.warn "Usage of the schema table `#{si_table}` is deprecated. Please switch to using `schema_migrations` table"
146
+
147
+ old_version = select_value("SELECT version FROM #{quote_table_name(si_table)}").to_i
148
+ assume_migrated_upto_version(old_version)
149
+ drop_table(si_table)
150
+ end
151
+ end
117
152
  end
118
153
 
119
154
  # clear cached indexes when adding new index
120
155
  def add_index(table_name, column_name, options = {}) #:nodoc:
121
156
  column_names = Array(column_name)
122
- index_name = index_name(table_name, :column => column_names)
157
+ index_name = index_name(table_name, column: column_names)
123
158
 
124
159
  if Hash === options # legacy support, since this param was a string
160
+ options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :tablespace, :options, :using)
161
+
125
162
  index_type = options[:unique] ? "UNIQUE" : ""
126
163
  index_name = options[:name].to_s if options.key?(:name)
127
164
  tablespace = tablespace_for(:index, options[:tablespace])
165
+ max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
166
+ additional_options = options[:options]
128
167
  else
168
+ if options
169
+ message = "Passing a string as third argument of `add_index` is deprecated and will" +
170
+ " be removed in Rails 4.1." +
171
+ " Use add_index(#{table_name.inspect}, #{column_name.inspect}, unique: true) instead"
172
+
173
+ ActiveSupport::Deprecation.warn message
174
+ end
175
+
129
176
  index_type = options
177
+ additional_options = nil
178
+ max_index_length = allowed_index_name_length
130
179
  end
131
180
 
132
- if index_name.to_s.length > index_name_length
133
- raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters"
181
+ if index_name.to_s.length > max_index_length
182
+ raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{max_index_length} characters"
134
183
  end
135
184
  if index_name_exists?(table_name, index_name, false)
136
185
  raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
137
186
  end
138
187
  quoted_column_names = column_names.map { |e| quote_column_name_or_expression(e) }.join(", ")
139
188
 
140
- execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})#{tablespace} #{options[:options]}"
189
+ execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})#{tablespace} #{additional_options}"
141
190
  ensure
142
191
  self.all_schema_indexes = nil
143
192
  end
@@ -147,6 +196,14 @@ module ActiveRecord
147
196
  def remove_index(table_name, options = {}) #:nodoc:
148
197
  index_name = index_name(table_name, options)
149
198
  unless index_name_exists?(table_name, index_name, true)
199
+ # sometimes options can be String or Array with column names
200
+ options = {} unless options.is_a?(Hash)
201
+ if options.has_key? :name
202
+ options_without_column = options.dup
203
+ options_without_column.delete :column
204
+ index_name_without_column = index_name(table_name, options_without_column)
205
+ return index_name_without_column if index_name_exists?(table_name, index_name_without_column, false)
206
+ end
150
207
  raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
151
208
  end
152
209
  remove_index!(table_name, index_name)
@@ -166,10 +223,10 @@ module ActiveRecord
166
223
  options = {} unless options.is_a?(Hash)
167
224
  identifier_max_length = options[:identifier_max_length] || index_name_length
168
225
  return default_name if default_name.length <= identifier_max_length
169
-
226
+
170
227
  # remove 'index', 'on' and 'and' keywords
171
228
  shortened_name = "i_#{table_name}_#{Array(options[:column]) * '_'}"
172
-
229
+
173
230
  # leave just first three letters from each word
174
231
  if shortened_name.length > identifier_max_length
175
232
  shortened_name = shortened_name.split('_').map{|w| w[0,3]}.join('_')
@@ -221,6 +278,8 @@ module ActiveRecord
221
278
  add_column_sql << tablespace_for((type_to_sql(type).downcase.to_sym), nil, table_name, column_name) if type
222
279
 
223
280
  execute(add_column_sql)
281
+
282
+ create_sequence_and_trigger(table_name, options) if type && type.to_sym == :primary_key
224
283
  ensure
225
284
  clear_table_columns_cache(table_name)
226
285
  end
@@ -266,25 +325,17 @@ module ActiveRecord
266
325
 
267
326
  def rename_column(table_name, column_name, new_column_name) #:nodoc:
268
327
  execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} to #{quote_column_name(new_column_name)}"
328
+ self.all_schema_indexes = nil
329
+ rename_column_indexes(table_name, column_name, new_column_name)
269
330
  ensure
270
331
  clear_table_columns_cache(table_name)
271
332
  end
272
333
 
273
- def remove_column(table_name, *column_names) #:nodoc:
274
- major, minor = ActiveRecord::VERSION::MAJOR, ActiveRecord::VERSION::MINOR
275
- is_deprecated = (major == 3 and minor >= 2) or major > 3
276
-
277
- if column_names.flatten! and is_deprecated
278
- message = 'Passing array to remove_columns is deprecated, please use ' +
279
- 'multiple arguments, like: `remove_columns(:posts, :foo, :bar)`'
280
- ActiveSupport::Deprecation.warn message, caller
281
- end
282
-
283
- column_names.each do |column_name|
284
- execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
285
- end
334
+ def remove_column(table_name, column_name, type = nil, options = {}) #:nodoc:
335
+ execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
286
336
  ensure
287
337
  clear_table_columns_cache(table_name)
338
+ self.all_schema_indexes = nil
288
339
  end
289
340
 
290
341
  def add_comment(table_name, column_name, comment) #:nodoc: