activerecord 3.0.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (181) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +2102 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +35 -44
  5. data/examples/performance.rb +110 -100
  6. data/lib/active_record/aggregations.rb +59 -75
  7. data/lib/active_record/associations/alias_tracker.rb +76 -0
  8. data/lib/active_record/associations/association.rb +248 -0
  9. data/lib/active_record/associations/association_scope.rb +135 -0
  10. data/lib/active_record/associations/belongs_to_association.rb +60 -59
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +16 -59
  12. data/lib/active_record/associations/builder/association.rb +108 -0
  13. data/lib/active_record/associations/builder/belongs_to.rb +98 -0
  14. data/lib/active_record/associations/builder/collection_association.rb +89 -0
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +39 -0
  16. data/lib/active_record/associations/builder/has_many.rb +15 -0
  17. data/lib/active_record/associations/builder/has_one.rb +25 -0
  18. data/lib/active_record/associations/builder/singular_association.rb +32 -0
  19. data/lib/active_record/associations/collection_association.rb +608 -0
  20. data/lib/active_record/associations/collection_proxy.rb +986 -0
  21. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +40 -112
  22. data/lib/active_record/associations/has_many_association.rb +83 -76
  23. data/lib/active_record/associations/has_many_through_association.rb +147 -66
  24. data/lib/active_record/associations/has_one_association.rb +67 -108
  25. data/lib/active_record/associations/has_one_through_association.rb +21 -25
  26. data/lib/active_record/associations/join_dependency/join_association.rb +174 -0
  27. data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
  28. data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
  29. data/lib/active_record/associations/join_dependency.rb +235 -0
  30. data/lib/active_record/associations/join_helper.rb +45 -0
  31. data/lib/active_record/associations/preloader/association.rb +121 -0
  32. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  33. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  34. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
  35. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  36. data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
  37. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  38. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  39. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  40. data/lib/active_record/associations/preloader/through_association.rb +63 -0
  41. data/lib/active_record/associations/preloader.rb +178 -0
  42. data/lib/active_record/associations/singular_association.rb +64 -0
  43. data/lib/active_record/associations/through_association.rb +87 -0
  44. data/lib/active_record/associations.rb +512 -1224
  45. data/lib/active_record/attribute_assignment.rb +201 -0
  46. data/lib/active_record/attribute_methods/before_type_cast.rb +49 -12
  47. data/lib/active_record/attribute_methods/dirty.rb +51 -28
  48. data/lib/active_record/attribute_methods/primary_key.rb +94 -22
  49. data/lib/active_record/attribute_methods/query.rb +5 -4
  50. data/lib/active_record/attribute_methods/read.rb +63 -72
  51. data/lib/active_record/attribute_methods/serialization.rb +162 -0
  52. data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -41
  53. data/lib/active_record/attribute_methods/write.rb +39 -13
  54. data/lib/active_record/attribute_methods.rb +362 -29
  55. data/lib/active_record/autosave_association.rb +132 -75
  56. data/lib/active_record/base.rb +83 -1627
  57. data/lib/active_record/callbacks.rb +69 -47
  58. data/lib/active_record/coders/yaml_column.rb +38 -0
  59. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +411 -138
  60. data/lib/active_record/connection_adapters/abstract/database_limits.rb +21 -11
  61. data/lib/active_record/connection_adapters/abstract/database_statements.rb +234 -173
  62. data/lib/active_record/connection_adapters/abstract/query_cache.rb +36 -22
  63. data/lib/active_record/connection_adapters/abstract/quoting.rb +82 -25
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +176 -414
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +562 -232
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +281 -53
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +782 -0
  70. data/lib/active_record/connection_adapters/column.rb +318 -0
  71. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  72. data/lib/active_record/connection_adapters/mysql2_adapter.rb +273 -0
  73. data/lib/active_record/connection_adapters/mysql_adapter.rb +365 -450
  74. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
  75. data/lib/active_record/connection_adapters/postgresql/cast.rb +152 -0
  76. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid.rb +366 -0
  78. data/lib/active_record/connection_adapters/postgresql/quoting.rb +171 -0
  79. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  80. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +489 -0
  81. data/lib/active_record/connection_adapters/postgresql_adapter.rb +672 -752
  82. data/lib/active_record/connection_adapters/schema_cache.rb +129 -0
  83. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +588 -17
  84. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  85. data/lib/active_record/connection_handling.rb +98 -0
  86. data/lib/active_record/core.rb +463 -0
  87. data/lib/active_record/counter_cache.rb +108 -101
  88. data/lib/active_record/dynamic_matchers.rb +131 -0
  89. data/lib/active_record/errors.rb +54 -13
  90. data/lib/active_record/explain.rb +38 -0
  91. data/lib/active_record/explain_registry.rb +30 -0
  92. data/lib/active_record/explain_subscriber.rb +29 -0
  93. data/lib/active_record/fixture_set/file.rb +55 -0
  94. data/lib/active_record/fixtures.rb +703 -785
  95. data/lib/active_record/inheritance.rb +200 -0
  96. data/lib/active_record/integration.rb +60 -0
  97. data/lib/active_record/locale/en.yml +8 -1
  98. data/lib/active_record/locking/optimistic.rb +69 -60
  99. data/lib/active_record/locking/pessimistic.rb +34 -12
  100. data/lib/active_record/log_subscriber.rb +40 -6
  101. data/lib/active_record/migration/command_recorder.rb +164 -0
  102. data/lib/active_record/migration/join_table.rb +15 -0
  103. data/lib/active_record/migration.rb +614 -216
  104. data/lib/active_record/model_schema.rb +345 -0
  105. data/lib/active_record/nested_attributes.rb +248 -119
  106. data/lib/active_record/null_relation.rb +65 -0
  107. data/lib/active_record/persistence.rb +275 -57
  108. data/lib/active_record/query_cache.rb +29 -9
  109. data/lib/active_record/querying.rb +62 -0
  110. data/lib/active_record/railtie.rb +135 -21
  111. data/lib/active_record/railties/console_sandbox.rb +5 -0
  112. data/lib/active_record/railties/controller_runtime.rb +17 -5
  113. data/lib/active_record/railties/databases.rake +249 -359
  114. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  115. data/lib/active_record/readonly_attributes.rb +30 -0
  116. data/lib/active_record/reflection.rb +283 -103
  117. data/lib/active_record/relation/batches.rb +38 -34
  118. data/lib/active_record/relation/calculations.rb +252 -139
  119. data/lib/active_record/relation/delegation.rb +125 -0
  120. data/lib/active_record/relation/finder_methods.rb +182 -188
  121. data/lib/active_record/relation/merger.rb +161 -0
  122. data/lib/active_record/relation/predicate_builder.rb +86 -21
  123. data/lib/active_record/relation/query_methods.rb +917 -134
  124. data/lib/active_record/relation/spawn_methods.rb +53 -92
  125. data/lib/active_record/relation.rb +405 -143
  126. data/lib/active_record/result.rb +67 -0
  127. data/lib/active_record/runtime_registry.rb +17 -0
  128. data/lib/active_record/sanitization.rb +168 -0
  129. data/lib/active_record/schema.rb +20 -14
  130. data/lib/active_record/schema_dumper.rb +55 -46
  131. data/lib/active_record/schema_migration.rb +39 -0
  132. data/lib/active_record/scoping/default.rb +146 -0
  133. data/lib/active_record/scoping/named.rb +175 -0
  134. data/lib/active_record/scoping.rb +82 -0
  135. data/lib/active_record/serialization.rb +8 -46
  136. data/lib/active_record/serializers/xml_serializer.rb +21 -68
  137. data/lib/active_record/statement_cache.rb +26 -0
  138. data/lib/active_record/store.rb +156 -0
  139. data/lib/active_record/tasks/database_tasks.rb +203 -0
  140. data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
  141. data/lib/active_record/tasks/mysql_database_tasks.rb +143 -0
  142. data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
  143. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  144. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  145. data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
  146. data/lib/active_record/test_case.rb +57 -28
  147. data/lib/active_record/timestamp.rb +49 -18
  148. data/lib/active_record/transactions.rb +106 -63
  149. data/lib/active_record/translation.rb +22 -0
  150. data/lib/active_record/validations/associated.rb +25 -24
  151. data/lib/active_record/validations/presence.rb +65 -0
  152. data/lib/active_record/validations/uniqueness.rb +123 -83
  153. data/lib/active_record/validations.rb +29 -29
  154. data/lib/active_record/version.rb +7 -5
  155. data/lib/active_record.rb +83 -34
  156. data/lib/rails/generators/active_record/migration/migration_generator.rb +46 -9
  157. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
  158. data/lib/rails/generators/active_record/migration/templates/migration.rb +30 -8
  159. data/lib/rails/generators/active_record/model/model_generator.rb +15 -5
  160. data/lib/rails/generators/active_record/model/templates/model.rb +7 -2
  161. data/lib/rails/generators/active_record/model/templates/module.rb +3 -1
  162. data/lib/rails/generators/active_record.rb +4 -8
  163. metadata +163 -121
  164. data/CHANGELOG +0 -6023
  165. data/examples/associations.png +0 -0
  166. data/lib/active_record/association_preload.rb +0 -403
  167. data/lib/active_record/associations/association_collection.rb +0 -562
  168. data/lib/active_record/associations/association_proxy.rb +0 -295
  169. data/lib/active_record/associations/through_association_scope.rb +0 -154
  170. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -113
  171. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -401
  172. data/lib/active_record/dynamic_finder_match.rb +0 -53
  173. data/lib/active_record/dynamic_scope_match.rb +0 -32
  174. data/lib/active_record/named_scope.rb +0 -138
  175. data/lib/active_record/observer.rb +0 -140
  176. data/lib/active_record/session_store.rb +0 -340
  177. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -16
  178. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  179. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -2
  180. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -24
  181. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -16
