activerecord 4.2.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 (221) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1372 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +218 -0
  5. data/examples/performance.rb +184 -0
  6. data/examples/simple.rb +14 -0
  7. data/lib/active_record.rb +173 -0
  8. data/lib/active_record/aggregations.rb +266 -0
  9. data/lib/active_record/association_relation.rb +22 -0
  10. data/lib/active_record/associations.rb +1724 -0
  11. data/lib/active_record/associations/alias_tracker.rb +87 -0
  12. data/lib/active_record/associations/association.rb +253 -0
  13. data/lib/active_record/associations/association_scope.rb +194 -0
  14. data/lib/active_record/associations/belongs_to_association.rb +111 -0
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +40 -0
  16. data/lib/active_record/associations/builder/association.rb +149 -0
  17. data/lib/active_record/associations/builder/belongs_to.rb +116 -0
  18. data/lib/active_record/associations/builder/collection_association.rb +91 -0
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +124 -0
  20. data/lib/active_record/associations/builder/has_many.rb +15 -0
  21. data/lib/active_record/associations/builder/has_one.rb +23 -0
  22. data/lib/active_record/associations/builder/singular_association.rb +38 -0
  23. data/lib/active_record/associations/collection_association.rb +634 -0
  24. data/lib/active_record/associations/collection_proxy.rb +1027 -0
  25. data/lib/active_record/associations/has_many_association.rb +184 -0
  26. data/lib/active_record/associations/has_many_through_association.rb +238 -0
  27. data/lib/active_record/associations/has_one_association.rb +105 -0
  28. data/lib/active_record/associations/has_one_through_association.rb +36 -0
  29. data/lib/active_record/associations/join_dependency.rb +282 -0
  30. data/lib/active_record/associations/join_dependency/join_association.rb +122 -0
  31. data/lib/active_record/associations/join_dependency/join_base.rb +22 -0
  32. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  33. data/lib/active_record/associations/preloader.rb +203 -0
  34. data/lib/active_record/associations/preloader/association.rb +162 -0
  35. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  36. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  37. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  38. data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
  39. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  40. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  41. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  42. data/lib/active_record/associations/preloader/through_association.rb +96 -0
  43. data/lib/active_record/associations/singular_association.rb +86 -0
  44. data/lib/active_record/associations/through_association.rb +96 -0
  45. data/lib/active_record/attribute.rb +149 -0
  46. data/lib/active_record/attribute_assignment.rb +212 -0
  47. data/lib/active_record/attribute_decorators.rb +66 -0
  48. data/lib/active_record/attribute_methods.rb +439 -0
  49. data/lib/active_record/attribute_methods/before_type_cast.rb +71 -0
  50. data/lib/active_record/attribute_methods/dirty.rb +181 -0
  51. data/lib/active_record/attribute_methods/primary_key.rb +128 -0
  52. data/lib/active_record/attribute_methods/query.rb +40 -0
  53. data/lib/active_record/attribute_methods/read.rb +103 -0
  54. data/lib/active_record/attribute_methods/serialization.rb +70 -0
  55. data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -0
  56. data/lib/active_record/attribute_methods/write.rb +83 -0
  57. data/lib/active_record/attribute_set.rb +77 -0
  58. data/lib/active_record/attribute_set/builder.rb +86 -0
  59. data/lib/active_record/attributes.rb +139 -0
  60. data/lib/active_record/autosave_association.rb +439 -0
  61. data/lib/active_record/base.rb +317 -0
  62. data/lib/active_record/callbacks.rb +313 -0
  63. data/lib/active_record/coders/json.rb +13 -0
  64. data/lib/active_record/coders/yaml_column.rb +38 -0
  65. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +659 -0
  66. data/lib/active_record/connection_adapters/abstract/database_limits.rb +67 -0
  67. data/lib/active_record/connection_adapters/abstract/database_statements.rb +373 -0
  68. data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -0
  69. data/lib/active_record/connection_adapters/abstract/quoting.rb +133 -0
  70. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
  72. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +574 -0
  73. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
  74. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +991 -0
  75. data/lib/active_record/connection_adapters/abstract/transaction.rb +219 -0
  76. data/lib/active_record/connection_adapters/abstract_adapter.rb +487 -0
  77. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +883 -0
  78. data/lib/active_record/connection_adapters/column.rb +82 -0
  79. data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
  80. data/lib/active_record/connection_adapters/mysql2_adapter.rb +282 -0
  81. data/lib/active_record/connection_adapters/mysql_adapter.rb +491 -0
  82. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
  83. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  84. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +97 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  110. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  111. data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
  112. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  114. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +588 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +754 -0
  117. data/lib/active_record/connection_adapters/schema_cache.rb +94 -0
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +628 -0
  119. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  120. data/lib/active_record/connection_handling.rb +132 -0
  121. data/lib/active_record/core.rb +566 -0
  122. data/lib/active_record/counter_cache.rb +175 -0
  123. data/lib/active_record/dynamic_matchers.rb +140 -0
  124. data/lib/active_record/enum.rb +198 -0
  125. data/lib/active_record/errors.rb +252 -0
  126. data/lib/active_record/explain.rb +38 -0
  127. data/lib/active_record/explain_registry.rb +30 -0
  128. data/lib/active_record/explain_subscriber.rb +29 -0
  129. data/lib/active_record/fixture_set/file.rb +56 -0
  130. data/lib/active_record/fixtures.rb +1007 -0
  131. data/lib/active_record/gem_version.rb +15 -0
  132. data/lib/active_record/inheritance.rb +247 -0
  133. data/lib/active_record/integration.rb +113 -0
  134. data/lib/active_record/locale/en.yml +47 -0
  135. data/lib/active_record/locking/optimistic.rb +204 -0
  136. data/lib/active_record/locking/pessimistic.rb +77 -0
  137. data/lib/active_record/log_subscriber.rb +75 -0
  138. data/lib/active_record/migration.rb +1051 -0
  139. data/lib/active_record/migration/command_recorder.rb +197 -0
  140. data/lib/active_record/migration/join_table.rb +15 -0
  141. data/lib/active_record/model_schema.rb +340 -0
  142. data/lib/active_record/nested_attributes.rb +548 -0
  143. data/lib/active_record/no_touching.rb +52 -0
  144. data/lib/active_record/null_relation.rb +81 -0
  145. data/lib/active_record/persistence.rb +532 -0
  146. data/lib/active_record/query_cache.rb +56 -0
  147. data/lib/active_record/querying.rb +68 -0
  148. data/lib/active_record/railtie.rb +162 -0
  149. data/lib/active_record/railties/console_sandbox.rb +5 -0
  150. data/lib/active_record/railties/controller_runtime.rb +50 -0
  151. data/lib/active_record/railties/databases.rake +391 -0
  152. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  153. data/lib/active_record/readonly_attributes.rb +23 -0
  154. data/lib/active_record/reflection.rb +881 -0
  155. data/lib/active_record/relation.rb +681 -0
  156. data/lib/active_record/relation/batches.rb +138 -0
  157. data/lib/active_record/relation/calculations.rb +403 -0
  158. data/lib/active_record/relation/delegation.rb +140 -0
  159. data/lib/active_record/relation/finder_methods.rb +528 -0
  160. data/lib/active_record/relation/merger.rb +170 -0
  161. data/lib/active_record/relation/predicate_builder.rb +126 -0
  162. data/lib/active_record/relation/predicate_builder/array_handler.rb +47 -0
  163. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  164. data/lib/active_record/relation/query_methods.rb +1176 -0
  165. data/lib/active_record/relation/spawn_methods.rb +75 -0
  166. data/lib/active_record/result.rb +131 -0
  167. data/lib/active_record/runtime_registry.rb +22 -0
  168. data/lib/active_record/sanitization.rb +191 -0
  169. data/lib/active_record/schema.rb +64 -0
  170. data/lib/active_record/schema_dumper.rb +251 -0
  171. data/lib/active_record/schema_migration.rb +56 -0
  172. data/lib/active_record/scoping.rb +87 -0
  173. data/lib/active_record/scoping/default.rb +134 -0
  174. data/lib/active_record/scoping/named.rb +164 -0
  175. data/lib/active_record/serialization.rb +22 -0
  176. data/lib/active_record/serializers/xml_serializer.rb +193 -0
  177. data/lib/active_record/statement_cache.rb +111 -0
  178. data/lib/active_record/store.rb +205 -0
  179. data/lib/active_record/tasks/database_tasks.rb +296 -0
  180. data/lib/active_record/tasks/mysql_database_tasks.rb +145 -0
  181. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  182. data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
  183. data/lib/active_record/timestamp.rb +121 -0
  184. data/lib/active_record/transactions.rb +417 -0
  185. data/lib/active_record/translation.rb +22 -0
  186. data/lib/active_record/type.rb +23 -0
  187. data/lib/active_record/type/big_integer.rb +13 -0
  188. data/lib/active_record/type/binary.rb +50 -0
  189. data/lib/active_record/type/boolean.rb +30 -0
  190. data/lib/active_record/type/date.rb +46 -0
  191. data/lib/active_record/type/date_time.rb +43 -0
  192. data/lib/active_record/type/decimal.rb +40 -0
  193. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  194. data/lib/active_record/type/decorator.rb +14 -0
  195. data/lib/active_record/type/float.rb +19 -0
  196. data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
  197. data/lib/active_record/type/integer.rb +55 -0
  198. data/lib/active_record/type/mutable.rb +16 -0
  199. data/lib/active_record/type/numeric.rb +36 -0
  200. data/lib/active_record/type/serialized.rb +56 -0
  201. data/lib/active_record/type/string.rb +36 -0
  202. data/lib/active_record/type/text.rb +11 -0
  203. data/lib/active_record/type/time.rb +26 -0
  204. data/lib/active_record/type/time_value.rb +38 -0
  205. data/lib/active_record/type/type_map.rb +64 -0
  206. data/lib/active_record/type/unsigned_integer.rb +15 -0
  207. data/lib/active_record/type/value.rb +101 -0
  208. data/lib/active_record/validations.rb +90 -0
  209. data/lib/active_record/validations/associated.rb +51 -0
  210. data/lib/active_record/validations/presence.rb +67 -0
  211. data/lib/active_record/validations/uniqueness.rb +229 -0
  212. data/lib/active_record/version.rb +8 -0
  213. data/lib/rails/generators/active_record.rb +17 -0
  214. data/lib/rails/generators/active_record/migration.rb +18 -0
  215. data/lib/rails/generators/active_record/migration/migration_generator.rb +70 -0
  216. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +22 -0
  217. data/lib/rails/generators/active_record/migration/templates/migration.rb +45 -0
  218. data/lib/rails/generators/active_record/model/model_generator.rb +52 -0
  219. data/lib/rails/generators/active_record/model/templates/model.rb +10 -0
  220. data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
  221. metadata +309 -0
