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,883 @@
1
+ require 'arel/visitors/bind_visitor'
2
+ require 'active_support/core_ext/string/strip'
3
+
4
+ module ActiveRecord
5
+ module ConnectionAdapters
6
+ class AbstractMysqlAdapter < AbstractAdapter
7
+ include Savepoints
8
+
9
+ class SchemaCreation < AbstractAdapter::SchemaCreation
10
+ def visit_AddColumn(o)
11
+ add_column_position!(super, column_options(o))
12
+ end
13
+
14
+ private
15
+
16
+ def visit_DropForeignKey(name)
17
+ "DROP FOREIGN KEY #{name}"
18
+ end
19
+
20
+ def visit_TableDefinition(o)
21
+ name = o.name
22
+ create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE #{quote_table_name(name)} "
23
+
24
+ statements = o.columns.map { |c| accept c }
25
+ statements.concat(o.indexes.map { |column_name, options| index_in_create(name, column_name, options) })
26
+
27
+ create_sql << "(#{statements.join(', ')}) " if statements.present?
28
+ create_sql << "#{o.options}"
29
+ create_sql << " AS #{@conn.to_sql(o.as)}" if o.as
30
+ create_sql
31
+ end
32
+
33
+ def visit_ChangeColumnDefinition(o)
34
+ column = o.column
35
+ options = o.options
36
+ sql_type = type_to_sql(o.type, options[:limit], options[:precision], options[:scale])
37
+ change_column_sql = "CHANGE #{quote_column_name(column.name)} #{quote_column_name(options[:name])} #{sql_type}"
38
+ add_column_options!(change_column_sql, options.merge(column: column))
39
+ add_column_position!(change_column_sql, options)
40
+ end
41
+
42
+ def add_column_position!(sql, options)
43
+ if options[:first]
44
+ sql << " FIRST"
45
+ elsif options[:after]
46
+ sql << " AFTER #{quote_column_name(options[:after])}"
47
+ end
48
+ sql
49
+ end
50
+
51
+ def index_in_create(table_name, column_name, options)
52
+ index_name, index_type, index_columns, index_options, index_algorithm, index_using = @conn.add_index_options(table_name, column_name, options)
53
+ "#{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_options} #{index_algorithm}"
54
+ end
55
+ end
56
+
57
+ def schema_creation
58
+ SchemaCreation.new self
59
+ end
60
+
61
+ class Column < ConnectionAdapters::Column # :nodoc:
62
+ attr_reader :collation, :strict, :extra
63
+
64
+ def initialize(name, default, cast_type, sql_type = nil, null = true, collation = nil, strict = false, extra = "")
65
+ @strict = strict
66
+ @collation = collation
67
+ @extra = extra
68
+ super(name, default, cast_type, sql_type, null)
69
+ assert_valid_default(default)
70
+ extract_default
71
+ end
72
+
73
+ def extract_default
74
+ if blob_or_text_column?
75
+ @default = null || strict ? nil : ''
76
+ elsif missing_default_forged_as_empty_string?(@default)
77
+ @default = nil
78
+ end
79
+ end
80
+
81
+ def has_default?
82
+ return false if blob_or_text_column? # MySQL forbids defaults on blob and text columns
83
+ super
84
+ end
85
+
86
+ def blob_or_text_column?
87
+ sql_type =~ /blob/i || type == :text
88
+ end
89
+
90
+ def case_sensitive?
91
+ collation && !collation.match(/_ci$/)
92
+ end
93
+
94
+ def ==(other)
95
+ super &&
96
+ collation == other.collation &&
97
+ strict == other.strict &&
98
+ extra == other.extra
99
+ end
100
+
101
+ private
102
+
103
+ # MySQL misreports NOT NULL column default when none is given.
104
+ # We can't detect this for columns which may have a legitimate ''
105
+ # default (string) but we can for others (integer, datetime, boolean,
106
+ # and the rest).
107
+ #
108
+ # Test whether the column has default '', is not null, and is not
109
+ # a type allowing default ''.
110
+ def missing_default_forged_as_empty_string?(default)
111
+ type != :string && !null && default == ''
112
+ end
113
+
114
+ def assert_valid_default(default)
115
+ if blob_or_text_column? && default.present?
116
+ raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
117
+ end
118
+ end
119
+
120
+ def attributes_for_hash
121
+ super + [collation, strict, extra]
122
+ end
123
+ end
124
+
125
+ ##
126
+ # :singleton-method:
127
+ # By default, the MysqlAdapter will consider all columns of type <tt>tinyint(1)</tt>
128
+ # as boolean. If you wish to disable this emulation (which was the default
129
+ # behavior in versions 0.13.1 and earlier) you can add the following line
130
+ # to your application.rb file:
131
+ #
132
+ # ActiveRecord::ConnectionAdapters::Mysql[2]Adapter.emulate_booleans = false
133
+ class_attribute :emulate_booleans
134
+ self.emulate_booleans = true
135
+
136
+ LOST_CONNECTION_ERROR_MESSAGES = [
137
+ "Server shutdown in progress",
138
+ "Broken pipe",
139
+ "Lost connection to MySQL server during query",
140
+ "MySQL server has gone away" ]
141
+
142
+ QUOTED_TRUE, QUOTED_FALSE = '1', '0'
143
+
144
+ NATIVE_DATABASE_TYPES = {
145
+ :primary_key => "int(11) auto_increment PRIMARY KEY",
146
+ :string => { :name => "varchar", :limit => 255 },
147
+ :text => { :name => "text" },
148
+ :integer => { :name => "int", :limit => 4 },
149
+ :float => { :name => "float" },
150
+ :decimal => { :name => "decimal" },
151
+ :datetime => { :name => "datetime" },
152
+ :time => { :name => "time" },
153
+ :date => { :name => "date" },
154
+ :binary => { :name => "blob" },
155
+ :boolean => { :name => "tinyint", :limit => 1 }
156
+ }
157
+
158
+ INDEX_TYPES = [:fulltext, :spatial]
159
+ INDEX_USINGS = [:btree, :hash]
160
+
161
+ # FIXME: Make the first parameter more similar for the two adapters
162
+ def initialize(connection, logger, connection_options, config)
163
+ super(connection, logger)
164
+ @connection_options, @config = connection_options, config
165
+ @quoted_column_names, @quoted_table_names = {}, {}
166
+
167
+ @visitor = Arel::Visitors::MySQL.new self
168
+
169
+ if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
170
+ @prepared_statements = true
171
+ else
172
+ @prepared_statements = false
173
+ end
174
+ end
175
+
176
+ # Returns true, since this connection adapter supports migrations.
177
+ def supports_migrations?
178
+ true
179
+ end
180
+
181
+ def supports_primary_key?
182
+ true
183
+ end
184
+
185
+ def supports_bulk_alter? #:nodoc:
186
+ true
187
+ end
188
+
189
+ # Technically MySQL allows to create indexes with the sort order syntax
190
+ # but at the moment (5.5) it doesn't yet implement them
191
+ def supports_index_sort_order?
192
+ true
193
+ end
194
+
195
+ # MySQL 4 technically support transaction isolation, but it is affected by a bug
196
+ # where the transaction level gets persisted for the whole session:
197
+ #
198
+ # http://bugs.mysql.com/bug.php?id=39170
199
+ def supports_transaction_isolation?
200
+ version[0] >= 5
201
+ end
202
+
203
+ def supports_indexes_in_create?
204
+ true
205
+ end
206
+
207
+ def supports_foreign_keys?
208
+ true
209
+ end
210
+
211
+ def supports_views?
212
+ version[0] >= 5
213
+ end
214
+
215
+ def native_database_types
216
+ NATIVE_DATABASE_TYPES
217
+ end
218
+
219
+ def index_algorithms
220
+ { default: 'ALGORITHM = DEFAULT', copy: 'ALGORITHM = COPY', inplace: 'ALGORITHM = INPLACE' }
221
+ end
222
+
223
+ # HELPER METHODS ===========================================
224
+
225
+ # The two drivers have slightly different ways of yielding hashes of results, so
226
+ # this method must be implemented to provide a uniform interface.
227
+ def each_hash(result) # :nodoc:
228
+ raise NotImplementedError
229
+ end
230
+
231
+ def new_column(field, default, cast_type, sql_type = nil, null = true, collation = "", extra = "") # :nodoc:
232
+ Column.new(field, default, cast_type, sql_type, null, collation, strict_mode?, extra)
233
+ end
234
+
235
+ # Must return the MySQL error number from the exception, if the exception has an
236
+ # error number.
237
+ def error_number(exception) # :nodoc:
238
+ raise NotImplementedError
239
+ end
240
+
241
+ # QUOTING ==================================================
242
+
243
+ def _quote(value) # :nodoc:
244
+ if value.is_a?(Type::Binary::Data)
245
+ "x'#{value.hex}'"
246
+ else
247
+ super
248
+ end
249
+ end
250
+
251
+ def quote_column_name(name) #:nodoc:
252
+ @quoted_column_names[name] ||= "`#{name.to_s.gsub('`', '``')}`"
253
+ end
254
+
255
+ def quote_table_name(name) #:nodoc:
256
+ @quoted_table_names[name] ||= quote_column_name(name).gsub('.', '`.`')
257
+ end
258
+
259
+ def quoted_true
260
+ QUOTED_TRUE
261
+ end
262
+
263
+ def unquoted_true
264
+ 1
265
+ end
266
+
267
+ def quoted_false
268
+ QUOTED_FALSE
269
+ end
270
+
271
+ def unquoted_false
272
+ 0
273
+ end
274
+
275
+ # REFERENTIAL INTEGRITY ====================================
276
+
277
+ def disable_referential_integrity #:nodoc:
278
+ old = select_value("SELECT @@FOREIGN_KEY_CHECKS")
279
+
280
+ begin
281
+ update("SET FOREIGN_KEY_CHECKS = 0")
282
+ yield
283
+ ensure
284
+ update("SET FOREIGN_KEY_CHECKS = #{old}")
285
+ end
286
+ end
287
+
288
+ #--
289
+ # DATABASE STATEMENTS ======================================
290
+ #++
291
+
292
+ def clear_cache!
293
+ super
294
+ reload_type_map
295
+ end
296
+
297
+ # Executes the SQL statement in the context of this connection.
298
+ def execute(sql, name = nil)
299
+ log(sql, name) { @connection.query(sql) }
300
+ end
301
+
302
+ # MysqlAdapter has to free a result after using it, so we use this method to write
303
+ # stuff in an abstract way without concerning ourselves about whether it needs to be
304
+ # explicitly freed or not.
305
+ def execute_and_free(sql, name = nil) #:nodoc:
306
+ yield execute(sql, name)
307
+ end
308
+
309
+ def update_sql(sql, name = nil) #:nodoc:
310
+ super
311
+ @connection.affected_rows
312
+ end
313
+
314
+ def begin_db_transaction
315
+ execute "BEGIN"
316
+ end
317
+
318
+ def begin_isolated_db_transaction(isolation)
319
+ execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
320
+ begin_db_transaction
321
+ end
322
+
323
+ def commit_db_transaction #:nodoc:
324
+ execute "COMMIT"
325
+ end
326
+
327
+ def rollback_db_transaction #:nodoc:
328
+ execute "ROLLBACK"
329
+ end
330
+
331
+ # In the simple case, MySQL allows us to place JOINs directly into the UPDATE
332
+ # query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
333
+ # these, we must use a subquery.
334
+ def join_to_update(update, select) #:nodoc:
335
+ if select.limit || select.offset || select.orders.any?
336
+ super
337
+ else
338
+ update.table select.source
339
+ update.wheres = select.constraints
340
+ end
341
+ end
342
+
343
+ def empty_insert_statement_value
344
+ "VALUES ()"
345
+ end
346
+
347
+ # SCHEMA STATEMENTS ========================================
348
+
349
+ # Drops the database specified on the +name+ attribute
350
+ # and creates it again using the provided +options+.
351
+ def recreate_database(name, options = {})
352
+ drop_database(name)
353
+ sql = create_database(name, options)
354
+ reconnect!
355
+ sql
356
+ end
357
+
358
+ # Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>.
359
+ # Charset defaults to utf8.
360
+ #
361
+ # Example:
362
+ # create_database 'charset_test', charset: 'latin1', collation: 'latin1_bin'
363
+ # create_database 'matt_development'
364
+ # create_database 'matt_development', charset: :big5
365
+ def create_database(name, options = {})
366
+ if options[:collation]
367
+ execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}` COLLATE `#{options[:collation]}`"
368
+ else
369
+ execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}`"
370
+ end
371
+ end
372
+
373
+ # Drops a MySQL database.
374
+ #
375
+ # Example:
376
+ # drop_database('sebastian_development')
377
+ def drop_database(name) #:nodoc:
378
+ execute "DROP DATABASE IF EXISTS `#{name}`"
379
+ end
380
+
381
+ def current_database
382
+ select_value 'SELECT DATABASE() as db'
383
+ end
384
+
385
+ # Returns the database character set.
386
+ def charset
387
+ show_variable 'character_set_database'
388
+ end
389
+
390
+ # Returns the database collation strategy.
391
+ def collation
392
+ show_variable 'collation_database'
393
+ end
394
+
395
+ def tables(name = nil, database = nil, like = nil) #:nodoc:
396
+ sql = "SHOW TABLES "
397
+ sql << "IN #{quote_table_name(database)} " if database
398
+ sql << "LIKE #{quote(like)}" if like
399
+
400
+ execute_and_free(sql, 'SCHEMA') do |result|
401
+ result.collect { |field| field.first }
402
+ end
403
+ end
404
+
405
+ def truncate(table_name, name = nil)
406
+ execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
407
+ end
408
+
409
+ def table_exists?(name)
410
+ return false unless name.present?
411
+ return true if tables(nil, nil, name).any?
412
+
413
+ name = name.to_s
414
+ schema, table = name.split('.', 2)
415
+
416
+ unless table # A table was provided without a schema
417
+ table = schema
418
+ schema = nil
419
+ end
420
+
421
+ tables(nil, schema, table).any?
422
+ end
423
+
424
+ # Returns an array of indexes for the given table.
425
+ def indexes(table_name, name = nil) #:nodoc:
426
+ indexes = []
427
+ current_index = nil
428
+ execute_and_free("SHOW KEYS FROM #{quote_table_name(table_name)}", 'SCHEMA') do |result|
429
+ each_hash(result) do |row|
430
+ if current_index != row[:Key_name]
431
+ next if row[:Key_name] == 'PRIMARY' # skip the primary key
432
+ current_index = row[:Key_name]
433
+
434
+ mysql_index_type = row[:Index_type].downcase.to_sym
435
+ index_type = INDEX_TYPES.include?(mysql_index_type) ? mysql_index_type : nil
436
+ index_using = INDEX_USINGS.include?(mysql_index_type) ? mysql_index_type : nil
437
+ indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique].to_i == 0, [], [], nil, nil, index_type, index_using)
438
+ end
439
+
440
+ indexes.last.columns << row[:Column_name]
441
+ indexes.last.lengths << row[:Sub_part]
442
+ end
443
+ end
444
+
445
+ indexes
446
+ end
447
+
448
+ # Returns an array of +Column+ objects for the table specified by +table_name+.
449
+ def columns(table_name)#:nodoc:
450
+ sql = "SHOW FULL FIELDS FROM #{quote_table_name(table_name)}"
451
+ execute_and_free(sql, 'SCHEMA') do |result|
452
+ each_hash(result).map do |field|
453
+ field_name = set_field_encoding(field[:Field])
454
+ sql_type = field[:Type]
455
+ cast_type = lookup_cast_type(sql_type)
456
+ new_column(field_name, field[:Default], cast_type, sql_type, field[:Null] == "YES", field[:Collation], field[:Extra])
457
+ end
458
+ end
459
+ end
460
+
461
+ def create_table(table_name, options = {}) #:nodoc:
462
+ super(table_name, options.reverse_merge(:options => "ENGINE=InnoDB"))
463
+ end
464
+
465
+ def bulk_change_table(table_name, operations) #:nodoc:
466
+ sqls = operations.flat_map do |command, args|
467
+ table, arguments = args.shift, args
468
+ method = :"#{command}_sql"
469
+
470
+ if respond_to?(method, true)
471
+ send(method, table, *arguments)
472
+ else
473
+ raise "Unknown method called : #{method}(#{arguments.inspect})"
474
+ end
475
+ end.join(", ")
476
+
477
+ execute("ALTER TABLE #{quote_table_name(table_name)} #{sqls}")
478
+ end
479
+
480
+ # Renames a table.
481
+ #
482
+ # Example:
483
+ # rename_table('octopuses', 'octopi')
484
+ def rename_table(table_name, new_name)
485
+ execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
486
+ rename_table_indexes(table_name, new_name)
487
+ end
488
+
489
+ def drop_table(table_name, options = {})
490
+ execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
491
+ end
492
+
493
+ def rename_index(table_name, old_name, new_name)
494
+ if supports_rename_index?
495
+ execute "ALTER TABLE #{quote_table_name(table_name)} RENAME INDEX #{quote_table_name(old_name)} TO #{quote_table_name(new_name)}"
496
+ else
497
+ super
498
+ end
499
+ end
500
+
501
+ def change_column_default(table_name, column_name, default) #:nodoc:
502
+ column = column_for(table_name, column_name)
503
+ change_column table_name, column_name, column.sql_type, :default => default
504
+ end
505
+
506
+ def change_column_null(table_name, column_name, null, default = nil)
507
+ column = column_for(table_name, column_name)
508
+
509
+ unless null || default.nil?
510
+ execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
511
+ end
512
+
513
+ change_column table_name, column_name, column.sql_type, :null => null
514
+ end
515
+
516
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
517
+ execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_sql(table_name, column_name, type, options)}")
518
+ end
519
+
520
+ def rename_column(table_name, column_name, new_column_name) #:nodoc:
521
+ execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_sql(table_name, column_name, new_column_name)}")
522
+ rename_column_indexes(table_name, column_name, new_column_name)
523
+ end
524
+
525
+ def add_index(table_name, column_name, options = {}) #:nodoc:
526
+ index_name, index_type, index_columns, index_options, index_algorithm, index_using = add_index_options(table_name, column_name, options)
527
+ execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options} #{index_algorithm}"
528
+ end
529
+
530
+ def foreign_keys(table_name)
531
+ fk_info = select_all <<-SQL.strip_heredoc
532
+ SELECT fk.referenced_table_name as 'to_table'
533
+ ,fk.referenced_column_name as 'primary_key'
534
+ ,fk.column_name as 'column'
535
+ ,fk.constraint_name as 'name'
536
+ FROM information_schema.key_column_usage fk
537
+ WHERE fk.referenced_column_name is not null
538
+ AND fk.table_schema = '#{@config[:database]}'
539
+ AND fk.table_name = '#{table_name}'
540
+ SQL
541
+
542
+ create_table_info = select_one("SHOW CREATE TABLE #{quote_table_name(table_name)}")["Create Table"]
543
+
544
+ fk_info.map do |row|
545
+ options = {
546
+ column: row['column'],
547
+ name: row['name'],
548
+ primary_key: row['primary_key']
549
+ }
550
+
551
+ options[:on_update] = extract_foreign_key_action(create_table_info, row['name'], "UPDATE")
552
+ options[:on_delete] = extract_foreign_key_action(create_table_info, row['name'], "DELETE")
553
+
554
+ ForeignKeyDefinition.new(table_name, row['to_table'], options)
555
+ end
556
+ end
557
+
558
+ # Maps logical Rails types to MySQL-specific data types.
559
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil)
560
+ case type.to_s
561
+ when 'binary'
562
+ case limit
563
+ when 0..0xfff; "varbinary(#{limit})"
564
+ when nil; "blob"
565
+ when 0x1000..0xffffffff; "blob(#{limit})"
566
+ else raise(ActiveRecordError, "No binary type has character length #{limit}")
567
+ end
568
+ when 'integer'
569
+ case limit
570
+ when 1; 'tinyint'
571
+ when 2; 'smallint'
572
+ when 3; 'mediumint'
573
+ when nil, 4, 11; 'int(11)' # compatibility with MySQL default
574
+ when 5..8; 'bigint'
575
+ else raise(ActiveRecordError, "No integer type has byte size #{limit}")
576
+ end
577
+ when 'text'
578
+ case limit
579
+ when 0..0xff; 'tinytext'
580
+ when nil, 0x100..0xffff; 'text'
581
+ when 0x10000..0xffffff; 'mediumtext'
582
+ when 0x1000000..0xffffffff; 'longtext'
583
+ else raise(ActiveRecordError, "No text type has character length #{limit}")
584
+ end
585
+ else
586
+ super
587
+ end
588
+ end
589
+
590
+ # SHOW VARIABLES LIKE 'name'
591
+ def show_variable(name)
592
+ variables = select_all("SHOW VARIABLES LIKE '#{name}'", 'SCHEMA')
593
+ variables.first['Value'] unless variables.empty?
594
+ end
595
+
596
+ # Returns a table's primary key and belonging sequence.
597
+ def pk_and_sequence_for(table)
598
+ execute_and_free("SHOW CREATE TABLE #{quote_table_name(table)}", 'SCHEMA') do |result|
599
+ create_table = each_hash(result).first[:"Create Table"]
600
+ if create_table.to_s =~ /PRIMARY KEY\s+(?:USING\s+\w+\s+)?\((.+)\)/
601
+ keys = $1.split(",").map { |key| key.delete('`"') }
602
+ keys.length == 1 ? [keys.first, nil] : nil
603
+ else
604
+ nil
605
+ end
606
+ end
607
+ end
608
+
609
+ # Returns just a table's primary key
610
+ def primary_key(table)
611
+ pk_and_sequence = pk_and_sequence_for(table)
612
+ pk_and_sequence && pk_and_sequence.first
613
+ end
614
+
615
+ def case_sensitive_modifier(node, table_attribute)
616
+ node = Arel::Nodes.build_quoted node, table_attribute
617
+ Arel::Nodes::Bin.new(node)
618
+ end
619
+
620
+ def case_sensitive_comparison(table, attribute, column, value)
621
+ if column.case_sensitive?
622
+ table[attribute].eq(value)
623
+ else
624
+ super
625
+ end
626
+ end
627
+
628
+ def case_insensitive_comparison(table, attribute, column, value)
629
+ if column.case_sensitive?
630
+ super
631
+ else
632
+ table[attribute].eq(value)
633
+ end
634
+ end
635
+
636
+ def strict_mode?
637
+ self.class.type_cast_config_to_boolean(@config.fetch(:strict, true))
638
+ end
639
+
640
+ def valid_type?(type)
641
+ !native_database_types[type].nil?
642
+ end
643
+
644
+ protected
645
+
646
+ def initialize_type_map(m) # :nodoc:
647
+ super
648
+
649
+ register_class_with_limit m, %r(char)i, MysqlString
650
+
651
+ m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1)
652
+ m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1)
653
+ m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1)
654
+ m.register_type %r(blob)i, Type::Binary.new(limit: 2**16 - 1)
655
+ m.register_type %r(mediumtext)i, Type::Text.new(limit: 2**24 - 1)
656
+ m.register_type %r(mediumblob)i, Type::Binary.new(limit: 2**24 - 1)
657
+ m.register_type %r(longtext)i, Type::Text.new(limit: 2**32 - 1)
658
+ m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
659
+ m.register_type %r(^float)i, Type::Float.new(limit: 24)
660
+ m.register_type %r(^double)i, Type::Float.new(limit: 53)
661
+
662
+ register_integer_type m, %r(^bigint)i, limit: 8
663
+ register_integer_type m, %r(^int)i, limit: 4
664
+ register_integer_type m, %r(^mediumint)i, limit: 3
665
+ register_integer_type m, %r(^smallint)i, limit: 2
666
+ register_integer_type m, %r(^tinyint)i, limit: 1
667
+
668
+ m.alias_type %r(tinyint\(1\))i, 'boolean' if emulate_booleans
669
+ m.alias_type %r(set)i, 'varchar'
670
+ m.alias_type %r(year)i, 'integer'
671
+ m.alias_type %r(bit)i, 'binary'
672
+
673
+ m.register_type(%r(enum)i) do |sql_type|
674
+ limit = sql_type[/^enum\((.+)\)/i, 1]
675
+ .split(',').map{|enum| enum.strip.length - 2}.max
676
+ MysqlString.new(limit: limit)
677
+ end
678
+ end
679
+
680
+ def register_integer_type(mapping, key, options) # :nodoc:
681
+ mapping.register_type(key) do |sql_type|
682
+ if /unsigned/i =~ sql_type
683
+ Type::UnsignedInteger.new(options)
684
+ else
685
+ Type::Integer.new(options)
686
+ end
687
+ end
688
+ end
689
+
690
+ # MySQL is too stupid to create a temporary table for use subquery, so we have
691
+ # to give it some prompting in the form of a subsubquery. Ugh!
692
+ def subquery_for(key, select)
693
+ subsubselect = select.clone
694
+ subsubselect.projections = [key]
695
+
696
+ subselect = Arel::SelectManager.new(select.engine)
697
+ subselect.project Arel.sql(key.name)
698
+ subselect.from subsubselect.as('__active_record_temp')
699
+ end
700
+
701
+ def add_index_length(option_strings, column_names, options = {})
702
+ if options.is_a?(Hash) && length = options[:length]
703
+ case length
704
+ when Hash
705
+ column_names.each {|name| option_strings[name] += "(#{length[name]})" if length.has_key?(name) && length[name].present?}
706
+ when Fixnum
707
+ column_names.each {|name| option_strings[name] += "(#{length})"}
708
+ end
709
+ end
710
+
711
+ return option_strings
712
+ end
713
+
714
+ def quoted_columns_for_index(column_names, options = {})
715
+ option_strings = Hash[column_names.map {|name| [name, '']}]
716
+
717
+ # add index length
718
+ option_strings = add_index_length(option_strings, column_names, options)
719
+
720
+ # add index sort order
721
+ option_strings = add_index_sort_order(option_strings, column_names, options)
722
+
723
+ column_names.map {|name| quote_column_name(name) + option_strings[name]}
724
+ end
725
+
726
+ def translate_exception(exception, message)
727
+ case error_number(exception)
728
+ when 1062
729
+ RecordNotUnique.new(message, exception)
730
+ when 1452
731
+ InvalidForeignKey.new(message, exception)
732
+ else
733
+ super
734
+ end
735
+ end
736
+
737
+ def add_column_sql(table_name, column_name, type, options = {})
738
+ td = create_table_definition table_name, options[:temporary], options[:options]
739
+ cd = td.new_column_definition(column_name, type, options)
740
+ schema_creation.visit_AddColumn cd
741
+ end
742
+
743
+ def change_column_sql(table_name, column_name, type, options = {})
744
+ column = column_for(table_name, column_name)
745
+
746
+ unless options_include_default?(options)
747
+ options[:default] = column.default
748
+ end
749
+
750
+ unless options.has_key?(:null)
751
+ options[:null] = column.null
752
+ end
753
+
754
+ options[:name] = column.name
755
+ schema_creation.accept ChangeColumnDefinition.new column, type, options
756
+ end
757
+
758
+ def rename_column_sql(table_name, column_name, new_column_name)
759
+ column = column_for(table_name, column_name)
760
+ options = {
761
+ name: new_column_name,
762
+ default: column.default,
763
+ null: column.null,
764
+ auto_increment: column.extra == "auto_increment"
765
+ }
766
+
767
+ current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'", 'SCHEMA')["Type"]
768
+ schema_creation.accept ChangeColumnDefinition.new column, current_type, options
769
+ end
770
+
771
+ def remove_column_sql(table_name, column_name, type = nil, options = {})
772
+ "DROP #{quote_column_name(column_name)}"
773
+ end
774
+
775
+ def remove_columns_sql(table_name, *column_names)
776
+ column_names.map {|column_name| remove_column_sql(table_name, column_name) }
777
+ end
778
+
779
+ def add_index_sql(table_name, column_name, options = {})
780
+ index_name, index_type, index_columns = add_index_options(table_name, column_name, options)
781
+ "ADD #{index_type} INDEX #{index_name} (#{index_columns})"
782
+ end
783
+
784
+ def remove_index_sql(table_name, options = {})
785
+ index_name = index_name_for_remove(table_name, options)
786
+ "DROP INDEX #{index_name}"
787
+ end
788
+
789
+ def add_timestamps_sql(table_name, options = {})
790
+ [add_column_sql(table_name, :created_at, :datetime, options), add_column_sql(table_name, :updated_at, :datetime, options)]
791
+ end
792
+
793
+ def remove_timestamps_sql(table_name, options = {})
794
+ [remove_column_sql(table_name, :updated_at), remove_column_sql(table_name, :created_at)]
795
+ end
796
+
797
+ private
798
+
799
+ def version
800
+ @version ||= full_version.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
801
+ end
802
+
803
+ def mariadb?
804
+ full_version =~ /mariadb/i
805
+ end
806
+
807
+ def supports_rename_index?
808
+ mariadb? ? false : (version[0] == 5 && version[1] >= 7) || version[0] >= 6
809
+ end
810
+
811
+ def configure_connection
812
+ variables = @config.fetch(:variables, {}).stringify_keys
813
+
814
+ # By default, MySQL 'where id is null' selects the last inserted id.
815
+ # Turn this off. http://dev.rubyonrails.org/ticket/6778
816
+ variables['sql_auto_is_null'] = 0
817
+
818
+ # Increase timeout so the server doesn't disconnect us.
819
+ wait_timeout = @config[:wait_timeout]
820
+ wait_timeout = 2147483 unless wait_timeout.is_a?(Fixnum)
821
+ variables['wait_timeout'] = self.class.type_cast_config_to_integer(wait_timeout)
822
+
823
+ # Make MySQL reject illegal values rather than truncating or blanking them, see
824
+ # http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html#sqlmode_strict_all_tables
825
+ # If the user has provided another value for sql_mode, don't replace it.
826
+ unless variables.has_key?('sql_mode')
827
+ variables['sql_mode'] = strict_mode? ? 'STRICT_ALL_TABLES' : ''
828
+ end
829
+
830
+ # NAMES does not have an equals sign, see
831
+ # http://dev.mysql.com/doc/refman/5.0/en/set-statement.html#id944430
832
+ # (trailing comma because variable_assignments will always have content)
833
+ if @config[:encoding]
834
+ encoding = "NAMES #{@config[:encoding]}"
835
+ encoding << " COLLATE #{@config[:collation]}" if @config[:collation]
836
+ encoding << ", "
837
+ end
838
+
839
+ # Gather up all of the SET variables...
840
+ variable_assignments = variables.map do |k, v|
841
+ if v == ':default' || v == :default
842
+ "@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default
843
+ elsif !v.nil?
844
+ "@@SESSION.#{k} = #{quote(v)}"
845
+ end
846
+ # or else nil; compact to clear nils out
847
+ end.compact.join(', ')
848
+
849
+ # ...and send them all in one query
850
+ @connection.query "SET #{encoding} #{variable_assignments}"
851
+ end
852
+
853
+ def extract_foreign_key_action(structure, name, action) # :nodoc:
854
+ if structure =~ /CONSTRAINT #{quote_column_name(name)} FOREIGN KEY .* REFERENCES .* ON #{action} (CASCADE|SET NULL|RESTRICT)/
855
+ case $1
856
+ when 'CASCADE'; :cascade
857
+ when 'SET NULL'; :nullify
858
+ end
859
+ end
860
+ end
861
+
862
+ class MysqlString < Type::String # :nodoc:
863
+ def type_cast_for_database(value)
864
+ case value
865
+ when true then "1"
866
+ when false then "0"
867
+ else super
868
+ end
869
+ end
870
+
871
+ private
872
+
873
+ def cast_value(value)
874
+ case value
875
+ when true then "1"
876
+ when false then "0"
877
+ else super
878
+ end
879
+ end
880
+ end
881
+ end
882
+ end
883
+ end