activerecord 3.2.22.4 → 4.0.13
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +2799 -617
- data/MIT-LICENSE +1 -1
- data/README.rdoc +23 -32
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +40 -34
- data/lib/active_record/association_relation.rb +22 -0
- data/lib/active_record/associations/alias_tracker.rb +4 -2
- data/lib/active_record/associations/association.rb +60 -46
- data/lib/active_record/associations/association_scope.rb +46 -40
- data/lib/active_record/associations/belongs_to_association.rb +17 -4
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
- data/lib/active_record/associations/builder/association.rb +81 -28
- data/lib/active_record/associations/builder/belongs_to.rb +73 -56
- data/lib/active_record/associations/builder/collection_association.rb +54 -40
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
- data/lib/active_record/associations/builder/has_many.rb +8 -64
- data/lib/active_record/associations/builder/has_one.rb +13 -50
- data/lib/active_record/associations/builder/singular_association.rb +13 -13
- data/lib/active_record/associations/collection_association.rb +130 -96
- data/lib/active_record/associations/collection_proxy.rb +916 -63
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +15 -13
- data/lib/active_record/associations/has_many_association.rb +35 -8
- data/lib/active_record/associations/has_many_through_association.rb +37 -17
- data/lib/active_record/associations/has_one_association.rb +42 -19
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +39 -22
- data/lib/active_record/associations/join_dependency/join_base.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_part.rb +21 -8
- data/lib/active_record/associations/join_dependency.rb +30 -9
- data/lib/active_record/associations/join_helper.rb +1 -11
- data/lib/active_record/associations/preloader/association.rb +29 -33
- data/lib/active_record/associations/preloader/collection_association.rb +1 -1
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +2 -2
- data/lib/active_record/associations/preloader/has_many_through.rb +6 -2
- data/lib/active_record/associations/preloader/has_one.rb +1 -1
- data/lib/active_record/associations/preloader/through_association.rb +13 -17
- data/lib/active_record/associations/preloader.rb +20 -43
- data/lib/active_record/associations/singular_association.rb +11 -11
- data/lib/active_record/associations/through_association.rb +3 -3
- data/lib/active_record/associations.rb +223 -282
- data/lib/active_record/attribute_assignment.rb +134 -154
- data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
- data/lib/active_record/attribute_methods/dirty.rb +36 -29
- data/lib/active_record/attribute_methods/primary_key.rb +45 -31
- data/lib/active_record/attribute_methods/query.rb +5 -4
- data/lib/active_record/attribute_methods/read.rb +67 -90
- data/lib/active_record/attribute_methods/serialization.rb +133 -70
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +51 -45
- data/lib/active_record/attribute_methods/write.rb +34 -39
- data/lib/active_record/attribute_methods.rb +268 -108
- data/lib/active_record/autosave_association.rb +80 -73
- data/lib/active_record/base.rb +54 -451
- data/lib/active_record/callbacks.rb +60 -22
- data/lib/active_record/coders/yaml_column.rb +18 -21
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +347 -197
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +146 -138
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +25 -19
- data/lib/active_record/connection_adapters/abstract/quoting.rb +19 -3
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +151 -142
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +499 -217
- data/lib/active_record/connection_adapters/abstract/transaction.rb +208 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +209 -44
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +169 -61
- data/lib/active_record/connection_adapters/column.rb +67 -36
- data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +28 -29
- data/lib/active_record/connection_adapters/mysql_adapter.rb +200 -73
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +98 -0
- data/lib/active_record/connection_adapters/postgresql/cast.rb +160 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +240 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +374 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +183 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +508 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +544 -899
- data/lib/active_record/connection_adapters/schema_cache.rb +76 -16
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +595 -16
- data/lib/active_record/connection_handling.rb +98 -0
- data/lib/active_record/core.rb +472 -0
- data/lib/active_record/counter_cache.rb +107 -108
- data/lib/active_record/dynamic_matchers.rb +115 -63
- data/lib/active_record/errors.rb +36 -18
- data/lib/active_record/explain.rb +15 -63
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +8 -4
- data/lib/active_record/fixture_set/file.rb +55 -0
- data/lib/active_record/fixtures.rb +159 -155
- data/lib/active_record/inheritance.rb +93 -59
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/locale/en.yml +8 -1
- data/lib/active_record/locking/optimistic.rb +39 -43
- data/lib/active_record/locking/pessimistic.rb +4 -4
- data/lib/active_record/log_subscriber.rb +19 -9
- data/lib/active_record/migration/command_recorder.rb +102 -33
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/migration.rb +411 -173
- data/lib/active_record/model_schema.rb +81 -94
- data/lib/active_record/nested_attributes.rb +173 -131
- data/lib/active_record/null_relation.rb +67 -0
- data/lib/active_record/persistence.rb +254 -106
- data/lib/active_record/query_cache.rb +18 -36
- data/lib/active_record/querying.rb +19 -15
- data/lib/active_record/railtie.rb +113 -38
- data/lib/active_record/railties/console_sandbox.rb +3 -4
- data/lib/active_record/railties/controller_runtime.rb +4 -3
- data/lib/active_record/railties/databases.rake +115 -368
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +7 -3
- data/lib/active_record/reflection.rb +110 -61
- data/lib/active_record/relation/batches.rb +29 -29
- data/lib/active_record/relation/calculations.rb +155 -125
- data/lib/active_record/relation/delegation.rb +94 -18
- data/lib/active_record/relation/finder_methods.rb +151 -203
- data/lib/active_record/relation/merger.rb +188 -0
- data/lib/active_record/relation/predicate_builder.rb +85 -42
- data/lib/active_record/relation/query_methods.rb +793 -146
- data/lib/active_record/relation/spawn_methods.rb +43 -150
- data/lib/active_record/relation.rb +293 -173
- data/lib/active_record/result.rb +48 -7
- data/lib/active_record/runtime_registry.rb +17 -0
- data/lib/active_record/sanitization.rb +41 -54
- data/lib/active_record/schema.rb +19 -12
- data/lib/active_record/schema_dumper.rb +41 -41
- data/lib/active_record/schema_migration.rb +46 -0
- data/lib/active_record/scoping/default.rb +56 -52
- data/lib/active_record/scoping/named.rb +78 -103
- data/lib/active_record/scoping.rb +54 -124
- data/lib/active_record/serialization.rb +6 -2
- data/lib/active_record/serializers/xml_serializer.rb +9 -15
- data/lib/active_record/statement_cache.rb +26 -0
- data/lib/active_record/store.rb +131 -15
- data/lib/active_record/tasks/database_tasks.rb +204 -0
- data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +144 -0
- data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
- data/lib/active_record/test_case.rb +67 -38
- data/lib/active_record/timestamp.rb +16 -11
- data/lib/active_record/transactions.rb +73 -51
- data/lib/active_record/validations/associated.rb +19 -13
- data/lib/active_record/validations/presence.rb +65 -0
- data/lib/active_record/validations/uniqueness.rb +110 -57
- data/lib/active_record/validations.rb +18 -17
- data/lib/active_record/version.rb +7 -6
- data/lib/active_record.rb +63 -45
- data/lib/rails/generators/active_record/migration/migration_generator.rb +45 -8
- data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +4 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
- data/lib/rails/generators/active_record/model/model_generator.rb +5 -4
- data/lib/rails/generators/active_record/model/templates/model.rb +4 -6
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- data/lib/rails/generators/active_record.rb +3 -5
- metadata +43 -29
- data/examples/associations.png +0 -0
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
- data/lib/active_record/dynamic_finder_match.rb +0 -68
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/fixtures/file.rb +0 -65
- data/lib/active_record/identity_map.rb +0 -162
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/session_store.rb +0 -360
- data/lib/rails/generators/active_record/migration.rb +0 -15
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,21 +1,43 @@
|
|
1
|
-
require 'active_support/core_ext/object/blank'
|
2
1
|
require 'arel/visitors/bind_visitor'
|
3
2
|
|
4
3
|
module ActiveRecord
|
5
4
|
module ConnectionAdapters
|
6
5
|
class AbstractMysqlAdapter < AbstractAdapter
|
6
|
+
class SchemaCreation < AbstractAdapter::SchemaCreation
|
7
|
+
private
|
8
|
+
|
9
|
+
def visit_AddColumn(o)
|
10
|
+
add_column_position!(super, o)
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_column_position!(sql, column)
|
14
|
+
if column.first
|
15
|
+
sql << " FIRST"
|
16
|
+
elsif column.after
|
17
|
+
sql << " AFTER #{quote_column_name(column.after)}"
|
18
|
+
end
|
19
|
+
sql
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def schema_creation
|
24
|
+
SchemaCreation.new self
|
25
|
+
end
|
26
|
+
|
7
27
|
class Column < ConnectionAdapters::Column # :nodoc:
|
8
|
-
attr_reader :collation
|
28
|
+
attr_reader :collation, :strict, :extra
|
9
29
|
|
10
|
-
def initialize(name, default, sql_type = nil, null = true, collation = nil)
|
11
|
-
|
30
|
+
def initialize(name, default, sql_type = nil, null = true, collation = nil, strict = false, extra = "")
|
31
|
+
@strict = strict
|
12
32
|
@collation = collation
|
33
|
+
@extra = extra
|
34
|
+
super(name, default, sql_type, null)
|
13
35
|
end
|
14
36
|
|
15
37
|
def extract_default(default)
|
16
|
-
if
|
38
|
+
if blob_or_text_column?
|
17
39
|
if default.blank?
|
18
|
-
|
40
|
+
null || strict ? nil : ''
|
19
41
|
else
|
20
42
|
raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
|
21
43
|
end
|
@@ -27,10 +49,14 @@ module ActiveRecord
|
|
27
49
|
end
|
28
50
|
|
29
51
|
def has_default?
|
30
|
-
return false if
|
52
|
+
return false if blob_or_text_column? #mysql forbids defaults on blob and text columns
|
31
53
|
super
|
32
54
|
end
|
33
55
|
|
56
|
+
def blob_or_text_column?
|
57
|
+
sql_type =~ /blob/i || type == :text
|
58
|
+
end
|
59
|
+
|
34
60
|
# Must return the relevant concrete adapter
|
35
61
|
def adapter
|
36
62
|
raise NotImplementedError
|
@@ -56,6 +82,8 @@ module ActiveRecord
|
|
56
82
|
|
57
83
|
def extract_limit(sql_type)
|
58
84
|
case sql_type
|
85
|
+
when /^enum\((.+)\)/i
|
86
|
+
$1.split(',').map{|enum| enum.strip.length - 2}.max
|
59
87
|
when /blob|text/i
|
60
88
|
case sql_type
|
61
89
|
when /tiny/i
|
@@ -72,8 +100,6 @@ module ActiveRecord
|
|
72
100
|
when /^mediumint/i; 3
|
73
101
|
when /^smallint/i; 2
|
74
102
|
when /^tinyint/i; 1
|
75
|
-
when /^enum\((.+)\)/i
|
76
|
-
$1.split(',').map{|enum| enum.strip.length - 2}.max
|
77
103
|
else
|
78
104
|
super
|
79
105
|
end
|
@@ -125,6 +151,9 @@ module ActiveRecord
|
|
125
151
|
:boolean => { :name => "tinyint", :limit => 1 }
|
126
152
|
}
|
127
153
|
|
154
|
+
INDEX_TYPES = [:fulltext, :spatial]
|
155
|
+
INDEX_USINGS = [:btree, :hash]
|
156
|
+
|
128
157
|
class BindSubstitution < Arel::Visitors::MySQL # :nodoc:
|
129
158
|
include Arel::Visitors::BindVisitor
|
130
159
|
end
|
@@ -135,10 +164,11 @@ module ActiveRecord
|
|
135
164
|
@connection_options, @config = connection_options, config
|
136
165
|
@quoted_column_names, @quoted_table_names = {}, {}
|
137
166
|
|
138
|
-
if config.fetch(:prepared_statements) { true }
|
167
|
+
if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
|
168
|
+
@prepared_statements = true
|
139
169
|
@visitor = Arel::Visitors::MySQL.new self
|
140
170
|
else
|
141
|
-
@visitor =
|
171
|
+
@visitor = unprepared_visitor
|
142
172
|
end
|
143
173
|
end
|
144
174
|
|
@@ -170,10 +200,22 @@ module ActiveRecord
|
|
170
200
|
true
|
171
201
|
end
|
172
202
|
|
203
|
+
# MySQL 4 technically support transaction isolation, but it is affected by a bug
|
204
|
+
# where the transaction level gets persisted for the whole session:
|
205
|
+
#
|
206
|
+
# http://bugs.mysql.com/bug.php?id=39170
|
207
|
+
def supports_transaction_isolation?
|
208
|
+
version[0] >= 5
|
209
|
+
end
|
210
|
+
|
173
211
|
def native_database_types
|
174
212
|
NATIVE_DATABASE_TYPES
|
175
213
|
end
|
176
214
|
|
215
|
+
def index_algorithms
|
216
|
+
{ default: 'ALGORITHM = DEFAULT', copy: 'ALGORITHM = COPY', inplace: 'ALGORITHM = INPLACE' }
|
217
|
+
end
|
218
|
+
|
177
219
|
# HELPER METHODS ===========================================
|
178
220
|
|
179
221
|
# The two drivers have slightly different ways of yielding hashes of results, so
|
@@ -183,8 +225,8 @@ module ActiveRecord
|
|
183
225
|
end
|
184
226
|
|
185
227
|
# Overridden by the adapters to instantiate their specific Column type.
|
186
|
-
def new_column(field, default, type, null, collation) # :nodoc:
|
187
|
-
Column.new(field, default, type, null, collation)
|
228
|
+
def new_column(field, default, type, null, collation, extra = "") # :nodoc:
|
229
|
+
Column.new(field, default, type, null, collation, extra)
|
188
230
|
end
|
189
231
|
|
190
232
|
# Must return the Mysql error number from the exception, if the exception has an
|
@@ -246,14 +288,14 @@ module ActiveRecord
|
|
246
288
|
end
|
247
289
|
rescue ActiveRecord::StatementInvalid => exception
|
248
290
|
if exception.message.split(":").first =~ /Packets out of order/
|
249
|
-
raise ActiveRecord::StatementInvalid
|
291
|
+
raise ActiveRecord::StatementInvalid.new("'Packets out of order' error was received from the database. Please update your mysql bindings (gem install mysql) and read http://dev.mysql.com/doc/mysql/en/password-hashing.html for more information. If you're on Windows, use the Instant Rails installer to get the updated mysql bindings.", exception.original_exception)
|
250
292
|
else
|
251
293
|
raise
|
252
294
|
end
|
253
295
|
end
|
254
296
|
|
255
297
|
# MysqlAdapter has to free a result after using it, so we use this method to write
|
256
|
-
# stuff in
|
298
|
+
# stuff in an abstract way without concerning ourselves about whether it needs to be
|
257
299
|
# explicitly freed or not.
|
258
300
|
def execute_and_free(sql, name = nil) #:nodoc:
|
259
301
|
yield execute(sql, name)
|
@@ -266,19 +308,26 @@ module ActiveRecord
|
|
266
308
|
|
267
309
|
def begin_db_transaction
|
268
310
|
execute "BEGIN"
|
269
|
-
rescue
|
311
|
+
rescue
|
312
|
+
# Transactions aren't supported
|
313
|
+
end
|
314
|
+
|
315
|
+
def begin_isolated_db_transaction(isolation)
|
316
|
+
execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
|
317
|
+
begin_db_transaction
|
318
|
+
rescue
|
270
319
|
# Transactions aren't supported
|
271
320
|
end
|
272
321
|
|
273
322
|
def commit_db_transaction #:nodoc:
|
274
323
|
execute "COMMIT"
|
275
|
-
rescue
|
324
|
+
rescue
|
276
325
|
# Transactions aren't supported
|
277
326
|
end
|
278
327
|
|
279
328
|
def rollback_db_transaction #:nodoc:
|
280
329
|
execute "ROLLBACK"
|
281
|
-
rescue
|
330
|
+
rescue
|
282
331
|
# Transactions aren't supported
|
283
332
|
end
|
284
333
|
|
@@ -296,55 +345,38 @@ module ActiveRecord
|
|
296
345
|
|
297
346
|
# In the simple case, MySQL allows us to place JOINs directly into the UPDATE
|
298
347
|
# query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
|
299
|
-
# these, we must use a subquery.
|
300
|
-
# temporary table for this automatically, so we have to give it some prompting
|
301
|
-
# in the form of a subsubquery. Ugh!
|
348
|
+
# these, we must use a subquery.
|
302
349
|
def join_to_update(update, select) #:nodoc:
|
303
350
|
if select.limit || select.offset || select.orders.any?
|
304
|
-
|
305
|
-
subsubselect.projections = [update.key]
|
306
|
-
|
307
|
-
subselect = Arel::SelectManager.new(select.engine)
|
308
|
-
subselect.project Arel.sql(update.key.name)
|
309
|
-
subselect.from subsubselect.as('__active_record_temp')
|
310
|
-
|
311
|
-
update.where update.key.in(subselect)
|
351
|
+
super
|
312
352
|
else
|
313
353
|
update.table select.source
|
314
354
|
update.wheres = select.constraints
|
315
355
|
end
|
316
356
|
end
|
317
357
|
|
318
|
-
|
319
|
-
|
320
|
-
def structure_dump #:nodoc:
|
321
|
-
if supports_views?
|
322
|
-
sql = "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"
|
323
|
-
else
|
324
|
-
sql = "SHOW TABLES"
|
325
|
-
end
|
326
|
-
|
327
|
-
select_all(sql).map { |table|
|
328
|
-
table.delete('Table_type')
|
329
|
-
sql = "SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}"
|
330
|
-
exec_query(sql).first['Create Table'] + ";\n\n"
|
331
|
-
}.join
|
358
|
+
def empty_insert_statement_value
|
359
|
+
"VALUES ()"
|
332
360
|
end
|
333
361
|
|
362
|
+
# SCHEMA STATEMENTS ========================================
|
363
|
+
|
334
364
|
# Drops the database specified on the +name+ attribute
|
335
365
|
# and creates it again using the provided +options+.
|
336
366
|
def recreate_database(name, options = {})
|
337
367
|
drop_database(name)
|
338
|
-
create_database(name, options)
|
368
|
+
sql = create_database(name, options)
|
369
|
+
reconnect!
|
370
|
+
sql
|
339
371
|
end
|
340
372
|
|
341
373
|
# Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>.
|
342
374
|
# Charset defaults to utf8.
|
343
375
|
#
|
344
376
|
# Example:
|
345
|
-
# create_database 'charset_test', :
|
377
|
+
# create_database 'charset_test', charset: 'latin1', collation: 'latin1_bin'
|
346
378
|
# create_database 'matt_development'
|
347
|
-
# create_database 'matt_development', :
|
379
|
+
# create_database 'matt_development', charset: :big5
|
348
380
|
def create_database(name, options = {})
|
349
381
|
if options[:collation]
|
350
382
|
execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}` COLLATE `#{options[:collation]}`"
|
@@ -409,7 +441,11 @@ module ActiveRecord
|
|
409
441
|
if current_index != row[:Key_name]
|
410
442
|
next if row[:Key_name] == 'PRIMARY' # skip the primary key
|
411
443
|
current_index = row[:Key_name]
|
412
|
-
|
444
|
+
|
445
|
+
mysql_index_type = row[:Index_type].downcase.to_sym
|
446
|
+
index_type = INDEX_TYPES.include?(mysql_index_type) ? mysql_index_type : nil
|
447
|
+
index_using = INDEX_USINGS.include?(mysql_index_type) ? mysql_index_type : nil
|
448
|
+
indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique].to_i == 0, [], [], nil, nil, index_type, index_using)
|
413
449
|
end
|
414
450
|
|
415
451
|
indexes.last.columns << row[:Column_name]
|
@@ -421,11 +457,12 @@ module ActiveRecord
|
|
421
457
|
end
|
422
458
|
|
423
459
|
# Returns an array of +Column+ objects for the table specified by +table_name+.
|
424
|
-
def columns(table_name
|
460
|
+
def columns(table_name)#:nodoc:
|
425
461
|
sql = "SHOW FULL FIELDS FROM #{quote_table_name(table_name)}"
|
426
462
|
execute_and_free(sql, 'SCHEMA') do |result|
|
427
463
|
each_hash(result).map do |field|
|
428
|
-
|
464
|
+
field_name = set_field_encoding(field[:Field])
|
465
|
+
new_column(field_name, field[:Default], field[:Type], field[:Null] == "YES", field[:Collation], field[:Extra])
|
429
466
|
end
|
430
467
|
end
|
431
468
|
end
|
@@ -455,10 +492,7 @@ module ActiveRecord
|
|
455
492
|
# rename_table('octopuses', 'octopi')
|
456
493
|
def rename_table(table_name, new_name)
|
457
494
|
execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
|
458
|
-
|
459
|
-
|
460
|
-
def add_column(table_name, column_name, type, options = {})
|
461
|
-
execute("ALTER TABLE #{quote_table_name(table_name)} #{add_column_sql(table_name, column_name, type, options)}")
|
495
|
+
rename_table_indexes(table_name, new_name)
|
462
496
|
end
|
463
497
|
|
464
498
|
def change_column_default(table_name, column_name, default)
|
@@ -482,11 +516,24 @@ module ActiveRecord
|
|
482
516
|
|
483
517
|
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
484
518
|
execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_sql(table_name, column_name, new_column_name)}")
|
519
|
+
rename_column_indexes(table_name, column_name, new_column_name)
|
520
|
+
end
|
521
|
+
|
522
|
+
def add_index(table_name, column_name, options = {}) #:nodoc:
|
523
|
+
index_name, index_type, index_columns, index_options, index_algorithm, index_using = add_index_options(table_name, column_name, options)
|
524
|
+
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} #{index_using} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options} #{index_algorithm}"
|
485
525
|
end
|
486
526
|
|
487
527
|
# Maps logical Rails types to MySQL-specific data types.
|
488
528
|
def type_to_sql(type, limit = nil, precision = nil, scale = nil)
|
489
529
|
case type.to_s
|
530
|
+
when 'binary'
|
531
|
+
case limit
|
532
|
+
when 0..0xfff; "varbinary(#{limit})"
|
533
|
+
when nil; "blob"
|
534
|
+
when 0x1000..0xffffffff; "blob(#{limit})"
|
535
|
+
else raise(ActiveRecordError, "No binary type has character length #{limit}")
|
536
|
+
end
|
490
537
|
when 'integer'
|
491
538
|
case limit
|
492
539
|
when 1; 'tinyint'
|
@@ -519,7 +566,7 @@ module ActiveRecord
|
|
519
566
|
|
520
567
|
# SHOW VARIABLES LIKE 'name'
|
521
568
|
def show_variable(name)
|
522
|
-
variables = select_all("SHOW VARIABLES LIKE '#{name}'")
|
569
|
+
variables = select_all("SHOW VARIABLES LIKE '#{name}'", 'SCHEMA')
|
523
570
|
variables.first['Value'] unless variables.empty?
|
524
571
|
end
|
525
572
|
|
@@ -528,7 +575,7 @@ module ActiveRecord
|
|
528
575
|
execute_and_free("SHOW CREATE TABLE #{quote_table_name(table)}", 'SCHEMA') do |result|
|
529
576
|
create_table = each_hash(result).first[:"Create Table"]
|
530
577
|
if create_table.to_s =~ /PRIMARY KEY\s+(?:USING\s+\w+\s+)?\((.+)\)/
|
531
|
-
keys = $1.split(",").map { |key| key.
|
578
|
+
keys = $1.split(",").map { |key| key.delete('`"') }
|
532
579
|
keys.length == 1 ? [keys.first, nil] : nil
|
533
580
|
else
|
534
581
|
nil
|
@@ -558,8 +605,27 @@ module ActiveRecord
|
|
558
605
|
where_sql
|
559
606
|
end
|
560
607
|
|
608
|
+
def strict_mode?
|
609
|
+
self.class.type_cast_config_to_boolean(@config.fetch(:strict, true))
|
610
|
+
end
|
611
|
+
|
612
|
+
def valid_type?(type)
|
613
|
+
!native_database_types[type].nil?
|
614
|
+
end
|
615
|
+
|
561
616
|
protected
|
562
617
|
|
618
|
+
# MySQL is too stupid to create a temporary table for use subquery, so we have
|
619
|
+
# to give it some prompting in the form of a subsubquery. Ugh!
|
620
|
+
def subquery_for(key, select)
|
621
|
+
subsubselect = select.clone
|
622
|
+
subsubselect.projections = [key]
|
623
|
+
|
624
|
+
subselect = Arel::SelectManager.new(select.engine)
|
625
|
+
subselect.project Arel.sql(key.name)
|
626
|
+
subselect.from subsubselect.as('__active_record_temp')
|
627
|
+
end
|
628
|
+
|
563
629
|
def add_index_length(option_strings, column_names, options = {})
|
564
630
|
if options.is_a?(Hash) && length = options[:length]
|
565
631
|
case length
|
@@ -626,20 +692,24 @@ module ActiveRecord
|
|
626
692
|
if column = columns(table_name).find { |c| c.name == column_name.to_s }
|
627
693
|
options[:default] = column.default
|
628
694
|
options[:null] = column.null
|
695
|
+
options[:auto_increment] = (column.extra == "auto_increment")
|
629
696
|
else
|
630
697
|
raise ActiveRecordError, "No such column: #{table_name}.#{column_name}"
|
631
698
|
end
|
632
699
|
|
633
|
-
current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
|
700
|
+
current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'", 'SCHEMA')["Type"]
|
634
701
|
rename_column_sql = "CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
|
635
702
|
add_column_options!(rename_column_sql, options)
|
636
703
|
rename_column_sql
|
637
704
|
end
|
638
705
|
|
639
|
-
def remove_column_sql(table_name,
|
640
|
-
|
706
|
+
def remove_column_sql(table_name, column_name, type = nil, options = {})
|
707
|
+
"DROP #{quote_column_name(column_name)}"
|
708
|
+
end
|
709
|
+
|
710
|
+
def remove_columns_sql(table_name, *column_names)
|
711
|
+
column_names.map {|column_name| remove_column_sql(table_name, column_name) }
|
641
712
|
end
|
642
|
-
alias :remove_columns_sql :remove_column
|
643
713
|
|
644
714
|
def add_index_sql(table_name, column_name, options = {})
|
645
715
|
index_name, index_type, index_columns = add_index_options(table_name, column_name, options)
|
@@ -651,8 +721,8 @@ module ActiveRecord
|
|
651
721
|
"DROP INDEX #{index_name}"
|
652
722
|
end
|
653
723
|
|
654
|
-
def add_timestamps_sql(table_name)
|
655
|
-
[add_column_sql(table_name, :created_at, :datetime), add_column_sql(table_name, :updated_at, :datetime)]
|
724
|
+
def add_timestamps_sql(table_name, options = {})
|
725
|
+
[add_column_sql(table_name, :created_at, :datetime, options), add_column_sql(table_name, :updated_at, :datetime, options)]
|
656
726
|
end
|
657
727
|
|
658
728
|
def remove_timestamps_sql(table_name)
|
@@ -671,6 +741,44 @@ module ActiveRecord
|
|
671
741
|
end
|
672
742
|
column
|
673
743
|
end
|
744
|
+
|
745
|
+
def configure_connection
|
746
|
+
variables = @config.fetch(:variables, {}).stringify_keys
|
747
|
+
|
748
|
+
# By default, MySQL 'where id is null' selects the last inserted id.
|
749
|
+
# Turn this off. http://dev.rubyonrails.org/ticket/6778
|
750
|
+
variables['sql_auto_is_null'] = 0
|
751
|
+
|
752
|
+
# Increase timeout so the server doesn't disconnect us.
|
753
|
+
wait_timeout = @config[:wait_timeout]
|
754
|
+
wait_timeout = 2147483 unless wait_timeout.is_a?(Fixnum)
|
755
|
+
variables['wait_timeout'] = self.class.type_cast_config_to_integer(wait_timeout)
|
756
|
+
|
757
|
+
# Make MySQL reject illegal values rather than truncating or blanking them, see
|
758
|
+
# http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html#sqlmode_strict_all_tables
|
759
|
+
# If the user has provided another value for sql_mode, don't replace it.
|
760
|
+
unless variables.has_key?('sql_mode')
|
761
|
+
variables['sql_mode'] = strict_mode? ? 'STRICT_ALL_TABLES' : ''
|
762
|
+
end
|
763
|
+
|
764
|
+
# NAMES does not have an equals sign, see
|
765
|
+
# http://dev.mysql.com/doc/refman/5.0/en/set-statement.html#id944430
|
766
|
+
# (trailing comma because variable_assignments will always have content)
|
767
|
+
encoding = "NAMES #{@config[:encoding]}, " if @config[:encoding]
|
768
|
+
|
769
|
+
# Gather up all of the SET variables...
|
770
|
+
variable_assignments = variables.map do |k, v|
|
771
|
+
if v == ':default' || v == :default
|
772
|
+
"@@SESSION.#{k.to_s} = DEFAULT" # Sets the value to the global or compile default
|
773
|
+
elsif !v.nil?
|
774
|
+
"@@SESSION.#{k.to_s} = #{quote(v)}"
|
775
|
+
end
|
776
|
+
# or else nil; compact to clear nils out
|
777
|
+
end.compact.join(', ')
|
778
|
+
|
779
|
+
# ...and send them all in one query
|
780
|
+
execute("SET #{encoding} #{variable_assignments}", :skip_logging)
|
781
|
+
end
|
674
782
|
end
|
675
783
|
end
|
676
784
|
end
|
@@ -13,7 +13,7 @@ module ActiveRecord
|
|
13
13
|
ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/
|
14
14
|
end
|
15
15
|
|
16
|
-
attr_reader :name, :default, :type, :limit, :null, :sql_type, :precision, :scale
|
16
|
+
attr_reader :name, :default, :type, :limit, :null, :sql_type, :precision, :scale, :default_function
|
17
17
|
attr_accessor :primary, :coder
|
18
18
|
|
19
19
|
alias :encoded? :coder
|
@@ -27,16 +27,17 @@ module ActiveRecord
|
|
27
27
|
# It will be mapped to one of the standard Rails SQL types in the <tt>type</tt> attribute.
|
28
28
|
# +null+ determines if this column allows +NULL+ values.
|
29
29
|
def initialize(name, default, sql_type = nil, null = true)
|
30
|
-
@name
|
31
|
-
@sql_type
|
32
|
-
@null
|
33
|
-
@limit
|
34
|
-
@precision
|
35
|
-
@scale
|
36
|
-
@type
|
37
|
-
@default
|
38
|
-
@
|
39
|
-
@
|
30
|
+
@name = name
|
31
|
+
@sql_type = sql_type
|
32
|
+
@null = null
|
33
|
+
@limit = extract_limit(sql_type)
|
34
|
+
@precision = extract_precision(sql_type)
|
35
|
+
@scale = extract_scale(sql_type)
|
36
|
+
@type = simplified_type(sql_type)
|
37
|
+
@default = extract_default(default)
|
38
|
+
@default_function = nil
|
39
|
+
@primary = nil
|
40
|
+
@coder = nil
|
40
41
|
end
|
41
42
|
|
42
43
|
# Returns +true+ if the column is either of type string or text.
|
@@ -66,6 +67,26 @@ module ActiveRecord
|
|
66
67
|
end
|
67
68
|
end
|
68
69
|
|
70
|
+
def binary?
|
71
|
+
type == :binary
|
72
|
+
end
|
73
|
+
|
74
|
+
# Casts a Ruby value to something appropriate for writing to the database.
|
75
|
+
def type_cast_for_write(value)
|
76
|
+
return value unless number?
|
77
|
+
|
78
|
+
case value
|
79
|
+
when FalseClass
|
80
|
+
0
|
81
|
+
when TrueClass
|
82
|
+
1
|
83
|
+
when String
|
84
|
+
value.presence
|
85
|
+
else
|
86
|
+
value
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
69
90
|
# Casts value (which is a String) to an appropriate instance.
|
70
91
|
def type_cast(value)
|
71
92
|
return nil if value.nil?
|
@@ -80,7 +101,7 @@ module ActiveRecord
|
|
80
101
|
when :decimal then klass.value_to_decimal(value)
|
81
102
|
when :datetime, :timestamp then klass.string_to_time(value)
|
82
103
|
when :time then klass.string_to_dummy_time(value)
|
83
|
-
when :date then klass.
|
104
|
+
when :date then klass.value_to_date(value)
|
84
105
|
when :binary then klass.binary_to_string(value)
|
85
106
|
when :boolean then klass.value_to_boolean(value)
|
86
107
|
else value
|
@@ -88,6 +109,10 @@ module ActiveRecord
|
|
88
109
|
end
|
89
110
|
|
90
111
|
def type_cast_code(var_name)
|
112
|
+
message = "Column#type_cast_code is deprecated in favor of using Column#type_cast only, " \
|
113
|
+
"and it is going to be removed in future Rails versions."
|
114
|
+
ActiveSupport::Deprecation.warn message
|
115
|
+
|
91
116
|
klass = self.class.name
|
92
117
|
|
93
118
|
case type
|
@@ -97,9 +122,12 @@ module ActiveRecord
|
|
97
122
|
when :decimal then "#{klass}.value_to_decimal(#{var_name})"
|
98
123
|
when :datetime, :timestamp then "#{klass}.string_to_time(#{var_name})"
|
99
124
|
when :time then "#{klass}.string_to_dummy_time(#{var_name})"
|
100
|
-
when :date then "#{klass}.
|
125
|
+
when :date then "#{klass}.value_to_date(#{var_name})"
|
101
126
|
when :binary then "#{klass}.binary_to_string(#{var_name})"
|
102
127
|
when :boolean then "#{klass}.value_to_boolean(#{var_name})"
|
128
|
+
when :hstore then "#{klass}.string_to_hstore(#{var_name})"
|
129
|
+
when :inet, :cidr then "#{klass}.string_to_cidr(#{var_name})"
|
130
|
+
when :json then "#{klass}.string_to_json(#{var_name})"
|
103
131
|
else var_name
|
104
132
|
end
|
105
133
|
end
|
@@ -132,11 +160,15 @@ module ActiveRecord
|
|
132
160
|
value
|
133
161
|
end
|
134
162
|
|
135
|
-
def
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
163
|
+
def value_to_date(value)
|
164
|
+
if value.is_a?(String)
|
165
|
+
return nil if value.empty?
|
166
|
+
fast_string_to_date(value) || fallback_string_to_date(value)
|
167
|
+
elsif value.respond_to?(:to_date)
|
168
|
+
value.to_date
|
169
|
+
else
|
170
|
+
value
|
171
|
+
end
|
140
172
|
end
|
141
173
|
|
142
174
|
def string_to_time(string)
|
@@ -161,7 +193,7 @@ module ActiveRecord
|
|
161
193
|
|
162
194
|
# convert something to a boolean
|
163
195
|
def value_to_boolean(value)
|
164
|
-
if value.is_a?(String) && value.
|
196
|
+
if value.is_a?(String) && value.empty?
|
165
197
|
nil
|
166
198
|
else
|
167
199
|
TRUE_VALUES.include?(value)
|
@@ -206,11 +238,19 @@ module ActiveRecord
|
|
206
238
|
end
|
207
239
|
end
|
208
240
|
|
209
|
-
def new_time(year, mon, mday, hour, min, sec, microsec)
|
241
|
+
def new_time(year, mon, mday, hour, min, sec, microsec, offset = nil)
|
210
242
|
# Treat 0000-00-00 00:00:00 as nil.
|
211
243
|
return nil if year.nil? || (year == 0 && mon == 0 && mday == 0)
|
212
244
|
|
213
|
-
|
245
|
+
if offset
|
246
|
+
time = Time.utc(year, mon, mday, hour, min, sec, microsec) rescue nil
|
247
|
+
return nil unless time
|
248
|
+
|
249
|
+
time -= offset
|
250
|
+
Base.default_timezone == :utc ? time : time.getlocal
|
251
|
+
else
|
252
|
+
Time.public_send(Base.default_timezone, year, mon, mday, hour, min, sec, microsec) rescue nil
|
253
|
+
end
|
214
254
|
end
|
215
255
|
|
216
256
|
def fast_string_to_date(string)
|
@@ -219,20 +259,11 @@ module ActiveRecord
|
|
219
259
|
end
|
220
260
|
end
|
221
261
|
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
|
228
|
-
end
|
229
|
-
end
|
230
|
-
else
|
231
|
-
def fast_string_to_time(string)
|
232
|
-
if string =~ Format::ISO_DATETIME
|
233
|
-
microsec = ($7.to_f * 1_000_000).round.to_i
|
234
|
-
new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
|
235
|
-
end
|
262
|
+
# Doesn't handle time zones.
|
263
|
+
def fast_string_to_time(string)
|
264
|
+
if string =~ Format::ISO_DATETIME
|
265
|
+
microsec = ($7.to_r * 1_000_000).to_i
|
266
|
+
new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
|
236
267
|
end
|
237
268
|
end
|
238
269
|
|
@@ -244,7 +275,7 @@ module ActiveRecord
|
|
244
275
|
time_hash = Date._parse(string)
|
245
276
|
time_hash[:sec_fraction] = microseconds(time_hash)
|
246
277
|
|
247
|
-
new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction))
|
278
|
+
new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction, :offset))
|
248
279
|
end
|
249
280
|
end
|
250
281
|
|