activerecord 1.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 (255) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +2102 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +213 -0
  5. data/examples/performance.rb +172 -0
  6. data/examples/simple.rb +14 -0
  7. data/lib/active_record/aggregations.rb +180 -84
  8. data/lib/active_record/associations/alias_tracker.rb +76 -0
  9. data/lib/active_record/associations/association.rb +248 -0
  10. data/lib/active_record/associations/association_scope.rb +135 -0
  11. data/lib/active_record/associations/belongs_to_association.rb +92 -0
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +35 -0
  13. data/lib/active_record/associations/builder/association.rb +108 -0
  14. data/lib/active_record/associations/builder/belongs_to.rb +98 -0
  15. data/lib/active_record/associations/builder/collection_association.rb +89 -0
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +39 -0
  17. data/lib/active_record/associations/builder/has_many.rb +15 -0
  18. data/lib/active_record/associations/builder/has_one.rb +25 -0
  19. data/lib/active_record/associations/builder/singular_association.rb +32 -0
  20. data/lib/active_record/associations/collection_association.rb +608 -0
  21. data/lib/active_record/associations/collection_proxy.rb +986 -0
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +58 -39
  23. data/lib/active_record/associations/has_many_association.rb +116 -85
  24. data/lib/active_record/associations/has_many_through_association.rb +197 -0
  25. data/lib/active_record/associations/has_one_association.rb +102 -0
  26. data/lib/active_record/associations/has_one_through_association.rb +36 -0
  27. data/lib/active_record/associations/join_dependency/join_association.rb +174 -0
  28. data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
  29. data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
  30. data/lib/active_record/associations/join_dependency.rb +235 -0
  31. data/lib/active_record/associations/join_helper.rb +45 -0
  32. data/lib/active_record/associations/preloader/association.rb +121 -0
  33. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  34. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  35. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
  36. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  37. data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
  38. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  39. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  40. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  41. data/lib/active_record/associations/preloader/through_association.rb +63 -0
  42. data/lib/active_record/associations/preloader.rb +178 -0
  43. data/lib/active_record/associations/singular_association.rb +64 -0
  44. data/lib/active_record/associations/through_association.rb +87 -0
  45. data/lib/active_record/associations.rb +1437 -431
  46. data/lib/active_record/attribute_assignment.rb +201 -0
  47. data/lib/active_record/attribute_methods/before_type_cast.rb +70 -0
  48. data/lib/active_record/attribute_methods/dirty.rb +118 -0
  49. data/lib/active_record/attribute_methods/primary_key.rb +122 -0
  50. data/lib/active_record/attribute_methods/query.rb +40 -0
  51. data/lib/active_record/attribute_methods/read.rb +107 -0
  52. data/lib/active_record/attribute_methods/serialization.rb +162 -0
  53. data/lib/active_record/attribute_methods/time_zone_conversion.rb +59 -0
  54. data/lib/active_record/attribute_methods/write.rb +63 -0
  55. data/lib/active_record/attribute_methods.rb +393 -0
  56. data/lib/active_record/autosave_association.rb +426 -0
  57. data/lib/active_record/base.rb +268 -930
  58. data/lib/active_record/callbacks.rb +203 -230
  59. data/lib/active_record/coders/yaml_column.rb +38 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +638 -0
  61. data/lib/active_record/connection_adapters/abstract/database_limits.rb +67 -0
  62. data/lib/active_record/connection_adapters/abstract/database_statements.rb +390 -0
  63. data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -0
  64. data/lib/active_record/connection_adapters/abstract/quoting.rb +129 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +501 -0
  66. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
  67. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +873 -0
  68. data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
  69. data/lib/active_record/connection_adapters/abstract_adapter.rb +389 -275
  70. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +782 -0
  71. data/lib/active_record/connection_adapters/column.rb +318 -0
  72. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  73. data/lib/active_record/connection_adapters/mysql2_adapter.rb +273 -0
  74. data/lib/active_record/connection_adapters/mysql_adapter.rb +517 -90
  75. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
  76. data/lib/active_record/connection_adapters/postgresql/cast.rb +152 -0
  77. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid.rb +366 -0
  79. data/lib/active_record/connection_adapters/postgresql/quoting.rb +171 -0
  80. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  81. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +489 -0
  82. data/lib/active_record/connection_adapters/postgresql_adapter.rb +911 -138
  83. data/lib/active_record/connection_adapters/schema_cache.rb +129 -0
  84. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +624 -0
  85. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  86. data/lib/active_record/connection_handling.rb +98 -0
  87. data/lib/active_record/core.rb +463 -0
  88. data/lib/active_record/counter_cache.rb +122 -0
  89. data/lib/active_record/dynamic_matchers.rb +131 -0
  90. data/lib/active_record/errors.rb +213 -0
  91. data/lib/active_record/explain.rb +38 -0
  92. data/lib/active_record/explain_registry.rb +30 -0
  93. data/lib/active_record/explain_subscriber.rb +29 -0
  94. data/lib/active_record/fixture_set/file.rb +55 -0
  95. data/lib/active_record/fixtures.rb +892 -138
  96. data/lib/active_record/inheritance.rb +200 -0
  97. data/lib/active_record/integration.rb +60 -0
  98. data/lib/active_record/locale/en.yml +47 -0
  99. data/lib/active_record/locking/optimistic.rb +181 -0
  100. data/lib/active_record/locking/pessimistic.rb +77 -0
  101. data/lib/active_record/log_subscriber.rb +82 -0
  102. data/lib/active_record/migration/command_recorder.rb +164 -0
  103. data/lib/active_record/migration/join_table.rb +15 -0
  104. data/lib/active_record/migration.rb +1015 -0
  105. data/lib/active_record/model_schema.rb +345 -0
  106. data/lib/active_record/nested_attributes.rb +546 -0
  107. data/lib/active_record/null_relation.rb +65 -0
  108. data/lib/active_record/persistence.rb +509 -0
  109. data/lib/active_record/query_cache.rb +56 -0
  110. data/lib/active_record/querying.rb +62 -0
  111. data/lib/active_record/railtie.rb +205 -0
  112. data/lib/active_record/railties/console_sandbox.rb +5 -0
  113. data/lib/active_record/railties/controller_runtime.rb +50 -0
  114. data/lib/active_record/railties/databases.rake +402 -0
  115. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  116. data/lib/active_record/readonly_attributes.rb +30 -0
  117. data/lib/active_record/reflection.rb +544 -87
  118. data/lib/active_record/relation/batches.rb +93 -0
  119. data/lib/active_record/relation/calculations.rb +399 -0
  120. data/lib/active_record/relation/delegation.rb +125 -0
  121. data/lib/active_record/relation/finder_methods.rb +349 -0
  122. data/lib/active_record/relation/merger.rb +161 -0
  123. data/lib/active_record/relation/predicate_builder.rb +106 -0
  124. data/lib/active_record/relation/query_methods.rb +1044 -0
  125. data/lib/active_record/relation/spawn_methods.rb +73 -0
  126. data/lib/active_record/relation.rb +655 -0
  127. data/lib/active_record/result.rb +67 -0
  128. data/lib/active_record/runtime_registry.rb +17 -0
  129. data/lib/active_record/sanitization.rb +168 -0
  130. data/lib/active_record/schema.rb +65 -0
  131. data/lib/active_record/schema_dumper.rb +204 -0
  132. data/lib/active_record/schema_migration.rb +39 -0
  133. data/lib/active_record/scoping/default.rb +146 -0
  134. data/lib/active_record/scoping/named.rb +175 -0
  135. data/lib/active_record/scoping.rb +82 -0
  136. data/lib/active_record/serialization.rb +22 -0
  137. data/lib/active_record/serializers/xml_serializer.rb +197 -0
  138. data/lib/active_record/statement_cache.rb +26 -0
  139. data/lib/active_record/store.rb +156 -0
  140. data/lib/active_record/tasks/database_tasks.rb +203 -0
  141. data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
  142. data/lib/active_record/tasks/mysql_database_tasks.rb +143 -0
  143. data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
  144. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  145. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  146. data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
  147. data/lib/active_record/test_case.rb +96 -0
  148. data/lib/active_record/timestamp.rb +119 -0
  149. data/lib/active_record/transactions.rb +366 -69
  150. data/lib/active_record/translation.rb +22 -0
  151. data/lib/active_record/validations/associated.rb +49 -0
  152. data/lib/active_record/validations/presence.rb +65 -0
  153. data/lib/active_record/validations/uniqueness.rb +225 -0
  154. data/lib/active_record/validations.rb +64 -185
  155. data/lib/active_record/version.rb +11 -0
  156. data/lib/active_record.rb +149 -24
  157. data/lib/rails/generators/active_record/migration/migration_generator.rb +62 -0
  158. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
  159. data/lib/rails/generators/active_record/migration/templates/migration.rb +39 -0
  160. data/lib/rails/generators/active_record/model/model_generator.rb +48 -0
  161. data/lib/rails/generators/active_record/model/templates/model.rb +10 -0
  162. data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
  163. data/lib/rails/generators/active_record.rb +23 -0
  164. metadata +261 -161
  165. data/CHANGELOG +0 -581
  166. data/README +0 -361
  167. data/RUNNING_UNIT_TESTS +0 -36
  168. data/dev-utils/eval_debugger.rb +0 -9
  169. data/examples/associations.png +0 -0
  170. data/examples/associations.rb +0 -87
  171. data/examples/shared_setup.rb +0 -15
  172. data/examples/validation.rb +0 -88
  173. data/install.rb +0 -60
  174. data/lib/active_record/associations/association_collection.rb +0 -70
  175. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -107
  176. data/lib/active_record/deprecated_associations.rb +0 -70
  177. data/lib/active_record/observer.rb +0 -71
  178. data/lib/active_record/support/class_attribute_accessors.rb +0 -43
  179. data/lib/active_record/support/class_inheritable_attributes.rb +0 -37
  180. data/lib/active_record/support/clean_logger.rb +0 -10
  181. data/lib/active_record/support/inflector.rb +0 -70
  182. data/lib/active_record/vendor/mysql.rb +0 -1117
  183. data/lib/active_record/vendor/simple.rb +0 -702
  184. data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
  185. data/lib/active_record/wrappings.rb +0 -59
  186. data/rakefile +0 -122
  187. data/test/abstract_unit.rb +0 -16
  188. data/test/aggregations_test.rb +0 -34
  189. data/test/all.sh +0 -8
  190. data/test/associations_test.rb +0 -477
  191. data/test/base_test.rb +0 -513
  192. data/test/class_inheritable_attributes_test.rb +0 -33
  193. data/test/connections/native_mysql/connection.rb +0 -24
  194. data/test/connections/native_postgresql/connection.rb +0 -24
  195. data/test/connections/native_sqlite/connection.rb +0 -24
  196. data/test/deprecated_associations_test.rb +0 -336
  197. data/test/finder_test.rb +0 -67
  198. data/test/fixtures/accounts/signals37 +0 -3
  199. data/test/fixtures/accounts/unknown +0 -2
  200. data/test/fixtures/auto_id.rb +0 -4
  201. data/test/fixtures/column_name.rb +0 -3
  202. data/test/fixtures/companies/first_client +0 -6
  203. data/test/fixtures/companies/first_firm +0 -4
  204. data/test/fixtures/companies/second_client +0 -6
  205. data/test/fixtures/company.rb +0 -37
  206. data/test/fixtures/company_in_module.rb +0 -33
  207. data/test/fixtures/course.rb +0 -3
  208. data/test/fixtures/courses/java +0 -2
  209. data/test/fixtures/courses/ruby +0 -2
  210. data/test/fixtures/customer.rb +0 -30
  211. data/test/fixtures/customers/david +0 -6
  212. data/test/fixtures/db_definitions/mysql.sql +0 -96
  213. data/test/fixtures/db_definitions/mysql2.sql +0 -4
  214. data/test/fixtures/db_definitions/postgresql.sql +0 -113
  215. data/test/fixtures/db_definitions/postgresql2.sql +0 -4
  216. data/test/fixtures/db_definitions/sqlite.sql +0 -85
  217. data/test/fixtures/db_definitions/sqlite2.sql +0 -4
  218. data/test/fixtures/default.rb +0 -2
  219. data/test/fixtures/developer.rb +0 -8
  220. data/test/fixtures/developers/david +0 -2
  221. data/test/fixtures/developers/jamis +0 -2
  222. data/test/fixtures/developers_projects/david_action_controller +0 -2
  223. data/test/fixtures/developers_projects/david_active_record +0 -2
  224. data/test/fixtures/developers_projects/jamis_active_record +0 -2
  225. data/test/fixtures/entrant.rb +0 -3
  226. data/test/fixtures/entrants/first +0 -3
  227. data/test/fixtures/entrants/second +0 -3
  228. data/test/fixtures/entrants/third +0 -3
  229. data/test/fixtures/fixture_database.sqlite +0 -0
  230. data/test/fixtures/fixture_database_2.sqlite +0 -0
  231. data/test/fixtures/movie.rb +0 -5
  232. data/test/fixtures/movies/first +0 -2
  233. data/test/fixtures/movies/second +0 -2
  234. data/test/fixtures/project.rb +0 -3
  235. data/test/fixtures/projects/action_controller +0 -2
  236. data/test/fixtures/projects/active_record +0 -2
  237. data/test/fixtures/reply.rb +0 -21
  238. data/test/fixtures/subscriber.rb +0 -5
  239. data/test/fixtures/subscribers/first +0 -2
  240. data/test/fixtures/subscribers/second +0 -2
  241. data/test/fixtures/topic.rb +0 -20
  242. data/test/fixtures/topics/first +0 -9
  243. data/test/fixtures/topics/second +0 -8
  244. data/test/fixtures_test.rb +0 -20
  245. data/test/inflector_test.rb +0 -104
  246. data/test/inheritance_test.rb +0 -125
  247. data/test/lifecycle_test.rb +0 -110
  248. data/test/modules_test.rb +0 -21
  249. data/test/multiple_db_test.rb +0 -46
  250. data/test/pk_test.rb +0 -57
  251. data/test/reflection_test.rb +0 -78
  252. data/test/thread_safety_test.rb +0 -33
  253. data/test/transactions_test.rb +0 -83
  254. data/test/unconnected_test.rb +0 -24
  255. data/test/validations_test.rb +0 -126