@@ -1,10 +1,12 @@
1
- require 'active_support/core_ext/array/wrap'
1
+ require 'active_record/migration/join_table'
2
2
 
3
3
  module ActiveRecord
4
4
  module ConnectionAdapters # :nodoc:
5
5
  module SchemaStatements
6
- # Returns a Hash of mappings from the abstract data types to the native
7
- # database types. See TableDefinition#column for details on the recognized
6
+ include ActiveRecord::Migration::JoinTable
7
+
8
+ # Returns a hash of mappings from the abstract data types to the native
9
+ # database types. See TableDefinition#column for details on the recognized
8
10
  # abstract data types.
9
11
  def native_database_types
10
12
  {}
@@ -12,11 +14,13 @@ module ActiveRecord
12
14
 
13
15
  # Truncates a table alias according to the limits of the current adapter.
14
16
  def table_alias_for(table_name)
15
- table_name[0..table_alias_length-1].gsub(/\./, '_')
17
+ table_name[0...table_alias_length].tr('.', '_')
16
18
  end
17
19
 
18
- # def tables(name = nil) end
19
-
20
+ # Checks to see if the table +table_name+ exists on the database.
21
+ #
22
+ # table_exists?(:developers)
23
+ #
20
24
  def table_exists?(table_name)
21
25
  tables.include?(table_name.to_s)
22
26
  end
@@ -24,22 +28,22 @@ module ActiveRecord
24
28
  # Returns an array of indexes for the given table.
25
29
  # def indexes(table_name, name = nil) end
26
30
 
27
- # Checks to see if an index exists on a table for a given index definition
31
+ # Checks to see if an index exists on a table for a given index definition.
28
32
  #
29
- # === Examples
30
- # # Check an index exists
31
- # index_exists?(:suppliers, :company_id)
33
+ # # Check an index exists
34
+ # index_exists?(:suppliers, :company_id)
32
35
  #
33
- # # Check an index on multiple columns exists
34
- # index_exists?(:suppliers, [:company_id, :company_type])
36
+ # # Check an index on multiple columns exists
37
+ # index_exists?(:suppliers, [:company_id, :company_type])
35
38
  #
36
- # # Check a unique index exists
37
- # index_exists?(:suppliers, :company_id, :unique => true)
39
+ # # Check a unique index exists
40
+ # index_exists?(:suppliers, :company_id, unique: true)
41
+ #
42
+ # # Check an index with a custom name exists
43
+ # index_exists?(:suppliers, :company_id, name: "idx_company_id"
38
44
  #
39
- # # Check an index with a custom name exists
40
- # index_exists?(:suppliers, :company_id, :name => "idx_company_id"
41
45
  def index_exists?(table_name, column_name, options = {})
42
- column_names = Array.wrap(column_name)
46
+ column_names = Array(column_name)
43
47
  index_name = options.key?(:name) ? options[:name].to_s : index_name(table_name, :column => column_names)
44
48
  if options[:unique]
45
49
  indexes(table_name).any?{ |i| i.unique && i.name == index_name }
@@ -50,55 +54,63 @@ module ActiveRecord
50
54
 
51
55
  # Returns an array of Column objects for the table specified by +table_name+.
52
56
  # See the concrete implementation for details on the expected parameter values.
53
- def columns(table_name, name = nil) end
57
+ def columns(table_name) end
54
58
 
55
59
  # Checks to see if a column exists in a given table.
56
60
  #
57
- # === Examples
58
- # # Check a column exists
59
- # column_exists?(:suppliers, :name)
61
+ # # Check a column exists
62
+ # column_exists?(:suppliers, :name)
63
+ #
64
+ # # Check a column exists of a particular type
65
+ # column_exists?(:suppliers, :name, :string)
60
66
  #
