activerecord 3.2.22.5 → 4.0.0.beta1
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 +1024 -543
- data/MIT-LICENSE +1 -1
- data/README.rdoc +20 -29
- data/examples/performance.rb +1 -1
- data/lib/active_record.rb +55 -44
- data/lib/active_record/aggregations.rb +40 -34
- data/lib/active_record/associations.rb +204 -276
- data/lib/active_record/associations/alias_tracker.rb +1 -1
- data/lib/active_record/associations/association.rb +30 -35
- data/lib/active_record/associations/association_scope.rb +40 -40
- data/lib/active_record/associations/belongs_to_association.rb +15 -2
- data/lib/active_record/associations/builder/association.rb +81 -28
- data/lib/active_record/associations/builder/belongs_to.rb +35 -57
- 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 +92 -88
- data/lib/active_record/associations/collection_proxy.rb +913 -63
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +12 -10
- data/lib/active_record/associations/has_many_association.rb +35 -9
- data/lib/active_record/associations/has_many_through_association.rb +24 -14
- data/lib/active_record/associations/has_one_association.rb +33 -13
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_association.rb +17 -22
- data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
- data/lib/active_record/associations/join_helper.rb +1 -11
- data/lib/active_record/associations/preloader.rb +14 -17
- 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 +1 -1
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- 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/singular_association.rb +11 -11
- data/lib/active_record/associations/through_association.rb +2 -2
- data/lib/active_record/attribute_assignment.rb +133 -153
- data/lib/active_record/attribute_methods.rb +196 -93
- data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
- data/lib/active_record/attribute_methods/dirty.rb +31 -28
- data/lib/active_record/attribute_methods/primary_key.rb +38 -30
- data/lib/active_record/attribute_methods/query.rb +5 -4
- data/lib/active_record/attribute_methods/read.rb +62 -91
- data/lib/active_record/attribute_methods/serialization.rb +97 -66
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -45
- data/lib/active_record/attribute_methods/write.rb +32 -39
- data/lib/active_record/autosave_association.rb +56 -70
- data/lib/active_record/base.rb +53 -450
- data/lib/active_record/callbacks.rb +53 -18
- data/lib/active_record/coders/yaml_column.rb +11 -9
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +353 -197
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -131
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -19
- data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -3
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +101 -91
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +59 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +225 -96
- data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +99 -46
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +114 -36
- data/lib/active_record/connection_adapters/column.rb +46 -24
- data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
- data/lib/active_record/connection_adapters/mysql_adapter.rb +181 -64
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/cast.rb +132 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +347 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +158 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +448 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +454 -885
- data/lib/active_record/connection_adapters/schema_cache.rb +48 -16
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +574 -13
- data/lib/active_record/connection_handling.rb +98 -0
- data/lib/active_record/core.rb +428 -0
- data/lib/active_record/counter_cache.rb +106 -108
- data/lib/active_record/dynamic_matchers.rb +110 -63
- data/lib/active_record/errors.rb +25 -8
- data/lib/active_record/explain.rb +8 -58
- data/lib/active_record/explain_subscriber.rb +6 -3
- data/lib/active_record/fixture_set/file.rb +56 -0
- data/lib/active_record/fixtures.rb +146 -148
- data/lib/active_record/inheritance.rb +77 -59
- data/lib/active_record/integration.rb +5 -5
- data/lib/active_record/locale/en.yml +8 -1
- data/lib/active_record/locking/optimistic.rb +38 -42
- data/lib/active_record/locking/pessimistic.rb +4 -4
- data/lib/active_record/log_subscriber.rb +19 -9
- data/lib/active_record/migration.rb +318 -153
- data/lib/active_record/migration/command_recorder.rb +90 -31
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/model_schema.rb +69 -92
- data/lib/active_record/nested_attributes.rb +113 -148
- data/lib/active_record/null_relation.rb +65 -0
- data/lib/active_record/persistence.rb +188 -97
- data/lib/active_record/query_cache.rb +18 -36
- data/lib/active_record/querying.rb +19 -15
- data/lib/active_record/railtie.rb +91 -36
- data/lib/active_record/railties/console_sandbox.rb +0 -2
- data/lib/active_record/railties/controller_runtime.rb +2 -2
- data/lib/active_record/railties/databases.rake +90 -309
- 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 +72 -56
- data/lib/active_record/relation.rb +241 -157
- data/lib/active_record/relation/batches.rb +25 -22
- data/lib/active_record/relation/calculations.rb +143 -121
- data/lib/active_record/relation/delegation.rb +96 -18
- data/lib/active_record/relation/finder_methods.rb +117 -183
- data/lib/active_record/relation/merger.rb +133 -0
- data/lib/active_record/relation/predicate_builder.rb +90 -42
- data/lib/active_record/relation/query_methods.rb +666 -136
- data/lib/active_record/relation/spawn_methods.rb +43 -150
- data/lib/active_record/result.rb +33 -6
- data/lib/active_record/sanitization.rb +24 -50
- data/lib/active_record/schema.rb +19 -12
- data/lib/active_record/schema_dumper.rb +31 -39
- data/lib/active_record/schema_migration.rb +36 -0
- data/lib/active_record/scoping.rb +0 -124
- data/lib/active_record/scoping/default.rb +48 -45
- data/lib/active_record/scoping/named.rb +74 -103
- data/lib/active_record/serialization.rb +6 -2
- data/lib/active_record/serializers/xml_serializer.rb +9 -15
- data/lib/active_record/store.rb +119 -15
- data/lib/active_record/tasks/database_tasks.rb +158 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +138 -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/test_case.rb +61 -38
- data/lib/active_record/timestamp.rb +8 -9
- data/lib/active_record/transactions.rb +65 -51
- data/lib/active_record/validations.rb +17 -15
- data/lib/active_record/validations/associated.rb +20 -14
- data/lib/active_record/validations/presence.rb +65 -0
- data/lib/active_record/validations/uniqueness.rb +93 -52
- data/lib/active_record/version.rb +4 -4
- data/lib/rails/generators/active_record.rb +3 -5
- data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -7
- data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
- data/lib/rails/generators/active_record/model/model_generator.rb +4 -3
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -6
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- metadata +53 -46
- 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,22 @@
|
|
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
|
7
6
|
class Column < ConnectionAdapters::Column # :nodoc:
|
8
|
-
attr_reader :collation
|
7
|
+
attr_reader :collation, :strict
|
9
8
|
|
10
|
-
def initialize(name, default, sql_type = nil, null = true, collation = nil)
|
11
|
-
|
9
|
+
def initialize(name, default, sql_type = nil, null = true, collation = nil, strict = false)
|
10
|
+
@strict = strict
|
12
11
|
@collation = collation
|
12
|
+
|
13
|
+
super(name, default, sql_type, null)
|
13
14
|
end
|
14
15
|
|
15
16
|
def extract_default(default)
|
16
|
-
if
|
17
|
+
if blob_or_text_column?
|
17
18
|
if default.blank?
|
18
|
-
|
19
|
+
null || strict ? nil : ''
|
19
20
|
else
|
20
21
|
raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
|
21
22
|
end
|
@@ -27,10 +28,14 @@ module ActiveRecord
|
|
27
28
|
end
|
28
29
|
|
29
30
|
def has_default?
|
30
|
-
return false if
|
31
|
+
return false if blob_or_text_column? #mysql forbids defaults on blob and text columns
|
31
32
|
super
|
32
33
|
end
|
33
34
|
|
35
|
+
def blob_or_text_column?
|
36
|
+
sql_type =~ /blob/i || type == :text
|
37
|
+
end
|
38
|
+
|
34
39
|
# Must return the relevant concrete adapter
|
35
40
|
def adapter
|
36
41
|
raise NotImplementedError
|
@@ -135,7 +140,7 @@ module ActiveRecord
|
|
135
140
|
@connection_options, @config = connection_options, config
|
136
141
|
@quoted_column_names, @quoted_table_names = {}, {}
|
137
142
|
|
138
|
-
if config.fetch(:prepared_statements) { true }
|
143
|
+
if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
|
139
144
|
@visitor = Arel::Visitors::MySQL.new self
|
140
145
|
else
|
141
146
|
@visitor = BindSubstitution.new self
|
@@ -170,6 +175,14 @@ module ActiveRecord
|
|
170
175
|
true
|
171
176
|
end
|
172
177
|
|
178
|
+
# MySQL 4 technically support transaction isolation, but it is affected by a bug
|
179
|
+
# where the transaction level gets persisted for the whole session:
|
180
|
+
#
|
181
|
+
# http://bugs.mysql.com/bug.php?id=39170
|
182
|
+
def supports_transaction_isolation?
|
183
|
+
version[0] >= 5
|
184
|
+
end
|
185
|
+
|
173
186
|
def native_database_types
|
174
187
|
NATIVE_DATABASE_TYPES
|
175
188
|
end
|
@@ -199,8 +212,6 @@ module ActiveRecord
|
|
199
212
|
if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary)
|
200
213
|
s = column.class.string_to_binary(value).unpack("H*")[0]
|
201
214
|
"x'#{s}'"
|
202
|
-
elsif value.kind_of?(BigDecimal)
|
203
|
-
value.to_s("F")
|
204
215
|
else
|
205
216
|
super
|
206
217
|
end
|
@@ -253,7 +264,7 @@ module ActiveRecord
|
|
253
264
|
end
|
254
265
|
|
255
266
|
# MysqlAdapter has to free a result after using it, so we use this method to write
|
256
|
-
# stuff in
|
267
|
+
# stuff in an abstract way without concerning ourselves about whether it needs to be
|
257
268
|
# explicitly freed or not.
|
258
269
|
def execute_and_free(sql, name = nil) #:nodoc:
|
259
270
|
yield execute(sql, name)
|
@@ -266,19 +277,26 @@ module ActiveRecord
|
|
266
277
|
|
267
278
|
def begin_db_transaction
|
268
279
|
execute "BEGIN"
|
269
|
-
rescue
|
280
|
+
rescue
|
281
|
+
# Transactions aren't supported
|
282
|
+
end
|
283
|
+
|
284
|
+
def begin_isolated_db_transaction(isolation)
|
285
|
+
execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
|
286
|
+
begin_db_transaction
|
287
|
+
rescue
|
270
288
|
# Transactions aren't supported
|
271
289
|
end
|
272
290
|
|
273
291
|
def commit_db_transaction #:nodoc:
|
274
292
|
execute "COMMIT"
|
275
|
-
rescue
|
293
|
+
rescue
|
276
294
|
# Transactions aren't supported
|
277
295
|
end
|
278
296
|
|
279
297
|
def rollback_db_transaction #:nodoc:
|
280
298
|
execute "ROLLBACK"
|
281
|
-
rescue
|
299
|
+
rescue
|
282
300
|
# Transactions aren't supported
|
283
301
|
end
|
284
302
|
|
@@ -296,25 +314,20 @@ module ActiveRecord
|
|
296
314
|
|
297
315
|
# In the simple case, MySQL allows us to place JOINs directly into the UPDATE
|
298
316
|
# 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!
|
317
|
+
# these, we must use a subquery.
|
302
318
|
def join_to_update(update, select) #:nodoc:
|
303
319
|
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)
|
320
|
+
super
|
312
321
|
else
|
313
322
|
update.table select.source
|
314
323
|
update.wheres = select.constraints
|
315
324
|
end
|
316
325
|
end
|
317
326
|
|
327
|
+
def empty_insert_statement_value
|
328
|
+
"VALUES ()"
|
329
|
+
end
|
330
|
+
|
318
331
|
# SCHEMA STATEMENTS ========================================
|
319
332
|
|
320
333
|
def structure_dump #:nodoc:
|
@@ -324,10 +337,10 @@ module ActiveRecord
|
|
324
337
|
sql = "SHOW TABLES"
|
325
338
|
end
|
326
339
|
|
327
|
-
select_all(sql).map { |table|
|
340
|
+
select_all(sql, 'SCHEMA').map { |table|
|
328
341
|
table.delete('Table_type')
|
329
342
|
sql = "SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}"
|
330
|
-
exec_query(sql).first['Create Table'] + ";\n\n"
|
343
|
+
exec_query(sql, 'SCHEMA').first['Create Table'] + ";\n\n"
|
331
344
|
}.join
|
332
345
|
end
|
333
346
|
|
@@ -342,9 +355,9 @@ module ActiveRecord
|
|
342
355
|
# Charset defaults to utf8.
|
343
356
|
#
|
344
357
|
# Example:
|
345
|
-
# create_database 'charset_test', :
|
358
|
+
# create_database 'charset_test', charset: 'latin1', collation: 'latin1_bin'
|
346
359
|
# create_database 'matt_development'
|
347
|
-
# create_database 'matt_development', :
|
360
|
+
# create_database 'matt_development', charset: :big5
|
348
361
|
def create_database(name, options = {})
|
349
362
|
if options[:collation]
|
350
363
|
execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}` COLLATE `#{options[:collation]}`"
|
@@ -421,7 +434,7 @@ module ActiveRecord
|
|
421
434
|
end
|
422
435
|
|
423
436
|
# Returns an array of +Column+ objects for the table specified by +table_name+.
|
424
|
-
def columns(table_name
|
437
|
+
def columns(table_name)#:nodoc:
|
425
438
|
sql = "SHOW FULL FIELDS FROM #{quote_table_name(table_name)}"
|
426
439
|
execute_and_free(sql, 'SCHEMA') do |result|
|
427
440
|
each_hash(result).map do |field|
|
@@ -455,6 +468,7 @@ module ActiveRecord
|
|
455
468
|
# rename_table('octopuses', 'octopi')
|
456
469
|
def rename_table(table_name, new_name)
|
457
470
|
execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
|
471
|
+
rename_table_indexes(table_name, new_name)
|
458
472
|
end
|
459
473
|
|
460
474
|
def add_column(table_name, column_name, type, options = {})
|
@@ -482,11 +496,19 @@ module ActiveRecord
|
|
482
496
|
|
483
497
|
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
484
498
|
execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_sql(table_name, column_name, new_column_name)}")
|
499
|
+
rename_column_indexes(table_name, column_name, new_column_name)
|
485
500
|
end
|
486
501
|
|
487
502
|
# Maps logical Rails types to MySQL-specific data types.
|
488
503
|
def type_to_sql(type, limit = nil, precision = nil, scale = nil)
|
489
504
|
case type.to_s
|
505
|
+
when 'binary'
|
506
|
+
case limit
|
507
|
+
when 0..0xfff; "varbinary(#{limit})"
|
508
|
+
when nil; "blob"
|
509
|
+
when 0x1000..0xffffffff; "blob(#{limit})"
|
510
|
+
else raise(ActiveRecordError, "No binary type has character length #{limit}")
|
511
|
+
end
|
490
512
|
when 'integer'
|
491
513
|
case limit
|
492
514
|
when 1; 'tinyint'
|
@@ -519,7 +541,7 @@ module ActiveRecord
|
|
519
541
|
|
520
542
|
# SHOW VARIABLES LIKE 'name'
|
521
543
|
def show_variable(name)
|
522
|
-
variables = select_all("SHOW VARIABLES LIKE '#{name}'")
|
544
|
+
variables = select_all("SHOW VARIABLES LIKE '#{name}'", 'SCHEMA')
|
523
545
|
variables.first['Value'] unless variables.empty?
|
524
546
|
end
|
525
547
|
|
@@ -528,7 +550,7 @@ module ActiveRecord
|
|
528
550
|
execute_and_free("SHOW CREATE TABLE #{quote_table_name(table)}", 'SCHEMA') do |result|
|
529
551
|
create_table = each_hash(result).first[:"Create Table"]
|
530
552
|
if create_table.to_s =~ /PRIMARY KEY\s+(?:USING\s+\w+\s+)?\((.+)\)/
|
531
|
-
keys = $1.split(",").map { |key| key.
|
553
|
+
keys = $1.split(",").map { |key| key.delete('`"') }
|
532
554
|
keys.length == 1 ? [keys.first, nil] : nil
|
533
555
|
else
|
534
556
|
nil
|
@@ -558,8 +580,23 @@ module ActiveRecord
|
|
558
580
|
where_sql
|
559
581
|
end
|
560
582
|
|
583
|
+
def strict_mode?
|
584
|
+
self.class.type_cast_config_to_boolean(@config.fetch(:strict, true))
|
585
|
+
end
|
586
|
+
|
561
587
|
protected
|
562
588
|
|
589
|
+
# MySQL is too stupid to create a temporary table for use subquery, so we have
|
590
|
+
# to give it some prompting in the form of a subsubquery. Ugh!
|
591
|
+
def subquery_for(key, select)
|
592
|
+
subsubselect = select.clone
|
593
|
+
subsubselect.projections = [key]
|
594
|
+
|
595
|
+
subselect = Arel::SelectManager.new(select.engine)
|
596
|
+
subselect.project Arel.sql(key.name)
|
597
|
+
subselect.from subsubselect.as('__active_record_temp')
|
598
|
+
end
|
599
|
+
|
563
600
|
def add_index_length(option_strings, column_names, options = {})
|
564
601
|
if options.is_a?(Hash) && length = options[:length]
|
565
602
|
case length
|
@@ -630,16 +667,19 @@ module ActiveRecord
|
|
630
667
|
raise ActiveRecordError, "No such column: #{table_name}.#{column_name}"
|
631
668
|
end
|
632
669
|
|
633
|
-
current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
|
670
|
+
current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'", 'SCHEMA')["Type"]
|
634
671
|
rename_column_sql = "CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
|
635
672
|
add_column_options!(rename_column_sql, options)
|
636
673
|
rename_column_sql
|
637
674
|
end
|
638
675
|
|
639
|
-
def remove_column_sql(table_name,
|
640
|
-
|
676
|
+
def remove_column_sql(table_name, column_name, type = nil, options = {})
|
677
|
+
"DROP #{quote_column_name(column_name)}"
|
678
|
+
end
|
679
|
+
|
680
|
+
def remove_columns_sql(table_name, *column_names)
|
681
|
+
column_names.map {|column_name| remove_column_sql(table_name, column_name) }
|
641
682
|
end
|
642
|
-
alias :remove_columns_sql :remove_column
|
643
683
|
|
644
684
|
def add_index_sql(table_name, column_name, options = {})
|
645
685
|
index_name, index_type, index_columns = add_index_options(table_name, column_name, options)
|
@@ -671,6 +711,44 @@ module ActiveRecord
|
|
671
711
|
end
|
672
712
|
column
|
673
713
|
end
|
714
|
+
|
715
|
+
def configure_connection
|
716
|
+
variables = @config[:variables] || {}
|
717
|
+
|
718
|
+
# By default, MySQL 'where id is null' selects the last inserted id.
|
719
|
+
# Turn this off. http://dev.rubyonrails.org/ticket/6778
|
720
|
+
variables[:sql_auto_is_null] = 0
|
721
|
+
|
722
|
+
# Increase timeout so the server doesn't disconnect us.
|
723
|
+
wait_timeout = @config[:wait_timeout]
|
724
|
+
wait_timeout = 2147483 unless wait_timeout.is_a?(Fixnum)
|
725
|
+
variables[:wait_timeout] = self.class.type_cast_config_to_integer(wait_timeout)
|
726
|
+
|
727
|
+
# Make MySQL reject illegal values rather than truncating or blanking them, see
|
728
|
+
# http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html#sqlmode_strict_all_tables
|
729
|
+
# If the user has provided another value for sql_mode, don't replace it.
|
730
|
+
if strict_mode? && !variables.has_key?(:sql_mode)
|
731
|
+
variables[:sql_mode] = 'STRICT_ALL_TABLES'
|
732
|
+
end
|
733
|
+
|
734
|
+
# NAMES does not have an equals sign, see
|
735
|
+
# http://dev.mysql.com/doc/refman/5.0/en/set-statement.html#id944430
|
736
|
+
# (trailing comma because variable_assignments will always have content)
|
737
|
+
encoding = "NAMES #{@config[:encoding]}, " if @config[:encoding]
|
738
|
+
|
739
|
+
# Gather up all of the SET variables...
|
740
|
+
variable_assignments = variables.map do |k, v|
|
741
|
+
if v == ':default' || v == :default
|
742
|
+
"@@SESSION.#{k.to_s} = DEFAULT" # Sets the value to the global or compile default
|
743
|
+
elsif !v.nil?
|
744
|
+
"@@SESSION.#{k.to_s} = #{quote(v)}"
|
745
|
+
end
|
746
|
+
# or else nil; compact to clear nils out
|
747
|
+
end.compact.join(', ')
|
748
|
+
|
749
|
+
# ...and send them all in one query
|
750
|
+
execute("SET #{encoding} #{variable_assignments}", :skip_logging)
|
751
|
+
end
|
674
752
|
end
|
675
753
|
end
|
676
754
|
end
|
@@ -66,6 +66,26 @@ module ActiveRecord
|
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
69
|
+
def binary?
|
70
|
+
type == :binary
|
71
|
+
end
|
72
|
+
|
73
|
+
# Casts a Ruby value to something appropriate for writing to the database.
|
74
|
+
def type_cast_for_write(value)
|
75
|
+
return value unless number?
|
76
|
+
|
77
|
+
case value
|
78
|
+
when FalseClass
|
79
|
+
0
|
80
|
+
when TrueClass
|
81
|
+
1
|
82
|
+
when String
|
83
|
+
value.presence
|
84
|
+
else
|
85
|
+
value
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
69
89
|
# Casts value (which is a String) to an appropriate instance.
|
70
90
|
def type_cast(value)
|
71
91
|
return nil if value.nil?
|
@@ -80,7 +100,7 @@ module ActiveRecord
|
|
80
100
|
when :decimal then klass.value_to_decimal(value)
|
81
101
|
when :datetime, :timestamp then klass.string_to_time(value)
|
82
102
|
when :time then klass.string_to_dummy_time(value)
|
83
|
-
when :date then klass.
|
103
|
+
when :date then klass.value_to_date(value)
|
84
104
|
when :binary then klass.binary_to_string(value)
|
85
105
|
when :boolean then klass.value_to_boolean(value)
|
86
106
|
else value
|
@@ -88,6 +108,10 @@ module ActiveRecord
|
|
88
108
|
end
|
89
109
|
|
90
110
|
def type_cast_code(var_name)
|
111
|
+
message = "Column#type_cast_code is deprecated in favor of using Column#type_cast only, " \
|
112
|
+
"and it is going to be removed in future Rails versions."
|
113
|
+
ActiveSupport::Deprecation.warn message
|
114
|
+
|
91
115
|
klass = self.class.name
|
92
116
|
|
93
117
|
case type
|
@@ -97,9 +121,12 @@ module ActiveRecord
|
|
97
121
|
when :decimal then "#{klass}.value_to_decimal(#{var_name})"
|
98
122
|
when :datetime, :timestamp then "#{klass}.string_to_time(#{var_name})"
|
99
123
|
when :time then "#{klass}.string_to_dummy_time(#{var_name})"
|
100
|
-
when :date then "#{klass}.
|
124
|
+
when :date then "#{klass}.value_to_date(#{var_name})"
|
101
125
|
when :binary then "#{klass}.binary_to_string(#{var_name})"
|
102
126
|
when :boolean then "#{klass}.value_to_boolean(#{var_name})"
|
127
|
+
when :hstore then "#{klass}.string_to_hstore(#{var_name})"
|
128
|
+
when :inet, :cidr then "#{klass}.string_to_cidr(#{var_name})"
|
129
|
+
when :json then "#{klass}.string_to_json(#{var_name})"
|
103
130
|
else var_name
|
104
131
|
end
|
105
132
|
end
|
@@ -132,23 +159,27 @@ module ActiveRecord
|
|
132
159
|
value
|
133
160
|
end
|
134
161
|
|
135
|
-
def
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
162
|
+
def value_to_date(value)
|
163
|
+
if value.is_a?(String)
|
164
|
+
return nil if value.blank?
|
165
|
+
fast_string_to_date(value) || fallback_string_to_date(value)
|
166
|
+
elsif value.respond_to?(:to_date)
|
167
|
+
value.to_date
|
168
|
+
else
|
169
|
+
value
|
170
|
+
end
|
140
171
|
end
|
141
172
|
|
142
173
|
def string_to_time(string)
|
143
174
|
return string unless string.is_a?(String)
|
144
|
-
return nil if string.
|
175
|
+
return nil if string.blank?
|
145
176
|
|
146
177
|
fast_string_to_time(string) || fallback_string_to_time(string)
|
147
178
|
end
|
148
179
|
|
149
180
|
def string_to_dummy_time(string)
|
150
181
|
return string unless string.is_a?(String)
|
151
|
-
return nil if string.
|
182
|
+
return nil if string.blank?
|
152
183
|
|
153
184
|
dummy_time_string = "2000-01-01 #{string}"
|
154
185
|
|
@@ -210,7 +241,7 @@ module ActiveRecord
|
|
210
241
|
# Treat 0000-00-00 00:00:00 as nil.
|
211
242
|
return nil if year.nil? || (year == 0 && mon == 0 && mday == 0)
|
212
243
|
|
213
|
-
Time.
|
244
|
+
Time.send(Base.default_timezone, year, mon, mday, hour, min, sec, microsec) rescue nil
|
214
245
|
end
|
215
246
|
|
216
247
|
def fast_string_to_date(string)
|
@@ -219,20 +250,11 @@ module ActiveRecord
|
|
219
250
|
end
|
220
251
|
end
|
221
252
|
|
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
|
253
|
+
# Doesn't handle time zones.
|
254
|
+
def fast_string_to_time(string)
|
255
|
+
if string =~ Format::ISO_DATETIME
|
256
|
+
microsec = ($7.to_r * 1_000_000).to_i
|
257
|
+
new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
|
236
258
|
end
|
237
259
|
end
|
238
260
|
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
class ConnectionSpecification #:nodoc:
|
6
|
+
attr_reader :config, :adapter_method
|
7
|
+
|
8
|
+
def initialize(config, adapter_method)
|
9
|
+
@config, @adapter_method = config, adapter_method
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize_dup(original)
|
13
|
+
@config = original.config.dup
|
14
|
+
end
|
15
|
+
|
16
|
+
##
|
17
|
+
# Builds a ConnectionSpecification from user input
|
18
|
+
class Resolver # :nodoc:
|
19
|
+
attr_reader :config, :klass, :configurations
|
20
|
+
|
21
|
+
def initialize(config, configurations)
|
22
|
+
@config = config
|
23
|
+
@configurations = configurations
|
24
|
+
end
|
25
|
+
|
26
|
+
def spec
|
27
|
+
case config
|
28
|
+
when nil
|
29
|
+
raise AdapterNotSpecified unless defined?(Rails.env)
|
30
|
+
resolve_string_connection Rails.env
|
31
|
+
when Symbol, String
|
32
|
+
resolve_string_connection config.to_s
|
33
|
+
when Hash
|
34
|
+
resolve_hash_connection config
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
def resolve_string_connection(spec) # :nodoc:
|
40
|
+
hash = configurations.fetch(spec) do |k|
|
41
|
+
self.class.connection_url_to_hash(k)
|
42
|
+
end
|
43
|
+
|
44
|
+
raise(AdapterNotSpecified, "#{spec} database is not configured") unless hash
|
45
|
+
|
46
|
+
resolve_hash_connection hash
|
47
|
+
end
|
48
|
+
|
49
|
+
def resolve_hash_connection(spec) # :nodoc:
|
50
|
+
spec = spec.symbolize_keys
|
51
|
+
|
52
|
+
raise(AdapterNotSpecified, "database configuration does not specify adapter") unless spec.key?(:adapter)
|
53
|
+
|
54
|
+
path_to_adapter = "active_record/connection_adapters/#{spec[:adapter]}_adapter"
|
55
|
+
begin
|
56
|
+
require path_to_adapter
|
57
|
+
rescue Gem::LoadError => e
|
58
|
+
raise Gem::LoadError, "Specified '#{spec[:adapter]}' for database adapter, but the gem is not loaded. Add `gem '#{e.name}'` to your Gemfile."
|
59
|
+
rescue LoadError => e
|
60
|
+
raise LoadError, "Could not load '#{path_to_adapter}'. Make sure that the adapter in config/database.yml is valid. If you use an adapter other than 'mysql', 'mysql2', 'postgresql' or 'sqlite3' add the necessary adapter gem to the Gemfile.", e.backtrace
|
61
|
+
end
|
62
|
+
|
63
|
+
adapter_method = "#{spec[:adapter]}_connection"
|
64
|
+
|
65
|
+
ConnectionSpecification.new(spec, adapter_method)
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.connection_url_to_hash(url) # :nodoc:
|
69
|
+
config = URI.parse url
|
70
|
+
adapter = config.scheme
|
71
|
+
adapter = "postgresql" if adapter == "postgres"
|
72
|
+
spec = { :adapter => adapter,
|
73
|
+
:username => config.user,
|
74
|
+
:password => config.password,
|
75
|
+
:port => config.port,
|
76
|
+
:database => config.path.sub(%r{^/},""),
|
77
|
+
:host => config.host }
|
78
|
+
|
79
|
+
spec.reject!{ |_,value| value.blank? }
|
80
|
+
|
81
|
+
uri_parser = URI::Parser.new
|
82
|
+
|
83
|
+
spec.map { |key,value| spec[key] = uri_parser.unescape(value) if value.is_a?(String) }
|
84
|
+
|
85
|
+
if config.query
|
86
|
+
options = Hash[config.query.split("&").map{ |pair| pair.split("=") }].symbolize_keys
|
87
|
+
|
88
|
+
spec.merge!(options)
|
89
|
+
end
|
90
|
+
|
91
|
+
spec
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|