@@ -0,0 +1,873 @@
1
+ require 'active_record/migration/join_table'
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters # :nodoc:
5
+ module SchemaStatements
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
10
+ # abstract data types.
11
+ def native_database_types
12
+ {}
13
+ end
14
+
15
+ # Truncates a table alias according to the limits of the current adapter.
16
+ def table_alias_for(table_name)
17
+ table_name[0...table_alias_length].tr('.', '_')
18
+ end
19
+
20
+ # Checks to see if the table +table_name+ exists on the database.
21
+ #
22
+ # table_exists?(:developers)
23
+ #
24
+ def table_exists?(table_name)
25
+ tables.include?(table_name.to_s)
26
+ end
27
+
28
+ # Returns an array of indexes for the given table.
29
+ # def indexes(table_name, name = nil) end
30
+
31
+ # Checks to see if an index exists on a table for a given index definition.
32
+ #
33
+ # # Check an index exists
34
+ # index_exists?(:suppliers, :company_id)
35
+ #
36
+ # # Check an index on multiple columns exists
37
+ # index_exists?(:suppliers, [:company_id, :company_type])
38
+ #
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"
44
+ #
45
+ def index_exists?(table_name, column_name, options = {})
46
+ column_names = Array(column_name)
47
+ index_name = options.key?(:name) ? options[:name].to_s : index_name(table_name, :column => column_names)
48
+ if options[:unique]
49
+ indexes(table_name).any?{ |i| i.unique && i.name == index_name }
50
+ else
51
+ indexes(table_name).any?{ |i| i.name == index_name }
52
+ end
53
+ end
54
+
55
+ # Returns an array of Column objects for the table specified by +table_name+.
56
+ # See the concrete implementation for details on the expected parameter values.
57
+ def columns(table_name) end
58
+
59
+ # Checks to see if a column exists in a given table.
60
+ #
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)
66
+ #
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)
72
+ #
73
+ def column_exists?(table_name, column_name, type = nil, options = {})
74
+ columns(table_name).any?{ |c| c.name == column_name.to_s &&
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]) }
81
+ end
82
+
83
+ # Creates a new table with the name +table_name+. +table_name+ may either
84
+ # be a String or a Symbol.
85
+ #
86
+ # There are two ways to work with +create_table+. You can use the block
87
+ # form or the regular form, like this:
88
+ #
89
+ # === Block form
90
+ #
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
99
+ #
100
+ # === Block form, with shorthand
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
107
+ #
108
+ # === Regular form
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})
114
+ #
115
+ # The +options+ hash can include the following keys:
116
+ # [<tt>:id</tt>]
117
+ # Whether to automatically add a primary key column. Defaults to true.
118
+ # Join tables for +has_and_belongs_to_many+ should set it to false.
119
+ # [<tt>:primary_key</tt>]
120
+ # The name of the primary key, if one is to be added automatically.
121
+ # Defaults to +id+. If <tt>:id</tt> is false this option is ignored.
122
+ #
123
+ # Also note that this just sets the primary key in the table. You additionally
124
+ # need to configure the primary key in the model via +self.primary_key=+.
125
+ # Models do NOT auto-detect the primary key from their table definition.
126
+ #
127
+ # [<tt>:options</tt>]
128
+ # Any extra options you want appended to the table definition.
129
+ # [<tt>:temporary</tt>]
130
+ # Make a temporary table.
131
+ # [<tt>:force</tt>]
132
+ # Set to true to drop the table before creating it.
133
+ # Defaults to false.
134
+ #
135
+ # ====== Add a backend specific option to the generated SQL (MySQL)
136
+ #
137
+ # create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
138
+ #
139
+ # generates:
140
+ #
141
+ # CREATE TABLE suppliers (
142
+ # id int(11) DEFAULT NULL auto_increment PRIMARY KEY
143
+ # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
144
+ #
145
+ # ====== Rename the primary key column
146
+ #
147
+ # create_table(:objects, primary_key: 'guid') do |t|
148
+ # t.column :name, :string, limit: 80
149
+ # end
150
+ #
151
+ # generates:
152
+ #
153
+ # CREATE TABLE objects (
154
+ # guid int(11) DEFAULT NULL auto_increment PRIMARY KEY,
155
+ # name varchar(80)
156
+ # )
157
+ #
158
+ # ====== Do not add a primary key column
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
+ #
165
+ # generates:
166
+ #
167
+ # CREATE TABLE categories_suppliers (
168
+ # category_id int,
169
+ # supplier_id int
170
+ # )
171
+ #
172
+ # See also TableDefinition#column for details on how to create columns.
173
+ def create_table(table_name, options = {})
174
+ td = create_table_definition table_name, options[:temporary], options[:options]
175
+
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?
185
+
186
+ if options[:force] && table_exists?(table_name)
187
+ drop_table(table_name, options)
188
+ end
189
+
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)
256
+ end
257
+
258
+ # A block for changing columns in +table+.
259
+ #
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.
273
+ #
274
+ # ====== Add a column
275
+ #
276
+ # change_table(:suppliers) do |t|
277
+ # t.column :name, :string, limit: 60
278
+ # end
279
+ #
280
+ # ====== Add 2 integer columns
281
+ #
282
+ # change_table(:suppliers) do |t|
283
+ # t.integer :width, :height, null: false, default: 0
284
+ # end
285
+ #
286
+ # ====== Add created_at/updated_at columns
287
+ #
288
+ # change_table(:suppliers) do |t|
289
+ # t.timestamps
290
+ # end
291
+ #
292
+ # ====== Add a foreign key column
293
+ #
294
+ # change_table(:suppliers) do |t|
295
+ # t.references :company
296
+ # end
297
+ #
298
+ # Creates a <tt>company_id(integer)</tt> column.
299
+ #
300
+ # ====== Add a polymorphic foreign key column
301
+ #
302
+ # change_table(:suppliers) do |t|
303
+ # t.belongs_to :company, polymorphic: true
304
+ # end
305
+ #
306
+ # Creates <tt>company_type(varchar)</tt> and <tt>company_id(integer)</tt> columns.
307
+ #
308
+ # ====== Remove a column
309
+ #
310
+ # change_table(:suppliers) do |t|
311
+ # t.remove :company
312
+ # end
313
+ #
314
+ # ====== Remove several columns
315
+ #
316
+ # change_table(:suppliers) do |t|
317
+ # t.remove :company_id
318
+ # t.remove :width, :height
319
+ # end
320
+ #
321
+ # ====== Remove an index
322
+ #
323
+ # change_table(:suppliers) do |t|
324
+ # t.remove_index :company_id
325
+ # end
326
+ #
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
336
+ end
337
+
338
+ # Renames a table.
339
+ #
340
+ # rename_table('octopuses', 'octopi')
341
+ #
342
+ def rename_table(table_name, new_name)
343
+ raise NotImplementedError, "rename_table is not implemented"
344
+ end
345
+
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.
351
+ def drop_table(table_name, options = {})
352
+ execute "DROP TABLE #{quote_table_name(table_name)}"
353
+ end
354
+
355
+ # Adds a new column to the named table.
356
+ # See TableDefinition#column for details of the options you can use.
357
+ def add_column(table_name, column_name, type, options = {})
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)
371
+ end
372
+ end
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
384
+
385
+ # Changes the column's definition according to the new options.
386
+ # See TableDefinition#column for details of the options you can use.
387
+ #
388
+ # change_column(:suppliers, :name, :string, limit: 80)
389
+ # change_column(:accounts, :description, :text)
390
+ #
391
+ def change_column(table_name, column_name, type, options = {})
392
+ raise NotImplementedError, "change_column is not implemented"
393
+ end
394
+
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
+ #
404
+ def change_column_default(table_name, column_name, default)
405
+ raise NotImplementedError, "change_column_default is not implemented"
406
+ end
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
+
428
+ # Renames a column.
429
+ #
430
+ # rename_column(:suppliers, :description, :name)
431
+ #
432
+ def rename_column(table_name, column_name, new_column_name)
433
+ raise NotImplementedError, "rename_column is not implemented"
434
+ end
435
+
436
+ # Adds a new index to the table. +column_name+ can be a single Symbol, or
437
+ # an Array of Symbols.
438
+ #
439
+ # The index will be named after the table and the column name(s), unless
440
+ # you pass <tt>:name</tt> as an option.
441
+ #
442
+ # ====== Creating a simple index
443
+ #
444
+ # add_index(:suppliers, :name)
445
+ #
446
+ # generates:
447
+ #
448
+ # CREATE INDEX suppliers_name_index ON suppliers(name)
449
+ #
450
+ # ====== Creating a unique index
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)
457
+ #
458
+ # ====== Creating a named index
459
+ #
460
+ # add_index(:accounts, [:branch_id, :party_id], unique: true, name: 'by_branch_party')
461
+ #
462
+ # generates:
463
+ #
464
+ # CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id)
465
+ #
466
+ # ====== Creating an index with specific key length
467
+ #
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).
491
+ #
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.
520
+ def add_index(table_name, column_name, options = {})
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}"
523
+ end
524
+
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
542
+ #
543
+ def remove_index(table_name, options = {})
544
+ remove_index!(table_name, index_name_for_remove(table_name, options))
545
+ end
546
+
547
+ def remove_index!(table_name, index_name) #:nodoc:
548
+ execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
549
+ end
550
+
551
+ # Renames an index.
552
+ #
553
+ # Rename the +index_people_on_last_name+ index to +index_users_on_last_name+:
554
+ #
555
+ # rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
556
+ #
557
+ def rename_index(table_name, old_name, new_name)
558
+ # this is a naive implementation; some DBs may support this more efficiently (Postgres, for instance)
559
+ old_index_def = indexes(table_name).detect { |i| i.name == old_name }
560
+ return unless old_index_def
561
+ remove_index(table_name, :name => old_name)
562
+ add_index(table_name, old_index_def.columns, :name => new_name, :unique => old_index_def.unique)
563
+ end
564
+
565
+ def index_name(table_name, options) #:nodoc:
566
+ if Hash === options
567
+ if options[:column]
568
+ "index_#{table_name}_on_#{Array(options[:column]) * '_and_'}"
569
+ elsif options[:name]
570
+ options[:name]
571
+ else
572
+ raise ArgumentError, "You must specify the index name"
573
+ end
574
+ else
575
+ index_name(table_name, :column => options)
576
+ end
577
+ end
578
+
579
+ # Verifies the existence of an index with a given name.
580
+ #
581
+ # The default argument is returned if the underlying implementation does not define the indexes method,
582
+ # as there's no way to determine the correct answer in that case.
583
+ def index_name_exists?(table_name, index_name, default)
584
+ return default unless respond_to?(:indexes)
585
+ index_name = index_name.to_s
586
+ indexes(table_name).detect { |i| i.name == index_name }
587
+ end
588
+
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]
627
+ end
628
+ alias :remove_belongs_to :remove_reference
629
+
630
+ def dump_schema_information #:nodoc:
631
+ sm_table = ActiveRecord::Migrator.schema_migrations_table_name
632
+
633
+ ActiveRecord::SchemaMigration.order('version').map { |sm|
634
+ "INSERT INTO #{sm_table} (version) VALUES ('#{sm.version}');"
635
+ }.join "\n\n"
636
+ end
637
+
638
+ # Should not be called normally, but this operation is non-destructive.
639
+ # The migrations module handles this automatically.
640
+ def initialize_schema_migrations_table
641
+ ActiveRecord::SchemaMigration.create_table
642
+ end
643
+
644
+ def assume_migrated_upto_version(version, migrations_paths = ActiveRecord::Migrator.migrations_paths)
645
+ migrations_paths = Array(migrations_paths)
646
+ version = version.to_i
647
+ sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
648
+
649
+ migrated = select_values("SELECT version FROM #{sm_table}").map { |v| v.to_i }
650
+ paths = migrations_paths.map {|p| "#{p}/[0-9]*_*.rb" }
651
+ versions = Dir[*paths].map do |filename|
652
+ filename.split('/').last.split('_').first.to_i
653
+ end
654
+
655
+ unless migrated.include?(version)
656
+ execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')"
657
+ end
658
+
659
+ inserted = Set.new
660
+ (versions - migrated).each do |v|
661
+ if inserted.include?(v)
662
+ raise "Duplicate migration #{v}. Please renumber your migrations to resolve the conflict."
663
+ elsif v < version
664
+ execute "INSERT INTO #{sm_table} (version) VALUES ('#{v}')"
665
+ inserted << v
666
+ end
667
+ end
668
+ end
669
+
670
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
671
+ if native = native_database_types[type.to_sym]
672
+ column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
673
+
674
+ if type == :decimal # ignore limit, use precision and scale
675
+ scale ||= native[:scale]
676
+
677
+ if precision ||= native[:precision]
678
+ if scale
679
+ column_type_sql << "(#{precision},#{scale})"
680
+ else
681
+ column_type_sql << "(#{precision})"
682
+ end
683
+ elsif scale
684
+ raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
685
+ end
686
+
687
+ elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
688
+ column_type_sql << "(#{limit})"
689
+ end
690
+
691
+ column_type_sql
692
+ else
693
+ type
694
+ end
695
+ end
696
+
697
+ def add_column_options!(sql, options) #:nodoc:
698
+ sql << " DEFAULT #{quote(options[:default], options[:column])}" if options_include_default?(options)
699
+ # must explicitly check for :null to allow change_column to work on migrations
700
+ if options[:null] == false
701
+ sql << " NOT NULL"
702
+ end
703
+ if options[:auto_increment] == true
704
+ sql << " AUTO_INCREMENT"
705
+ end
706
+ end
707
+
708
+ # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
709
+ # Both PostgreSQL and Oracle overrides this for custom DISTINCT syntax.
710
+ #
711
+ # distinct("posts.id", "posts.created_at desc")
712
+ #
713
+ def distinct(columns, order_by)
714
+ "DISTINCT #{columns}"
715
+ end
716
+
717
+ # Adds timestamps (+created_at+ and +updated_at+) columns to the named table.
718
+ #
719
+ # add_timestamps(:suppliers)
720
+ #
721
+ def add_timestamps(table_name)
722
+ add_column table_name, :created_at, :datetime
723
+ add_column table_name, :updated_at, :datetime
724
+ end
725
+
726
+ # Removes the timestamp columns (+created_at+ and +updated_at+) from the table definition.
727
+ #
728
+ # remove_timestamps(:suppliers)
729
+ #
730
+ def remove_timestamps(table_name)
731
+ remove_column table_name, :updated_at
732
+ remove_column table_name, :created_at
733
+ end
734
+
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
+
749
+ # Overridden by the mysql adapter for supporting index lengths
750
+ def quoted_columns_for_index(column_names, options = {})
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]}
759
+ end
760
+
761
+ def options_include_default?(options)
762
+ options.include?(:default) && !(options[:null] == false && options[:default].nil?)
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
871
+ end
872
+ end
873
+ end