61
- # # Check a column exists of a particular type
62
- # column_exists?(:suppliers, :name, :string)
67
+ # # Check a column exists with a specific definition
68
+ # column_exists?(:suppliers, :name, :string, limit: 100)
69
+ # column_exists?(:suppliers, :name, :string, default: 'default')
70
+ # column_exists?(:suppliers, :name, :string, null: false)
71
+ # column_exists?(:suppliers, :tax, :decimal, precision: 8, scale: 2)
63
72
  #
64
- # # Check a column exists with a specific definition
65
- # column_exists?(:suppliers, :name, :string, :limit => 100)
66
73
  def column_exists?(table_name, column_name, type = nil, options = {})
67
74
  columns(table_name).any?{ |c| c.name == column_name.to_s &&
68
- (!type || c.type == type) &&
69
- (!options[:limit] || c.limit == options[:limit]) &&
70
- (!options[:precision] || c.precision == options[:precision]) &&
71
- (!options[:scale] || c.scale == options[:scale]) }
75
+ (!type || c.type == type) &&
76
+ (!options.key?(:limit) || c.limit == options[:limit]) &&
77
+ (!options.key?(:precision) || c.precision == options[:precision]) &&
78
+ (!options.key?(:scale) || c.scale == options[:scale]) &&
79
+ (!options.key?(:default) || c.default == options[:default]) &&
80
+ (!options.key?(:null) || c.null == options[:null]) }
72
81
  end
73
82
 
74
83
  # Creates a new table with the name +table_name+. +table_name+ may either
75
84
  # be a String or a Symbol.
76
85
  #
77
- # There are two ways to work with +create_table+. You can use the block
86
+ # There are two ways to work with +create_table+. You can use the block
78
87
  # form or the regular form, like this:
79
88
  #
80
89
  # === Block form
81
- # # create_table() passes a TableDefinition object to the block.
82
- # # This form will not only create the table, but also columns for the
83
- # # table.
84
90
  #
85
- # create_table(:suppliers) do |t|
86
- # t.column :name, :string, :limit => 60
87
- # # Other fields here
88
- # end
91
+ # # create_table() passes a TableDefinition object to the block.
92
+ # # This form will not only create the table, but also columns for the
93
+ # # table.
94
+ #
95
+ # create_table(:suppliers) do |t|
96
+ # t.column :name, :string, limit: 60
97
+ # # Other fields here
98
+ # end
89
99
  #
90
100
  # === Block form, with shorthand
91
- # # You can also use the column types as method calls, rather than calling the column method.
92
- # create_table(:suppliers) do |t|
93
- # t.string :name, :limit => 60
94
- # # Other fields here
95
- # end
101
+ #
102
+ # # You can also use the column types as method calls, rather than calling the column method.
103
+ # create_table(:suppliers) do |t|
104
+ # t.string :name, limit: 60
105
+ # # Other fields here
106
+ # end
96
107
  #
97
108
  # === Regular form
98
- # # Creates a table called 'suppliers' with no columns.
99
- # create_table(:suppliers)
100
- # # Add a column to 'suppliers'.
101
- # add_column(:suppliers, :name, :string, {:limit => 60})
109
+ #
110
+ # # Creates a table called 'suppliers' with no columns.
111
+ # create_table(:suppliers)
112
+ # # Add a column to 'suppliers'.
113
+ # add_column(:suppliers, :name, :string, {limit: 60})
102
114
  #
103
115
  # The +options+ hash can include the following keys:
104
116
  # [<tt>:id</tt>]
@@ -109,7 +121,7 @@ module ActiveRecord
109
121
  # Defaults to +id+. If <tt>:id</tt> is false this option is ignored.
110
122
  #
111
123
  # Also note that this just sets the primary key in the table. You additionally
112
- # need to configure the primary key in the model via the +set_primary_key+ macro.
124
+ # need to configure the primary key in the model via +self.primary_key=+.
113
125
  # Models do NOT auto-detect the primary key from their table definition.
114
126
  #
115
127
  # [<tt>:options</tt>]
@@ -120,122 +132,222 @@ module ActiveRecord
120
132
  # Set to true to drop the table before creating it.
121
133
  # Defaults to false.
122
134
  #
123
- # ===== Examples
124
135
  # ====== Add a backend specific option to the generated SQL (MySQL)
125
- # create_table(:suppliers, :options => 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
136
+ #
137
+ # create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
138
+ #
126
139
  # generates:
127
- # CREATE TABLE suppliers (
128
- # id int(11) DEFAULT NULL auto_increment PRIMARY KEY
129
- # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
140
+ #
141
+ # CREATE TABLE suppliers (
142
+ # id int(11) DEFAULT NULL auto_increment PRIMARY KEY
143
+ # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
130
144
  #
131
145
  # ====== Rename the primary key column
132
- # create_table(:objects, :primary_key => 'guid') do |t|
133
- # t.column :name, :string, :limit => 80
134
- # end
146
+ #
147
+ # create_table(:objects, primary_key: 'guid') do |t|
148
+ # t.column :name, :string, limit: 80
149
+ # end
150
+ #
135
151
  # generates:
136
- # CREATE TABLE objects (
137
- # guid int(11) DEFAULT NULL auto_increment PRIMARY KEY,
138
- # name varchar(80)
139
- # )
152
+ #
153
+ # CREATE TABLE objects (
154
+ # guid int(11) DEFAULT NULL auto_increment PRIMARY KEY,
155
+ # name varchar(80)
156
+ # )
140
157
  #
141
158
  # ====== Do not add a primary key column
142
- # create_table(:categories_suppliers, :id => false) do |t|
143
- # t.column :category_id, :integer
144
- # t.column :supplier_id, :integer
145
- # end
159
+ #
160
+ # create_table(:categories_suppliers, id: false) do |t|
161
+ # t.column :category_id, :integer
162
+ # t.column :supplier_id, :integer
163
+ # end
164
+ #
146
165
  # generates:
147
- # CREATE TABLE categories_suppliers (
148
- # category_id int,
149
- # supplier_id int
150
- # )
166
+ #
167
+ # CREATE TABLE categories_suppliers (
168
+ # category_id int,
169
+ # supplier_id int
170
+ # )
151
171
  #
152
172
  # See also TableDefinition#column for details on how to create columns.
153
173
  def create_table(table_name, options = {})
154
- table_definition = TableDefinition.new(self)
155
- table_definition.primary_key(options[:primary_key] || Base.get_primary_key(table_name.to_s.singularize)) unless options[:id] == false
174
+ td = create_table_definition table_name, options[:temporary], options[:options]
156
175
 