@@ -0,0 +1,50 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters # :nodoc:
3
+ # The goal of this module is to move Adapter specific column
4
+ # definitions to the Adapter instead of having it in the schema
5
+ # dumper itself. This code represents the normal case.
6
+ # We can then redefine how certain data types may be handled in the schema dumper on the
7
+ # Adapter level by over-writing this code inside the database specific adapters
8
+ module ColumnDumper
9
+ def column_spec(column, types)
10
+ spec = prepare_column_options(column, types)
11
+ (spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k}: ")}
12
+ spec
13
+ end
14
+
15
+ # This can be overridden on a Adapter level basis to support other
16
+ # extended datatypes (Example: Adding an array option in the
17
+ # PostgreSQLAdapter)
18
+ def prepare_column_options(column, types)
19
+ spec = {}
20
+ spec[:name] = column.name.inspect
21
+ spec[:type] = column.type.to_s
22
+ spec[:null] = 'false' unless column.null
23
+
24
+ limit = column.limit || types[column.type][:limit]
25
+ spec[:limit] = limit.inspect if limit
26
+ spec[:precision] = column.precision.inspect if column.precision
27
+ spec[:scale] = column.scale.inspect if column.scale
28
+
29
+ default = schema_default(column) if column.has_default?
30
+ spec[:default] = default unless default.nil?
31
+
32
+ spec
33
+ end
34
+
35
+ # Lists the valid migration options
36
+ def migration_keys
37
+ [:name, :limit, :precision, :scale, :default, :null]
38
+ end
39
+
40
+ private
41
+
42
+ def schema_default(column)
43
+ default = column.type_cast_from_database(column.default)
44
+ unless default.nil?
45
+ column.type_cast_for_schema(default)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,991 @@
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).map(&:to_s)
47
+ index_name = options.key?(:name) ? options[:name].to_s : index_name(table_name, column: column_names)
48
+ checks = []
49
+ checks << lambda { |i| i.name == index_name }
50
+ checks << lambda { |i| i.columns == column_names }
51
+ checks << lambda { |i| i.unique } if options[:unique]
52
+
53
+ indexes(table_name).any? { |i| checks.all? { |check| check[i] } }
54
+ end
55
+
56
+ # Returns an array of Column objects for the table specified by +table_name+.
57
+ # See the concrete implementation for details on the expected parameter values.
58
+ def columns(table_name) end
59
+
60
+ # Checks to see if a column exists in a given table.
61
+ #
62
+ # # Check a column exists
63
+ # column_exists?(:suppliers, :name)
64
+ #
65
+ # # Check a column exists of a particular type
66
+ # column_exists?(:suppliers, :name, :string)
67
+ #
68
+ # # Check a column exists with a specific definition
69
+ # column_exists?(:suppliers, :name, :string, limit: 100)
70
+ # column_exists?(:suppliers, :name, :string, default: 'default')
71
+ # column_exists?(:suppliers, :name, :string, null: false)
72
+ # column_exists?(:suppliers, :tax, :decimal, precision: 8, scale: 2)
73
+ #
74
+ def column_exists?(table_name, column_name, type = nil, options = {})
75
+ column_name = column_name.to_s
76
+ columns(table_name).any?{ |c| c.name == column_name &&
77
+ (!type || c.type == type) &&
78
+ (!options.key?(:limit) || c.limit == options[:limit]) &&
79
+ (!options.key?(:precision) || c.precision == options[:precision]) &&
80
+ (!options.key?(:scale) || c.scale == options[:scale]) &&
81
+ (!options.key?(:default) || c.default == options[:default]) &&
82
+ (!options.key?(:null) || c.null == options[:null]) }
83
+ end
84
+
85
+ # Creates a new table with the name +table_name+. +table_name+ may either
86
+ # be a String or a Symbol.
87
+ #
88
+ # There are two ways to work with +create_table+. You can use the block
89
+ # form or the regular form, like this:
90
+ #
91
+ # === Block form
92
+ #
93
+ # # create_table() passes a TableDefinition object to the block.
94
+ # # This form will not only create the table, but also columns for the
95
+ # # table.
96
+ #
97
+ # create_table(:suppliers) do |t|
98
+ # t.column :name, :string, limit: 60
99
+ # # Other fields here
100
+ # end
101
+ #
102
+ # === Block form, with shorthand
103
+ #
104
+ # # You can also use the column types as method calls, rather than calling the column method.
105
+ # create_table(:suppliers) do |t|
106
+ # t.string :name, limit: 60
107
+ # # Other fields here
108
+ # end
109
+ #
110
+ # === Regular form
111
+ #
112
+ # # Creates a table called 'suppliers' with no columns.
113
+ # create_table(:suppliers)
114
+ # # Add a column to 'suppliers'.
115
+ # add_column(:suppliers, :name, :string, {limit: 60})
116
+ #
117
+ # The +options+ hash can include the following keys:
118
+ # [<tt>:id</tt>]
119
+ # Whether to automatically add a primary key column. Defaults to true.
120
+ # Join tables for +has_and_belongs_to_many+ should set it to false.
121
+ # [<tt>:primary_key</tt>]
122
+ # The name of the primary key, if one is to be added automatically.
123
+ # Defaults to +id+. If <tt>:id</tt> is false this option is ignored.
124
+ #
125
+ # Note that Active Record models will automatically detect their
126
+ # primary key. This can be avoided by using +self.primary_key=+ on the model
127
+ # to define the key explicitly.
128
+ #
129
+ # [<tt>:options</tt>]
130
+ # Any extra options you want appended to the table definition.
131
+ # [<tt>:temporary</tt>]
132
+ # Make a temporary table.
133
+ # [<tt>:force</tt>]
134
+ # Set to true to drop the table before creating it.
135
+ # Set to +:cascade+ to drop dependent objects as well.
136
+ # Defaults to false.
137
+ # [<tt>:as</tt>]
138
+ # SQL to use to generate the table. When this option is used, the block is
139
+ # ignored, as are the <tt>:id</tt> and <tt>:primary_key</tt> options.
140
+ #
141
+ # ====== Add a backend specific option to the generated SQL (MySQL)
142
+ #
143
+ # create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
144
+ #
145
+ # generates:
146
+ #
147
+ # CREATE TABLE suppliers (
148
+ # id int(11) DEFAULT NULL auto_increment PRIMARY KEY
149
+ # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
150
+ #
151
+ # ====== Rename the primary key column
152
+ #
153
+ # create_table(:objects, primary_key: 'guid') do |t|
154
+ # t.column :name, :string, limit: 80
155
+ # end
156
+ #
157
+ # generates:
158
+ #
159
+ # CREATE TABLE objects (
160
+ # guid int(11) DEFAULT NULL auto_increment PRIMARY KEY,
161
+ # name varchar(80)
162
+ # )
163
+ #
164
+ # ====== Do not add a primary key column
165
+ #
166
+ # create_table(:categories_suppliers, id: false) do |t|
167
+ # t.column :category_id, :integer
168
+ # t.column :supplier_id, :integer
169
+ # end
170
+ #
171
+ # generates:
172
+ #
173
+ # CREATE TABLE categories_suppliers (
174
+ # category_id int,
175
+ # supplier_id int
176
+ # )
177
+ #
178
+ # ====== Create a temporary table based on a query
179
+ #
180
+ # create_table(:long_query, temporary: true,
181
+ # as: "SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id")
182
+ #
183
+ # generates:
184
+ #
185
+ # CREATE TEMPORARY TABLE long_query AS
186
+ # SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
187
+ #
188
+ # See also TableDefinition#column for details on how to create columns.
189
+ def create_table(table_name, options = {})
190
+ td = create_table_definition table_name, options[:temporary], options[:options], options[:as]
191
+
192
+ if options[:id] != false && !options[:as]
193
+ pk = options.fetch(:primary_key) do
194
+ Base.get_primary_key table_name.to_s.singularize
195
+ end
196
+
197
+ td.primary_key pk, options.fetch(:id, :primary_key), options
198
+ end
199
+
200
+ yield td if block_given?
201
+
202
+ if options[:force] && table_exists?(table_name)
203
+ drop_table(table_name, options)
204
+ end
205
+
206
+ result = execute schema_creation.accept td
207
+ td.indexes.each_pair { |c, o| add_index(table_name, c, o) } unless supports_indexes_in_create?
208
+ result
209
+ end
210
+
211
+ # Creates a new join table with the name created using the lexical order of the first two
212
+ # arguments. These arguments can be a String or a Symbol.
213
+ #
214
+ # # Creates a table called 'assemblies_parts' with no id.
215
+ # create_join_table(:assemblies, :parts)
216
+ #
217
+ # You can pass a +options+ hash can include the following keys:
218
+ # [<tt>:table_name</tt>]
219
+ # Sets the table name overriding the default
220
+ # [<tt>:column_options</tt>]
221
+ # Any extra options you want appended to the columns definition.
222
+ # [<tt>:options</tt>]
223
+ # Any extra options you want appended to the table definition.
224
+ # [<tt>:temporary</tt>]
225
+ # Make a temporary table.
226
+ # [<tt>:force</tt>]
227
+ # Set to true to drop the table before creating it.
228
+ # Defaults to false.
229
+ #
230
+ # Note that +create_join_table+ does not create any indices by default; you can use
231
+ # its block form to do so yourself:
232
+ #
233
+ # create_join_table :products, :categories do |t|
234
+ # t.index :product_id
235
+ # t.index :category_id
236
+ # end
237
+ #
238
+ # ====== Add a backend specific option to the generated SQL (MySQL)
239
+ #
240
+ # create_join_table(:assemblies, :parts, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
241
+ #
242
+ # generates:
243
+ #
244
+ # CREATE TABLE assemblies_parts (
245
+ # assembly_id int NOT NULL,
246
+ # part_id int NOT NULL,
247
+ # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
248
+ #
249
+ def create_join_table(table_1, table_2, options = {})
250
+ join_table_name = find_join_table_name(table_1, table_2, options)
251
+
252
+ column_options = options.delete(:column_options) || {}
253
+ column_options.reverse_merge!(null: false)
254
+
255
+ t1_column, t2_column = [table_1, table_2].map{ |t| t.to_s.singularize.foreign_key }
256
+
257
+ create_table(join_table_name, options.merge!(id: false)) do |td|
258
+ td.integer t1_column, column_options
259
+ td.integer t2_column, column_options
260
+ yield td if block_given?
261
+ end
262
+ end
263
+
264
+ # Drops the join table specified by the given arguments.
265
+ # See +create_join_table+ for details.
266
+ #
267
+ # Although this command ignores the block if one is given, it can be helpful
268
+ # to provide one in a migration's +change+ method so it can be reverted.
269
+ # In that case, the block will be used by create_join_table.
270
+ def drop_join_table(table_1, table_2, options = {})
271
+ join_table_name = find_join_table_name(table_1, table_2, options)
272
+ drop_table(join_table_name)
273
+ end
274
+
275
+ # A block for changing columns in +table+.
276
+ #
277
+ # # change_table() yields a Table instance
278
+ # change_table(:suppliers) do |t|
279
+ # t.column :name, :string, limit: 60
280
+ # # Other column alterations here
281
+ # end
282
+ #
283
+ # The +options+ hash can include the following keys:
284
+ # [<tt>:bulk</tt>]
285
+ # Set this to true to make this a bulk alter query, such as
286
+ #
287
+ # ALTER TABLE `users` ADD COLUMN age INT(11), ADD COLUMN birthdate DATETIME ...
288
+ #
289
+ # Defaults to false.
290
+ #
291
+ # ====== Add a column
292
+ #
293
+ # change_table(:suppliers) do |t|
294
+ # t.column :name, :string, limit: 60
295
+ # end
296
+ #
297
+ # ====== Add 2 integer columns
298
+ #
299
+ # change_table(:suppliers) do |t|
300
+ # t.integer :width, :height, null: false, default: 0
301
+ # end
302
+ #
303
+ # ====== Add created_at/updated_at columns
304
+ #
305
+ # change_table(:suppliers) do |t|
306
+ # t.timestamps
307
+ # end
308
+ #
309
+ # ====== Add a foreign key column
310
+ #
311
+ # change_table(:suppliers) do |t|
312
+ # t.references :company
313
+ # end
314
+ #
315
+ # Creates a <tt>company_id(integer)</tt> column.
316
+ #
317
+ # ====== Add a polymorphic foreign key column
318
+ #
319
+ # change_table(:suppliers) do |t|
320
+ # t.belongs_to :company, polymorphic: true
321
+ # end
322
+ #
323
+ # Creates <tt>company_type(varchar)</tt> and <tt>company_id(integer)</tt> columns.
324
+ #
325
+ # ====== Remove a column
326
+ #
327
+ # change_table(:suppliers) do |t|
328
+ # t.remove :company
329
+ # end
330
+ #
331
+ # ====== Remove several columns
332
+ #
333
+ # change_table(:suppliers) do |t|
334
+ # t.remove :company_id
335
+ # t.remove :width, :height
336
+ # end
337
+ #
338
+ # ====== Remove an index
339
+ #
340
+ # change_table(:suppliers) do |t|
341
+ # t.remove_index :company_id
342
+ # end
343
+ #
344
+ # See also Table for details on all of the various column transformation.
345
+ def change_table(table_name, options = {})
346
+ if supports_bulk_alter? && options[:bulk]
347
+ recorder = ActiveRecord::Migration::CommandRecorder.new(self)
348
+ yield update_table_definition(table_name, recorder)
349
+ bulk_change_table(table_name, recorder.commands)
350
+ else
351
+ yield update_table_definition(table_name, self)
352
+ end
353
+ end
354
+
355
+ # Renames a table.
356
+ #
357
+ # rename_table('octopuses', 'octopi')
358
+ #
359
+ def rename_table(table_name, new_name)
360
+ raise NotImplementedError, "rename_table is not implemented"
361
+ end
362
+
363
+ # Drops a table from the database.
364
+ #
365
+ # [<tt>:force</tt>]
366
+ # Set to +:cascade+ to drop dependent objects as well.
367
+ # Defaults to false.
368
+ #
369
+ # Although this command ignores most +options+ and the block if one is given,
370
+ # it can be helpful to provide these in a migration's +change+ method so it can be reverted.
371
+ # In that case, +options+ and the block will be used by create_table.
372
+ def drop_table(table_name, options = {})
373
+ execute "DROP TABLE #{quote_table_name(table_name)}"
374
+ end
375
+
376
+ # Adds a new column to the named table.
377
+ # See TableDefinition#column for details of the options you can use.
378
+ def add_column(table_name, column_name, type, options = {})
379
+ at = create_alter_table table_name
380
+ at.add_column(column_name, type, options)
381
+ execute schema_creation.accept at
382
+ end
383
+
384
+ # Removes the given columns from the table definition.
385
+ #
386
+ # remove_columns(:suppliers, :qualification, :experience)
387
+ #
388
+ def remove_columns(table_name, *column_names)
389
+ raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)") if column_names.empty?
390
+ column_names.each do |column_name|
391
+ remove_column(table_name, column_name)
392
+ end
393
+ end
394
+
395
+ # Removes the column from the table definition.
396
+ #
397
+ # remove_column(:suppliers, :qualification)
398
+ #
399
+ # The +type+ and +options+ parameters will be ignored if present. It can be helpful
400
+ # to provide these in a migration's +change+ method so it can be reverted.
401
+ # In that case, +type+ and +options+ will be used by add_column.
402
+ def remove_column(table_name, column_name, type = nil, options = {})
403
+ execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{quote_column_name(column_name)}"
404
+ end
405
+
406
+ # Changes the column's definition according to the new options.
407
+ # See TableDefinition#column for details of the options you can use.
408
+ #
409
+ # change_column(:suppliers, :name, :string, limit: 80)
410
+ # change_column(:accounts, :description, :text)
411
+ #
412
+ def change_column(table_name, column_name, type, options = {})
413
+ raise NotImplementedError, "change_column is not implemented"
414
+ end
415
+
416
+ # Sets a new default value for a column:
417
+ #
418
+ # change_column_default(:suppliers, :qualification, 'new')
419
+ # change_column_default(:accounts, :authorized, 1)
420
+ #
421
+ # Setting the default to +nil+ effectively drops the default:
422
+ #
423
+ # change_column_default(:users, :email, nil)
424
+ #
425
+ def change_column_default(table_name, column_name, default)
426
+ raise NotImplementedError, "change_column_default is not implemented"
427
+ end
428
+
429
+ # Sets or removes a +NOT NULL+ constraint on a column. The +null+ flag
430
+ # indicates whether the value can be +NULL+. For example
431
+ #
432
+ # change_column_null(:users, :nickname, false)
433
+ #
434
+ # says nicknames cannot be +NULL+ (adds the constraint), whereas
435
+ #
436
+ # change_column_null(:users, :nickname, true)
437
+ #
438
+ # allows them to be +NULL+ (drops the constraint).
439
+ #
440
+ # The method accepts an optional fourth argument to replace existing
441
+ # +NULL+s with some other value. Use that one when enabling the
442
+ # constraint if needed, since otherwise those rows would not be valid.
443
+ #
444
+ # Please note the fourth argument does not set a column's default.
445
+ def change_column_null(table_name, column_name, null, default = nil)
446
+ raise NotImplementedError, "change_column_null is not implemented"
447
+ end
448
+
449
+ # Renames a column.
450
+ #
451
+ # rename_column(:suppliers, :description, :name)
452
+ #
453
+ def rename_column(table_name, column_name, new_column_name)
454
+ raise NotImplementedError, "rename_column is not implemented"
455
+ end
456
+
457
+ # Adds a new index to the table. +column_name+ can be a single Symbol, or
458
+ # an Array of Symbols.
459
+ #
460
+ # The index will be named after the table and the column name(s), unless
461
+ # you pass <tt>:name</tt> as an option.
462
+ #
463
+ # ====== Creating a simple index
464
+ #
465
+ # add_index(:suppliers, :name)
466
+ #
467
+ # generates:
468
+ #
469
+ # CREATE INDEX suppliers_name_index ON suppliers(name)
470
+ #
471
+ # ====== Creating a unique index
472
+ #
473
+ # add_index(:accounts, [:branch_id, :party_id], unique: true)
474
+ #
475
+ # generates:
476
+ #
477
+ # CREATE UNIQUE INDEX accounts_branch_id_party_id_index ON accounts(branch_id, party_id)
478
+ #
479
+ # ====== Creating a named index
480
+ #
481
+ # add_index(:accounts, [:branch_id, :party_id], unique: true, name: 'by_branch_party')
482
+ #
483
+ # generates:
484
+ #
485
+ # CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id)
486
+ #
487
+ # ====== Creating an index with specific key length
488
+ #
489
+ # add_index(:accounts, :name, name: 'by_name', length: 10)
490
+ #
491
+ # generates:
492
+ #
493
+ # CREATE INDEX by_name ON accounts(name(10))
494
+ #
495
+ # add_index(:accounts, [:name, :surname], name: 'by_name_surname', length: {name: 10, surname: 15})
496
+ #
497
+ # generates:
498
+ #
499
+ # CREATE INDEX by_name_surname ON accounts(name(10), surname(15))
500
+ #
501
+ # Note: SQLite doesn't support index length.
502
+ #
503
+ # ====== Creating an index with a sort order (desc or asc, asc is the default)
504
+ #
505
+ # add_index(:accounts, [:branch_id, :party_id, :surname], order: {branch_id: :desc, party_id: :asc})
506
+ #
507
+ # generates:
508
+ #
509
+ # CREATE INDEX by_branch_desc_party ON accounts(branch_id DESC, party_id ASC, surname)
510
+ #
511
+ # Note: MySQL doesn't yet support index order (it accepts the syntax but ignores it).
512
+ #
513
+ # ====== Creating a partial index
514
+ #
515
+ # add_index(:accounts, [:branch_id, :party_id], unique: true, where: "active")
516
+ #
517
+ # generates:
518
+ #
519
+ # CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
520
+ #
521
+ # ====== Creating an index with a specific method
522
+ #
523
+ # add_index(:developers, :name, using: 'btree')
524
+ #
525
+ # generates:
526
+ #
527
+ # CREATE INDEX index_developers_on_name ON developers USING btree (name) -- PostgreSQL
528
+ # CREATE INDEX index_developers_on_name USING btree ON developers (name) -- MySQL
529
+ #
530
+ # Note: only supported by PostgreSQL and MySQL
531
+ #
532
+ # ====== Creating an index with a specific type
533
+ #
534
+ # add_index(:developers, :name, type: :fulltext)
535
+ #
536
+ # generates:
537
+ #
538
+ # CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
539
+ #
540
+ # Note: only supported by MySQL. Supported: <tt>:fulltext</tt> and <tt>:spatial</tt> on MyISAM tables.
541
+ def add_index(table_name, column_name, options = {})
542
+ index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
543
+ execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
544
+ end
545
+
546
+ # Removes the given index from the table.
547
+ #
548
+ # Removes the +index_accounts_on_column+ in the +accounts+ table.
549
+ #
550
+ # remove_index :accounts, :column
551
+ #
552
+ # Removes the index named +index_accounts_on_branch_id+ in the +accounts+ table.
553
+ #
554
+ # remove_index :accounts, column: :branch_id
555
+ #
556
+ # Removes the index named +index_accounts_on_branch_id_and_party_id+ in the +accounts+ table.
557
+ #
558
+ # remove_index :accounts, column: [:branch_id, :party_id]
559
+ #
560
+ # Removes the index named +by_branch_party+ in the +accounts+ table.
561
+ #
562
+ # remove_index :accounts, name: :by_branch_party
563
+ #
564
+ def remove_index(table_name, options = {})
565
+ remove_index!(table_name, index_name_for_remove(table_name, options))
566
+ end
567
+
568
+ def remove_index!(table_name, index_name) #:nodoc:
569
+ execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
570
+ end
571
+
572
+ # Renames an index.
573
+ #
574
+ # Rename the +index_people_on_last_name+ index to +index_users_on_last_name+:
575
+ #
576
+ # rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
577
+ #
578
+ def rename_index(table_name, old_name, new_name)
579
+ if new_name.length > allowed_index_name_length
580
+ raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters"
581
+ end
582
+ # this is a naive implementation; some DBs may support this more efficiently (Postgres, for instance)
583
+ old_index_def = indexes(table_name).detect { |i| i.name == old_name }
584
+ return unless old_index_def
585
+ add_index(table_name, old_index_def.columns, name: new_name, unique: old_index_def.unique)
586
+ remove_index(table_name, name: old_name)
587
+ end
588
+
589
+ def index_name(table_name, options) #:nodoc:
590
+ if Hash === options
591
+ if options[:column]
592
+ "index_#{table_name}_on_#{Array(options[:column]) * '_and_'}"
593
+ elsif options[:name]
594
+ options[:name]
595
+ else
596
+ raise ArgumentError, "You must specify the index name"
597
+ end
598
+ else
599
+ index_name(table_name, :column => options)
600
+ end
601
+ end
602
+
603
+ # Verifies the existence of an index with a given name.
604
+ #
605
+ # The default argument is returned if the underlying implementation does not define the indexes method,
606
+ # as there's no way to determine the correct answer in that case.
607
+ def index_name_exists?(table_name, index_name, default)
608
+ return default unless respond_to?(:indexes)
609
+ index_name = index_name.to_s
610
+ indexes(table_name).detect { |i| i.name == index_name }
611
+ end
612
+
613
+ # Adds a reference. Optionally adds a +type+ column, if <tt>:polymorphic</tt> option is provided.
614
+ # The reference column is an +integer+ by default, the <tt>:type</tt> option can be used to specify
615
+ # a different type.
616
+ # <tt>add_reference</tt> and <tt>add_belongs_to</tt> are acceptable.
617
+ #
618
+ # ====== Create a user_id integer column
619
+ #
620
+ # add_reference(:products, :user)
621
+ #
622
+ # ====== Create a user_id string column
623
+ #
624
+ # add_reference(:products, :user, type: :string)
625
+ #
626
+ # ====== Create a supplier_id and supplier_type columns
627
+ #
628
+ # add_belongs_to(:products, :supplier, polymorphic: true)
629
+ #
630
+ # ====== Create a supplier_id, supplier_type columns and appropriate index
631
+ #
632
+ # add_reference(:products, :supplier, polymorphic: true, index: true)
633
+ #
634
+ def add_reference(table_name, ref_name, options = {})
635
+ polymorphic = options.delete(:polymorphic)
636
+ index_options = options.delete(:index)
637
+ type = options.delete(:type) || :integer
638
+ add_column(table_name, "#{ref_name}_id", type, options)
639
+ add_column(table_name, "#{ref_name}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic
640
+ add_index(table_name, polymorphic ? %w[type id].map{ |t| "#{ref_name}_#{t}" } : "#{ref_name}_id", index_options.is_a?(Hash) ? index_options : {}) if index_options
641
+ end
642
+ alias :add_belongs_to :add_reference
643
+
644
+ # Removes the reference(s). Also removes a +type+ column if one exists.
645
+ # <tt>remove_reference</tt>, <tt>remove_references</tt> and <tt>remove_belongs_to</tt> are acceptable.
646
+ #
647
+ # ====== Remove the reference
648
+ #
649
+ # remove_reference(:products, :user, index: true)
650
+ #
651
+ # ====== Remove polymorphic reference
652
+ #
653
+ # remove_reference(:products, :supplier, polymorphic: true)
654
+ #
655
+ def remove_reference(table_name, ref_name, options = {})
656
+ remove_column(table_name, "#{ref_name}_id")
657
+ remove_column(table_name, "#{ref_name}_type") if options[:polymorphic]
658
+ end
659
+ alias :remove_belongs_to :remove_reference
660
+
661
+ # Returns an array of foreign keys for the given table.
662
+ # The foreign keys are represented as +ForeignKeyDefinition+ objects.
663
+ def foreign_keys(table_name)
664
+ raise NotImplementedError, "foreign_keys is not implemented"
665
+ end
666
+
667
+ # Adds a new foreign key. +from_table+ is the table with the key column,
668
+ # +to_table+ contains the referenced primary key.
669
+ #
670
+ # The foreign key will be named after the following pattern: <tt>fk_rails_<identifier></tt>.
671
+ # +identifier+ is a 10 character long random string. A custom name can be specified with
672
+ # the <tt>:name</tt> option.
673
+ #
674
+ # ====== Creating a simple foreign key
675
+ #
676
+ # add_foreign_key :articles, :authors
677
+ #
678
+ # generates:
679
+ #
680
+ # ALTER TABLE "articles" ADD CONSTRAINT articles_author_id_fk FOREIGN KEY ("author_id") REFERENCES "authors" ("id")
681
+ #
682
+ # ====== Creating a foreign key on a specific column
683
+ #
684
+ # add_foreign_key :articles, :users, column: :author_id, primary_key: "lng_id"
685
+ #
686
+ # generates:
687
+ #
688
+ # ALTER TABLE "articles" ADD CONSTRAINT fk_rails_58ca3d3a82 FOREIGN KEY ("author_id") REFERENCES "users" ("lng_id")
689
+ #
690
+ # ====== Creating a cascading foreign key
691
+ #
692
+ # add_foreign_key :articles, :authors, on_delete: :cascade
693
+ #
694
+ # generates:
695
+ #
696
+ # ALTER TABLE "articles" ADD CONSTRAINT articles_author_id_fk FOREIGN KEY ("author_id") REFERENCES "authors" ("id") ON DELETE CASCADE
697
+ #
698
+ # The +options+ hash can include the following keys:
699
+ # [<tt>:column</tt>]
700
+ # The foreign key column name on +from_table+. Defaults to <tt>to_table.singularize + "_id"</tt>
701
+ # [<tt>:primary_key</tt>]
702
+ # The primary key column name on +to_table+. Defaults to +id+.
703
+ # [<tt>:name</tt>]
704
+ # The constraint name. Defaults to <tt>fk_rails_<identifier></tt>.
705
+ # [<tt>:on_delete</tt>]
706
+ # Action that happens <tt>ON DELETE</tt>. Valid values are +:nullify+, +:cascade:+ and +:restrict+
707
+ # [<tt>:on_update</tt>]
708
+ # Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade:+ and +:restrict+
709
+ def add_foreign_key(from_table, to_table, options = {})
710
+ return unless supports_foreign_keys?
711
+
712
+ options[:column] ||= foreign_key_column_for(to_table)
713
+
714
+ options = {
715
+ column: options[:column],
716
+ primary_key: options[:primary_key],
717
+ name: foreign_key_name(from_table, options),
718
+ on_delete: options[:on_delete],
719
+ on_update: options[:on_update]
720
+ }
721
+ at = create_alter_table from_table
722
+ at.add_foreign_key to_table, options
723
+
724
+ execute schema_creation.accept(at)
725
+ end
726
+
727
+ # Removes the given foreign key from the table.
728
+ #
729
+ # Removes the foreign key on +accounts.branch_id+.
730
+ #
731
+ # remove_foreign_key :accounts, :branches
732
+ #
733
+ # Removes the foreign key on +accounts.owner_id+.
734
+ #
735
+ # remove_foreign_key :accounts, column: :owner_id
736
+ #
737
+ # Removes the foreign key named +special_fk_name+ on the +accounts+ table.
738
+ #
739
+ # remove_foreign_key :accounts, name: :special_fk_name
740
+ #
741
+ def remove_foreign_key(from_table, options_or_to_table = {})
742
+ return unless supports_foreign_keys?
743
+
744
+ if options_or_to_table.is_a?(Hash)
745
+ options = options_or_to_table
746
+ else
747
+ options = { column: foreign_key_column_for(options_or_to_table) }
748
+ end
749
+
750
+ fk_name_to_delete = options.fetch(:name) do
751
+ fk_to_delete = foreign_keys(from_table).detect {|fk| fk.column == options[:column].to_s }
752
+
753
+ if fk_to_delete
754
+ fk_to_delete.name
755
+ else
756
+ raise ArgumentError, "Table '#{from_table}' has no foreign key on column '#{options[:column]}'"
757
+ end
758
+ end
759
+
760
+ at = create_alter_table from_table
761
+ at.drop_foreign_key fk_name_to_delete
762
+
763
+ execute schema_creation.accept(at)
764
+ end
765
+
766
+ def foreign_key_column_for(table_name) # :nodoc:
767
+ "#{table_name.to_s.singularize}_id"
768
+ end
769
+
770
+ def dump_schema_information #:nodoc:
771
+ sm_table = ActiveRecord::Migrator.schema_migrations_table_name
772
+
773
+ ActiveRecord::SchemaMigration.order('version').map { |sm|
774
+ "INSERT INTO #{sm_table} (version) VALUES ('#{sm.version}');"
775
+ }.join "\n\n"
776
+ end
777
+
778
+ # Should not be called normally, but this operation is non-destructive.
779
+ # The migrations module handles this automatically.
780
+ def initialize_schema_migrations_table
781
+ ActiveRecord::SchemaMigration.create_table
782
+ end
783
+
784
+ def assume_migrated_upto_version(version, migrations_paths = ActiveRecord::Migrator.migrations_paths)
785
+ migrations_paths = Array(migrations_paths)
786
+ version = version.to_i
787
+ sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
788
+
789
+ migrated = select_values("SELECT version FROM #{sm_table}").map { |v| v.to_i }
790
+ paths = migrations_paths.map {|p| "#{p}/[0-9]*_*.rb" }
791
+ versions = Dir[*paths].map do |filename|
792
+ filename.split('/').last.split('_').first.to_i
793
+ end
794
+
795
+ unless migrated.include?(version)
796
+ execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')"
797
+ end
798
+
799
+ inserted = Set.new
800
+ (versions - migrated).each do |v|
801
+ if inserted.include?(v)
802
+ raise "Duplicate migration #{v}. Please renumber your migrations to resolve the conflict."
803
+ elsif v < version
804
+ execute "INSERT INTO #{sm_table} (version) VALUES ('#{v}')"
805
+ inserted << v
806
+ end
807
+ end
808
+ end
809
+
810
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
811
+ if native = native_database_types[type.to_sym]
812
+ column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
813
+
814
+ if type == :decimal # ignore limit, use precision and scale
815
+ scale ||= native[:scale]
816
+
817
+ if precision ||= native[:precision]
818
+ if scale
819
+ column_type_sql << "(#{precision},#{scale})"
820
+ else
821
+ column_type_sql << "(#{precision})"
822
+ end
823
+ elsif scale
824
+ raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
825
+ end
826
+
827
+ elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
828
+ column_type_sql << "(#{limit})"
829
+ end
830
+
831
+ column_type_sql
832
+ else
833
+ type.to_s
834
+ end
835
+ end
836
+
837
+ # Given a set of columns and an ORDER BY clause, returns the columns for a SELECT DISTINCT.
838
+ # Both PostgreSQL and Oracle overrides this for custom DISTINCT syntax - they
839
+ # require the order columns appear in the SELECT.
840
+ #
841
+ # columns_for_distinct("posts.id", ["posts.created_at desc"])
842
+ def columns_for_distinct(columns, orders) #:nodoc:
843
+ columns
844
+ end
845
+
846
+ include TimestampDefaultDeprecation
847
+ # Adds timestamps (+created_at+ and +updated_at+) columns to +table_name+.
848
+ # Additional options (like <tt>null: false</tt>) are forwarded to #add_column.
849
+ #
850
+ # add_timestamps(:suppliers, null: false)
851
+ #
852
+ def add_timestamps(table_name, options = {})
853
+ emit_warning_if_null_unspecified(options)
854
+ add_column table_name, :created_at, :datetime, options
855
+ add_column table_name, :updated_at, :datetime, options
856
+ end
857
+
858
+ # Removes the timestamp columns (+created_at+ and +updated_at+) from the table definition.
859
+ #
860
+ # remove_timestamps(:suppliers)
861
+ #
862
+ def remove_timestamps(table_name, options = {})
863
+ remove_column table_name, :updated_at
864
+ remove_column table_name, :created_at
865
+ end
866
+
867
+ def update_table_definition(table_name, base) #:nodoc:
868
+ Table.new(table_name, base)
869
+ end
870
+
871
+ def add_index_options(table_name, column_name, options = {}) #:nodoc:
872
+ column_names = Array(column_name)
873
+ index_name = index_name(table_name, column: column_names)
874
+
875
+ options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type)
876
+
877
+ index_type = options[:unique] ? "UNIQUE" : ""
878
+ index_type = options[:type].to_s if options.key?(:type)
879
+ index_name = options[:name].to_s if options.key?(:name)
880
+ max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
881
+
882
+ if options.key?(:algorithm)
883
+ algorithm = index_algorithms.fetch(options[:algorithm]) {
884
+ raise ArgumentError.new("Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}")
885
+ }
886
+ end
887
+
888
+ using = "USING #{options[:using]}" if options[:using].present?
889
+
890
+ if supports_partial_index?
891
+ index_options = options[:where] ? " WHERE #{options[:where]}" : ""
892
+ end
893
+
894
+ if index_name.length > max_index_length
895
+ raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{max_index_length} characters"
896
+ end
897
+ if table_exists?(table_name) && index_name_exists?(table_name, index_name, false)
898
+ raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
899
+ end
900
+ index_columns = quoted_columns_for_index(column_names, options).join(", ")
901
+
902
+ [index_name, index_type, index_columns, index_options, algorithm, using]
903
+ end
904
+
905
+ protected
906
+ def add_index_sort_order(option_strings, column_names, options = {})
907
+ if options.is_a?(Hash) && order = options[:order]
908
+ case order
909
+ when Hash
910
+ column_names.each {|name| option_strings[name] += " #{order[name].upcase}" if order.has_key?(name)}
911
+ when String
912
+ column_names.each {|name| option_strings[name] += " #{order.upcase}"}
913
+ end
914
+ end
915
+
916
+ return option_strings
917
+ end
918
+
919
+ # Overridden by the MySQL adapter for supporting index lengths
920
+ def quoted_columns_for_index(column_names, options = {})
921
+ option_strings = Hash[column_names.map {|name| [name, '']}]
922
+
923
+ # add index sort order if supported
924
+ if supports_index_sort_order?
925
+ option_strings = add_index_sort_order(option_strings, column_names, options)
926
+ end
927
+
928
+ column_names.map {|name| quote_column_name(name) + option_strings[name]}
929
+ end
930
+
931
+ def options_include_default?(options)
932
+ options.include?(:default) && !(options[:null] == false && options[:default].nil?)
933
+ end
934
+
935
+ def index_name_for_remove(table_name, options = {})
936
+ index_name = index_name(table_name, options)
937
+
938
+ unless index_name_exists?(table_name, index_name, true)
939
+ if options.is_a?(Hash) && options.has_key?(:name)
940
+ options_without_column = options.dup
941
+ options_without_column.delete :column
942
+ index_name_without_column = index_name(table_name, options_without_column)
943
+
944
+ return index_name_without_column if index_name_exists?(table_name, index_name_without_column, false)
945
+ end
946
+
947
+ raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
948
+ end
949
+
950
+ index_name
951
+ end
952
+
953
+ def rename_table_indexes(table_name, new_name)
954
+ indexes(new_name).each do |index|
955
+ generated_index_name = index_name(table_name, column: index.columns)
956
+ if generated_index_name == index.name
957
+ rename_index new_name, generated_index_name, index_name(new_name, column: index.columns)
958
+ end
959
+ end
960
+ end
961
+
962
+ def rename_column_indexes(table_name, column_name, new_column_name)
963
+ column_name, new_column_name = column_name.to_s, new_column_name.to_s
964
+ indexes(table_name).each do |index|
965
+ next unless index.columns.include?(new_column_name)
966
+ old_columns = index.columns.dup
967
+ old_columns[old_columns.index(new_column_name)] = column_name
968
+ generated_index_name = index_name(table_name, column: old_columns)
969
+ if generated_index_name == index.name
970
+ rename_index table_name, generated_index_name, index_name(table_name, column: index.columns)
971
+ end
972
+ end
973
+ end
974
+
975
+ private
976
+ def create_table_definition(name, temporary, options, as = nil)
977
+ TableDefinition.new native_database_types, name, temporary, options, as
978
+ end
979
+
980
+ def create_alter_table(name)
981
+ AlterTable.new create_table_definition(name, false, {})
982
+ end
983
+
984
+ def foreign_key_name(table_name, options) # :nodoc:
985
+ options.fetch(:name) do
986
+ "fk_rails_#{SecureRandom.hex(5)}"
987
+ end
988
+ end
989
+ end
990
+ end
991
+ end