157
- yield table_definition if block_given?
176
+ unless options[:id] == false
177
+ pk = options.fetch(:primary_key) {
178
+ Base.get_primary_key table_name.to_s.singularize
179
+ }
180
+
181
+ td.primary_key pk, options.fetch(:id, :primary_key), options
182
+ end
183
+
184
+ yield td if block_given?
158
185
 
159
186
  if options[:force] && table_exists?(table_name)
160
187
  drop_table(table_name, options)
161
188
  end
162
189
 
163
- create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
164
- create_sql << "#{quote_table_name(table_name)} ("
165
- create_sql << table_definition.to_sql
166
- create_sql << ") #{options[:options]}"
167
- execute create_sql
190
+ execute schema_creation.accept td
191
+ td.indexes.each_pair { |c,o| add_index table_name, c, o }
192
+ end
193
+
194
+ # Creates a new join table with the name created using the lexical order of the first two
195
+ # arguments. These arguments can be a String or a Symbol.
196
+ #
197
+ # # Creates a table called 'assemblies_parts' with no id.
198
+ # create_join_table(:assemblies, :parts)
199
+ #
200
+ # You can pass a +options+ hash can include the following keys:
201
+ # [<tt>:table_name</tt>]
202
+ # Sets the table name overriding the default
203
+ # [<tt>:column_options</tt>]
204
+ # Any extra options you want appended to the columns definition.
205
+ # [<tt>:options</tt>]
206
+ # Any extra options you want appended to the table definition.
207
+ # [<tt>:temporary</tt>]
208
+ # Make a temporary table.
209
+ # [<tt>:force</tt>]
210
+ # Set to true to drop the table before creating it.
211
+ # Defaults to false.
212
+ #
213
+ # Note that +create_join_table+ does not create any indices by default; you can use
214
+ # its block form to do so yourself:
215
+ #
216
+ # create_join_table :products, :categories do |t|
217
+ # t.index :product_id
218
+ # t.index :category_id
219
+ # end
220
+ #
221
+ # ====== Add a backend specific option to the generated SQL (MySQL)
222
+ #
223
+ # create_join_table(:assemblies, :parts, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
224
+ #
225
+ # generates:
226
+ #
227
+ # CREATE TABLE assemblies_parts (
228
+ # assembly_id int NOT NULL,
229
+ # part_id int NOT NULL,
230
+ # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
231
+ #
232
+ def create_join_table(table_1, table_2, options = {})
233
+ join_table_name = find_join_table_name(table_1, table_2, options)
234
+
235
+ column_options = options.delete(:column_options) || {}
236
+ column_options.reverse_merge!(null: false)
237
+
238
+ t1_column, t2_column = [table_1, table_2].map{ |t| t.to_s.singularize.foreign_key }
239
+
240
+ create_table(join_table_name, options.merge!(id: false)) do |td|
241
+ td.integer t1_column, column_options
242
+ td.integer t2_column, column_options
243
+ yield td if block_given?
244
+ end
245
+ end
246
+
247
+ # Drops the join table specified by the given arguments.
248
+ # See +create_join_table+ for details.
249
+ #
250
+ # Although this command ignores the block if one is given, it can be helpful
251
+ # to provide one in a migration's +change+ method so it can be reverted.
252
+ # In that case, the block will be used by create_join_table.
253
+ def drop_join_table(table_1, table_2, options = {})
254
+ join_table_name = find_join_table_name(table_1, table_2, options)
255
+ drop_table(join_table_name)
168
256
  end
169
257
 
170
258
  # A block for changing columns in +table+.
171
259
  #
172
- # === Example
173
- # # change_table() yields a Table instance
174
- # change_table(:suppliers) do |t|
175
- # t.column :name, :string, :limit => 60
176
- # # Other column alterations here
177
- # end
260
+ # # change_table() yields a Table instance
261
+ # change_table(:suppliers) do |t|
262
+ # t.column :name, :string, limit: 60
263
+ # # Other column alterations here
264
+ # end
265
+ #
266
+ # The +options+ hash can include the following keys:
267
+ # [<tt>:bulk</tt>]
268
+ # Set this to true to make this a bulk alter query, such as
269
+ #
270
+ # ALTER TABLE `users` ADD COLUMN age INT(11), ADD COLUMN birthdate DATETIME ...
271
+ #
272
+ # Defaults to false.
178
273
  #
179
- # ===== Examples
180
274
  # ====== Add a column
181
- # change_table(:suppliers) do |t|
182
- # t.column :name, :string, :limit => 60
183
- # end
275
+ #
276
+ # change_table(:suppliers) do |t|
277
+ # t.column :name, :string, limit: 60
278
+ # end
184
279
  #
185
280
  # ====== Add 2 integer columns
186
- # change_table(:suppliers) do |t|
187
- # t.integer :width, :height, :null => false, :default => 0
188
- # end
281
+ #
282
+ # change_table(:suppliers) do |t|
283
+ # t.integer :width, :height, null: false, default: 0
284
+ # end
189
285
  #
190
286
  # ====== Add created_at/updated_at columns
191
- # change_table(:suppliers) do |t|
192
- # t.timestamps
193
- # end
287
+ #
288
+ # change_table(:suppliers) do |t|
289
+ # t.timestamps
290
+ # end
194
291
  #
195
292
  # ====== Add a foreign key column
196
- # change_table(:suppliers) do |t|
197
- # t.references :company
198
- # end
199
293
  #
200
- # Creates a <tt>company_id(integer)</tt> column
294
+ # change_table(:suppliers) do |t|
295
+ # t.references :company
296
+ # end
297
+ #
298
+ # Creates a <tt>company_id(integer)</tt> column.
201
299
  #
202
300
  # ====== Add a polymorphic foreign key column
301
+ #
203
302
  # change_table(:suppliers) do |t|
204
- # t.belongs_to :company, :polymorphic => true
303
+ # t.belongs_to :company, polymorphic: true
205
304
  # end
206
305
  #
207
- # Creates <tt>company_type(varchar)</tt> and <tt>company_id(integer)</tt> columns
306
+ # Creates <tt>company_type(varchar)</tt> and <tt>company_id(integer)</tt> columns.
208
307
  #
209
308
  # ====== Remove a column
309
+ #
210
310
  # change_table(:suppliers) do |t|
211
311
  # t.remove :company
212
312
  # end
213
313
  #
214
314
  # ====== Remove several columns
315
+ #
215
316
  # change_table(:suppliers) do |t|
216
317
  # t.remove :company_id
217
318
  # t.remove :width, :height
218
319
  # end
219
320
  #
220
321
  # ====== Remove an index
322
+ #
221
323
  # change_table(:suppliers) do |t|
222
324
  # t.remove_index :company_id
223
325
  # end
224
326
  #
225
- # See also Table for details on
226
- # all of the various column transformation
227
- def change_table(table_name)
228
- yield Table.new(table_name, self)
327
+ # See also Table for details on all of the various column transformation.
328
+ def change_table(table_name, options = {})
329
+ if supports_bulk_alter? && options[:bulk]
330
+ recorder = ActiveRecord::Migration::CommandRecorder.new(self)
331
+ yield update_table_definition(table_name, recorder)
332
+ bulk_change_table(table_name, recorder.commands)
333
+ else
334
+ yield update_table_definition(table_name, self)
335
+ end
229
336
  end
230
337
 
231
338
  # Renames a table.
232
- # ===== Example
233
- # rename_table('octopuses', 'octopi')
339
+ #
340
+ # rename_table('octopuses', 'octopi')
341
+ #
234
342
  def rename_table(table_name, new_name)
235
343
  raise NotImplementedError, "rename_table is not implemented"
236
344
  end
237
345
 
238
346
  # Drops a table from the database.
347
+ #
348
+ # Although this command ignores +options+ and the block if one is given, it can be helpful
349
+ # to provide these in a migration's +change+ method so it can be reverted.
350
+ # In that case, +options+ and the block will be used by create_table.
239
351
  def drop_table(table_name, options = {})
240
352
  execute "DROP TABLE #{quote_table_name(table_name)}"
241
353
  end
@@ -243,142 +355,205 @@ module ActiveRecord
243
355
  # Adds a new column to the named table.
244
356
  # See TableDefinition#column for details of the options you can use.
245
357
  def add_column(table_name, column_name, type, options = {})
246
- add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
247
- add_column_options!(add_column_sql, options)
248
- execute(add_column_sql)
249
- end
250
-
251
- # Removes the column(s) from the table definition.
252
- # ===== Examples
253
- # remove_column(:suppliers, :qualification)
254
- # remove_columns(:suppliers, :qualification, :experience)
255
- def remove_column(table_name, *column_names)
256
- raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty?
257
- column_names.flatten.each do |column_name|
258
- execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{quote_column_name(column_name)}"
358
+ at = create_alter_table table_name
359
+ at.add_column(column_name, type, options)
360
+ execute schema_creation.accept at
361
+ end
362
+
363
+ # Removes the given columns from the table definition.
364
+ #
365
+ # remove_columns(:suppliers, :qualification, :experience)
366
+ #
367
+ def remove_columns(table_name, *column_names)
368
+ raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)") if column_names.empty?
369
+ column_names.each do |column_name|
370
+ remove_column(table_name, column_name)
259
371
  end
260
372
  end
261
- alias :remove_columns :remove_column
373
+
374
+ # Removes the column from the table definition.
375
+ #
376
+ # remove_column(:suppliers, :qualification)
377
+ #
378
+ # The +type+ and +options+ parameters will be ignored if present. It can be helpful
379
+ # to provide these in a migration's +change+ method so it can be reverted.
380
+ # In that case, +type+ and +options+ will be used by add_column.
381
+ def remove_column(table_name, column_name, type = nil, options = {})
382
+ execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{quote_column_name(column_name)}"
383
+ end
262
384
 
263
385
  # Changes the column's definition according to the new options.
264
386
  # See TableDefinition#column for details of the options you can use.
265
- # ===== Examples
266
- # change_column(:suppliers, :name, :string, :limit => 80)
267
- # change_column(:accounts, :description, :text)
387
+ #
388
+ # change_column(:suppliers, :name, :string, limit: 80)
389
+ # change_column(:accounts, :description, :text)
390
+ #
268
391
  def change_column(table_name, column_name, type, options = {})
269
392
  raise NotImplementedError, "change_column is not implemented"
270
393
  end
271
394
 
272
- # Sets a new default value for a column. If you want to set the default
273
- # value to +NULL+, you are out of luck. You need to
274
- # DatabaseStatements#execute the appropriate SQL statement yourself.
275
- # ===== Examples
276
- # change_column_default(:suppliers, :qualification, 'new')
277
- # change_column_default(:accounts, :authorized, 1)
395
+ # Sets a new default value for a column:
396
+ #
397
+ # change_column_default(:suppliers, :qualification, 'new')
398
+ # change_column_default(:accounts, :authorized, 1)
399
+ #
400
+ # Setting the default to +nil+ effectively drops the default:
401
+ #
402
+ # change_column_default(:users, :email, nil)
403
+ #
278
404
  def change_column_default(table_name, column_name, default)
279
405
  raise NotImplementedError, "change_column_default is not implemented"
280
406
  end
281
407
 
408
+ # Sets or removes a +NOT NULL+ constraint on a column. The +null+ flag
409
+ # indicates whether the value can be +NULL+. For example
410
+ #
411
+ # change_column_null(:users, :nickname, false)
412
+ #
413
+ # says nicknames cannot be +NULL+ (adds the constraint), whereas
414
+ #
415
+ # change_column_null(:users, :nickname, true)
416
+ #
417
+ # allows them to be +NULL+ (drops the constraint).
418
+ #
419
+ # The method accepts an optional fourth argument to replace existing
420
+ # +NULL+s with some other value. Use that one when enabling the
421
+ # constraint if needed, since otherwise those rows would not be valid.
422
+ #
423
+ # Please note the fourth argument does not set a column's default.
424
+ def change_column_null(table_name, column_name, null, default = nil)
425
+ raise NotImplementedError, "change_column_null is not implemented"
426
+ end
427
+
282
428
  # Renames a column.
283
- # ===== Example
284
- # rename_column(:suppliers, :description, :name)
429
+ #
430
+ # rename_column(:suppliers, :description, :name)
431
+ #
285
432
  def rename_column(table_name, column_name, new_column_name)
286
433
  raise NotImplementedError, "rename_column is not implemented"
287
434
  end
288
435
 
289
- # Adds a new index to the table. +column_name+ can be a single Symbol, or
436
+ # Adds a new index to the table. +column_name+ can be a single Symbol, or
290
437
  # an Array of Symbols.
291
438
  #
292
- # The index will be named after the table and the first column name,
293
- # unless you pass <tt>:name</tt> as an option.
439
+ # The index will be named after the table and the column name(s), unless
440
+ # you pass <tt>:name</tt> as an option.
294
441
  #
295
- # When creating an index on multiple columns, the first column is used as a name
296
- # for the index. For example, when you specify an index on two columns
297
- # [<tt>:first</tt>, <tt>:last</tt>], the DBMS creates an index for both columns as well as an
298
- # index for the first column <tt>:first</tt>. Using just the first name for this index
299
- # makes sense, because you will never have to create a singular index with this
300
- # name.
442
+ # ====== Creating a simple index
301
443
  #
302
- # ===== Examples
444
+ # add_index(:suppliers, :name)
303
445
  #
304
- # ====== Creating a simple index
305
- # add_index(:suppliers, :name)
306
- # generates
307
- # CREATE INDEX suppliers_name_index ON suppliers(name)
446
+ # generates:
447
+ #
448
+ # CREATE INDEX suppliers_name_index ON suppliers(name)
308
449
  #
309
450
  # ====== Creating a unique index
310
- # add_index(:accounts, [:branch_id, :party_id], :unique => true)
311
- # generates
312
- # CREATE UNIQUE INDEX accounts_branch_id_party_id_index ON accounts(branch_id, party_id)
451
+ #
452
+ # add_index(:accounts, [:branch_id, :party_id], unique: true)
453
+ #
454
+ # generates:
455
+ #
456
+ # CREATE UNIQUE INDEX accounts_branch_id_party_id_index ON accounts(branch_id, party_id)
313
457
  #
314
458
  # ====== Creating a named index
315
- # add_index(:accounts, [:branch_id, :party_id], :unique => true, :name => 'by_branch_party')
316
- # generates
459
+ #
460
+ # add_index(:accounts, [:branch_id, :party_id], unique: true, name: 'by_branch_party')
461
+ #
462
+ # generates:
463
+ #
317
464
  # CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id)
318
465
  #
319
466
  # ====== Creating an index with specific key length
320
- # add_index(:accounts, :name, :name => 'by_name', :length => 10)
321
- # generates
322
- # CREATE INDEX by_name ON accounts(name(10))
323
467
  #
324
- # add_index(:accounts, [:name, :surname], :name => 'by_name_surname', :length => {:name => 10, :surname => 15})
325
- # generates
326
- # CREATE INDEX by_name_surname ON accounts(name(10), surname(15))
468
+ # add_index(:accounts, :name, name: 'by_name', length: 10)
469
+ #
470
+ # generates:
471
+ #
472
+ # CREATE INDEX by_name ON accounts(name(10))
473
+ #
474
+ # add_index(:accounts, [:name, :surname], name: 'by_name_surname', length: {name: 10, surname: 15})
475
+ #
476
+ # generates:
477
+ #
478
+ # CREATE INDEX by_name_surname ON accounts(name(10), surname(15))
479
+ #
480
+ # Note: SQLite doesn't support index length.
481
+ #
482
+ # ====== Creating an index with a sort order (desc or asc, asc is the default)
483
+ #
484
+ # add_index(:accounts, [:branch_id, :party_id, :surname], order: {branch_id: :desc, party_id: :asc})
485
+ #
486
+ # generates:
487
+ #
488
+ # CREATE INDEX by_branch_desc_party ON accounts(branch_id DESC, party_id ASC, surname)
489
+ #
490
+ # Note: MySQL doesn't yet support index order (it accepts the syntax but ignores it).
327
491
  #
328
- # Note: SQLite doesn't support index length
492
+ # ====== Creating a partial index
493
+ #
494
+ # add_index(:accounts, [:branch_id, :party_id], unique: true, where: "active")
495
+ #
496
+ # generates:
497
+ #
498
+ # CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
499
+ #
500
+ # ====== Creating an index with a specific method
501
+ #
502
+ # add_index(:developers, :name, using: 'btree')
503
+ #
504
+ # generates:
505
+ #
506
+ # CREATE INDEX index_developers_on_name ON developers USING btree (name) -- PostgreSQL
507
+ # CREATE INDEX index_developers_on_name USING btree ON developers (name) -- MySQL
508
+ #
509
+ # Note: only supported by PostgreSQL and MySQL
510
+ #
511
+ # ====== Creating an index with a specific type
512
+ #
513
+ # add_index(:developers, :name, type: :fulltext)
514
+ #
515
+ # generates:
516
+ #
517
+ # CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
518
+ #
519
+ # Note: only supported by MySQL. Supported: <tt>:fulltext</tt> and <tt>:spatial</tt> on MyISAM tables.
329
520
  def add_index(table_name, column_name, options = {})
330
- options[:name] = options[:name].to_s if options.key?(:name)
331
-
332
- column_names = Array.wrap(column_name)
333
- index_name = index_name(table_name, :column => column_names)
334
-
335
- if Hash === options # legacy support, since this param was a string
336
- index_type = options[:unique] ? "UNIQUE" : ""
337
- index_name = options[:name] || index_name
338
- else
339
- index_type = options
340
- end
341
-
342
- if index_name.length > index_name_length
343
- @logger.warn("Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters. Skipping.")
344
- return
345
- end
346
- if index_name_exists?(table_name, index_name, false)
347
- @logger.warn("Index name '#{index_name}' on table '#{table_name}' already exists. Skipping.")
348
- return
349
- end
350
- quoted_column_names = quoted_columns_for_index(column_names, options).join(", ")
351
-
352
- execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})"
521
+ index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
522
+ execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
353
523
  end
354
524
 
355
- # Remove the given index from the table.
525
+ # Removes the given index from the table.
526
+ #
527
+ # Removes the +index_accounts_on_column+ in the +accounts+ table.
528
+ #
529
+ # remove_index :accounts, :column
530
+ #
531
+ # Removes the index named +index_accounts_on_branch_id+ in the +accounts+ table.
532
+ #
533
+ # remove_index :accounts, column: :branch_id
534
+ #
535
+ # Removes the index named +index_accounts_on_branch_id_and_party_id+ in the +accounts+ table.
536
+ #
537
+ # remove_index :accounts, column: [:branch_id, :party_id]
538
+ #
539
+ # Removes the index named +by_branch_party+ in the +accounts+ table.
540
+ #
541
+ # remove_index :accounts, name: :by_branch_party
356
542
  #
357
- # Remove the suppliers_name_index in the suppliers table.
358
- # remove_index :suppliers, :name
359
- # Remove the index named accounts_branch_id_index in the accounts table.
360
- # remove_index :accounts, :column => :branch_id
361
- # Remove the index named accounts_branch_id_party_id_index in the accounts table.
362
- # remove_index :accounts, :column => [:branch_id, :party_id]
363
- # Remove the index named by_branch_party in the accounts table.
364
- # remove_index :accounts, :name => :by_branch_party
365
543
  def remove_index(table_name, options = {})
366
- index_name = index_name(table_name, options)
367
- unless index_name_exists?(table_name, index_name, true)
368
- @logger.warn("Index name '#{index_name}' on table '#{table_name}' does not exist. Skipping.")
369
- return
370
- end
371
- remove_index!(table_name, index_name)
544
+ remove_index!(table_name, index_name_for_remove(table_name, options))
372
545
  end
373
546
 
374
547
  def remove_index!(table_name, index_name) #:nodoc:
375
548
  execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
376
549
  end
377
550
 
378
- # Rename an index.
551
+ # Renames an index.
552
+ #
553
+ # Rename the +index_people_on_last_name+ index to +index_users_on_last_name+:
379
554
  #
380
- # Rename the index_people_on_last_name index to index_users_on_last_name
381
555
  # rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
556
+ #
382
557
  def rename_index(table_name, old_name, new_name)
383
558
  # this is a naive implementation; some DBs may support this more efficiently (Postgres, for instance)
384
559
  old_index_def = indexes(table_name).detect { |i| i.name == old_name }
@@ -388,9 +563,9 @@ module ActiveRecord
388
563
  end
389
564
 
390
565
  def index_name(table_name, options) #:nodoc:
391
- if Hash === options # legacy support
566
+ if Hash === options
392
567
  if options[:column]
393
- "index_#{table_name}_on_#{Array.wrap(options[:column]) * '_and_'}"
568
+ "index_#{table_name}_on_#{Array(options[:column]) * '_and_'}"
394
569
  elsif options[:name]
395
570
  options[:name]
396
571
  else
@@ -401,57 +576,79 @@ module ActiveRecord
401
576
  end
402
577
  end
403
578
 
404
- # Verify the existence of an index with a given name.
579
+ # Verifies the existence of an index with a given name.
405
580
  #
406
581
  # The default argument is returned if the underlying implementation does not define the indexes method,
407
582
  # as there's no way to determine the correct answer in that case.
408
583
  def index_name_exists?(table_name, index_name, default)
409
584
  return default unless respond_to?(:indexes)
585
+ index_name = index_name.to_s
410
586
  indexes(table_name).detect { |i| i.name == index_name }
411
587
  end
412
588
 
413
- # Returns a string of <tt>CREATE TABLE</tt> SQL statement(s) for recreating the
414
- # entire structure of the database.
415
- def structure_dump
589
+ # Adds a reference. Optionally adds a +type+ column, if <tt>:polymorphic</tt> option is provided.
590
+ # <tt>add_reference</tt> and <tt>add_belongs_to</tt> are acceptable.
591
+ #
592
+ # ====== Create a user_id column
593
+ #
594
+ # add_reference(:products, :user)
595
+ #
596
+ # ====== Create a supplier_id and supplier_type columns
597
+ #
598
+ # add_belongs_to(:products, :supplier, polymorphic: true)
599
+ #
600
+ # ====== Create a supplier_id, supplier_type columns and appropriate index
601
+ #
602
+ # add_reference(:products, :supplier, polymorphic: true, index: true)
603
+ #
604
+ def add_reference(table_name, ref_name, options = {})
605
+ polymorphic = options.delete(:polymorphic)
606
+ index_options = options.delete(:index)
607
+ add_column(table_name, "#{ref_name}_id", :integer, options)
608
+ add_column(table_name, "#{ref_name}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic
609
+ add_index(table_name, polymorphic ? %w[id type].map{ |t| "#{ref_name}_#{t}" } : "#{ref_name}_id", index_options.is_a?(Hash) ? index_options : nil) if index_options
610
+ end
611
+ alias :add_belongs_to :add_reference
612
+
613
+ # Removes the reference(s). Also removes a +type+ column if one exists.
614
+ # <tt>remove_reference</tt>, <tt>remove_references</tt> and <tt>remove_belongs_to</tt> are acceptable.
615
+ #
616
+ # ====== Remove the reference
617
+ #
618
+ # remove_reference(:products, :user, index: true)
619
+ #
620
+ # ====== Remove polymorphic reference
621
+ #
622
+ # remove_reference(:products, :supplier, polymorphic: true)
623
+ #
624
+ def remove_reference(table_name, ref_name, options = {})
625
+ remove_column(table_name, "#{ref_name}_id")
626
+ remove_column(table_name, "#{ref_name}_type") if options[:polymorphic]
416
627
  end
628
+ alias :remove_belongs_to :remove_reference
417
629
 
418
630
  def dump_schema_information #:nodoc:
419
631
  sm_table = ActiveRecord::Migrator.schema_migrations_table_name
420
- migrated = select_values("SELECT version FROM #{sm_table}")
421
- migrated.map { |v| "INSERT INTO #{sm_table} (version) VALUES ('#{v}');" }.join("\n\n")
632
+
633
+ ActiveRecord::SchemaMigration.order('version').map { |sm|
634
+ "INSERT INTO #{sm_table} (version) VALUES ('#{sm.version}');"
635
+ }.join "\n\n"
422
636
  end
423
637
 
424
638
  # Should not be called normally, but this operation is non-destructive.
425
639
  # The migrations module handles this automatically.
426
640
  def initialize_schema_migrations_table
427
- sm_table = ActiveRecord::Migrator.schema_migrations_table_name
428
-
429
- unless table_exists?(sm_table)
430
- create_table(sm_table, :id => false) do |schema_migrations_table|
431
- schema_migrations_table.column :version, :string, :null => false
432
- end
433
- add_index sm_table, :version, :unique => true,
434
- :name => "#{Base.table_name_prefix}unique_schema_migrations#{Base.table_name_suffix}"
435
-
436
- # Backwards-compatibility: if we find schema_info, assume we've
437
- # migrated up to that point:
438
- si_table = Base.table_name_prefix + 'schema_info' + Base.table_name_suffix
439
-
440
- if table_exists?(si_table)
441
-
442
- old_version = select_value("SELECT version FROM #{quote_table_name(si_table)}").to_i
443
- assume_migrated_upto_version(old_version)
444
- drop_table(si_table)
445
- end
446
- end
641
+ ActiveRecord::SchemaMigration.create_table
447
642
  end
448
643
 
449
- def assume_migrated_upto_version(version, migrations_path = ActiveRecord::Migrator.migrations_path)
644
+ def assume_migrated_upto_version(version, migrations_paths = ActiveRecord::Migrator.migrations_paths)
645
+ migrations_paths = Array(migrations_paths)
450
646
  version = version.to_i
451
647
  sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
452
648
 
453
649
  migrated = select_values("SELECT version FROM #{sm_table}").map { |v| v.to_i }
454
- versions = Dir["#{migrations_path}/[0-9]*_*.rb"].map do |filename|
650
+ paths = migrations_paths.map {|p| "#{p}/[0-9]*_*.rb" }
651
+ versions = Dir[*paths].map do |filename|
455
652
  filename.split('/').last.split('_').first.to_i
456
653
  end
457
654
 
@@ -471,7 +668,7 @@ module ActiveRecord
471
668
  end
472
669
 
473
670
  def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
474
- if native = native_database_types[type]
671
+ if native = native_database_types[type.to_sym]
475
672
  column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
476
673
 
477
674
  if type == :decimal # ignore limit, use precision and scale
@@ -484,7 +681,7 @@ module ActiveRecord
484
681
  column_type_sql << "(#{precision})"
485
682
  end
486
683
  elsif scale
487
- raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale if specified"
684
+ raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
488
685
  end
489
686
 
490
687
  elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
@@ -503,41 +700,174 @@ module ActiveRecord
503
700
  if options[:null] == false
504
701
  sql << " NOT NULL"
505
702
  end
703
+ if options[:auto_increment] == true
704
+ sql << " AUTO_INCREMENT"
705
+ end
506
706
  end
507
707
 
508
708
  # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
509
709
  # Both PostgreSQL and Oracle overrides this for custom DISTINCT syntax.
510
710
  #
511
711
  # distinct("posts.id", "posts.created_at desc")
712
+ #
512
713
  def distinct(columns, order_by)
513
714
  "DISTINCT #{columns}"
514
715
  end
515
716
 
516
- # Adds timestamps (created_at and updated_at) columns to the named table.
517
- # ===== Examples
518
- # add_timestamps(:suppliers)
717
+ # Adds timestamps (+created_at+ and +updated_at+) columns to the named table.
718
+ #
719
+ # add_timestamps(:suppliers)
720
+ #
519
721
  def add_timestamps(table_name)
520
722
  add_column table_name, :created_at, :datetime
521
723
  add_column table_name, :updated_at, :datetime
522
724
  end
523
725
 
524
- # Removes the timestamp columns (created_at and updated_at) from the table definition.
525
- # ===== Examples
726
+ # Removes the timestamp columns (+created_at+ and +updated_at+) from the table definition.
727
+ #
526
728
  # remove_timestamps(:suppliers)
729
+ #
527
730
  def remove_timestamps(table_name)
528
731
  remove_column table_name, :updated_at
529
732
  remove_column table_name, :created_at
530
733
  end
531
734
 
532
735
  protected
736
+ def add_index_sort_order(option_strings, column_names, options = {})
737
+ if options.is_a?(Hash) && order = options[:order]
738
+ case order
739
+ when Hash
740
+ column_names.each {|name| option_strings[name] += " #{order[name].upcase}" if order.has_key?(name)}
741
+ when String
742
+ column_names.each {|name| option_strings[name] += " #{order.upcase}"}
743
+ end
744
+ end
745
+
746
+ return option_strings
747
+ end
748
+
533
749
  # Overridden by the mysql adapter for supporting index lengths
534
750
  def quoted_columns_for_index(column_names, options = {})
535
- column_names.map {|name| quote_column_name(name) }
751
+ option_strings = Hash[column_names.map {|name| [name, '']}]
752
+
753
+ # add index sort order if supported
754
+ if supports_index_sort_order?
755
+ option_strings = add_index_sort_order(option_strings, column_names, options)
756
+ end
757
+
758
+ column_names.map {|name| quote_column_name(name) + option_strings[name]}
536
759
  end
537
760
 
538
761
  def options_include_default?(options)
539
762
  options.include?(:default) && !(options[:null] == false && options[:default].nil?)
540
763
  end
764
+
765
+ def add_index_options(table_name, column_name, options = {})
766
+ column_names = Array(column_name)
767
+ index_name = index_name(table_name, column: column_names)
768
+
769
+ if Hash === options # legacy support, since this param was a string
770
+ options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type)
771
+
772
+ index_type = options[:unique] ? "UNIQUE" : ""
773
+ index_type = options[:type].to_s if options.key?(:type)
774
+ index_name = options[:name].to_s if options.key?(:name)
775
+ max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
776
+
777
+ if options.key?(:algorithm)
778
+ algorithm = index_algorithms.fetch(options[:algorithm]) {
779
+ raise ArgumentError.new("Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}")
780
+ }
781
+ end
782
+
783
+ using = "USING #{options[:using]}" if options[:using].present?
784
+
785
+ if supports_partial_index?
786
+ index_options = options[:where] ? " WHERE #{options[:where]}" : ""
787
+ end
788
+ else
789
+ if options
790
+ message = "Passing a string as third argument of `add_index` is deprecated and will" +
791
+ " be removed in Rails 4.1." +
792
+ " Use add_index(#{table_name.inspect}, #{column_name.inspect}, unique: true) instead"
793
+
794
+ ActiveSupport::Deprecation.warn message
795
+ end
796
+
797
+ index_type = options
798
+ max_index_length = allowed_index_name_length
799
+ algorithm = using = nil
800
+ end
801
+
802
+ if index_name.length > max_index_length
803
+ raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{max_index_length} characters"
804
+ end
805
+ if index_name_exists?(table_name, index_name, false)
806
+ raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
807
+ end
808
+ index_columns = quoted_columns_for_index(column_names, options).join(", ")
809
+
810
+ [index_name, index_type, index_columns, index_options, algorithm, using]
811
+ end
812
+
813
+ def index_name_for_remove(table_name, options = {})
814
+ index_name = index_name(table_name, options)
815
+
816
+ unless index_name_exists?(table_name, index_name, true)
817
+ if options.is_a?(Hash) && options.has_key?(:name)
818
+ options_without_column = options.dup
819
+ options_without_column.delete :column
820
+ index_name_without_column = index_name(table_name, options_without_column)
821
+
822
+ return index_name_without_column if index_name_exists?(table_name, index_name_without_column, false)
823
+ end
824
+
825
+ raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
826
+ end
827
+
828
+ index_name
829
+ end
830
+
831
+ def columns_for_remove(table_name, *column_names)
832
+ ActiveSupport::Deprecation.warn("columns_for_remove is deprecated and will be removed in the future")
833
+ raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)") if column_names.blank?
834
+ column_names.map {|column_name| quote_column_name(column_name) }
835
+ end
836
+
837
+ def rename_table_indexes(table_name, new_name)
838
+ indexes(new_name).each do |index|
839
+ generated_index_name = index_name(table_name, column: index.columns)
840
+ if generated_index_name == index.name
841
+ rename_index new_name, generated_index_name, index_name(new_name, column: index.columns)
842
+ end
843
+ end
844
+ end
845
+
846
+ def rename_column_indexes(table_name, column_name, new_column_name)
847
+ column_name, new_column_name = column_name.to_s, new_column_name.to_s
848
+ indexes(table_name).each do |index|
849
+ next unless index.columns.include?(new_column_name)
850
+ old_columns = index.columns.dup
851
+ old_columns[old_columns.index(new_column_name)] = column_name
852
+ generated_index_name = index_name(table_name, column: old_columns)
853
+ if generated_index_name == index.name
854
+ rename_index table_name, generated_index_name, index_name(table_name, column: index.columns)
855
+ end
856
+ end
857
+ end
858
+
859
+ private
860
+ def create_table_definition(name, temporary, options)
861
+ TableDefinition.new native_database_types, name, temporary, options
862
+ end
863
+
864
+ def create_alter_table(name)
865
+ AlterTable.new create_table_definition(name, false, {})
866
+ end
867
+
868
+ def update_table_definition(table_name, base)
869
+ Table.new(table_name, base)
870
+ end
541
871
  end
542
872
  end
543
873
  end