activerecord 4.0.13 → 4.1.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 +745 -2700
- data/README.rdoc +2 -2
- data/examples/performance.rb +30 -18
- data/examples/simple.rb +4 -4
- data/lib/active_record.rb +2 -6
- data/lib/active_record/aggregations.rb +2 -1
- data/lib/active_record/association_relation.rb +0 -4
- data/lib/active_record/associations.rb +87 -43
- data/lib/active_record/associations/alias_tracker.rb +1 -3
- data/lib/active_record/associations/association.rb +8 -16
- data/lib/active_record/associations/association_scope.rb +5 -16
- data/lib/active_record/associations/belongs_to_association.rb +34 -25
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
- data/lib/active_record/associations/builder/association.rb +78 -54
- data/lib/active_record/associations/builder/belongs_to.rb +91 -58
- data/lib/active_record/associations/builder/collection_association.rb +47 -45
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +107 -25
- data/lib/active_record/associations/builder/has_many.rb +2 -2
- data/lib/active_record/associations/builder/has_one.rb +5 -7
- data/lib/active_record/associations/builder/singular_association.rb +6 -7
- data/lib/active_record/associations/collection_association.rb +68 -105
- data/lib/active_record/associations/collection_proxy.rb +12 -15
- data/lib/active_record/associations/has_many_association.rb +11 -9
- data/lib/active_record/associations/has_many_through_association.rb +16 -12
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +204 -165
- data/lib/active_record/associations/join_dependency/join_association.rb +43 -101
- data/lib/active_record/associations/join_dependency/join_base.rb +6 -8
- data/lib/active_record/associations/join_dependency/join_part.rb +18 -37
- data/lib/active_record/associations/join_helper.rb +2 -11
- data/lib/active_record/associations/preloader.rb +89 -34
- data/lib/active_record/associations/preloader/association.rb +43 -25
- data/lib/active_record/associations/preloader/collection_association.rb +2 -2
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- data/lib/active_record/associations/preloader/singular_association.rb +3 -3
- data/lib/active_record/associations/preloader/through_association.rb +58 -26
- data/lib/active_record/associations/singular_association.rb +6 -5
- data/lib/active_record/associations/through_association.rb +2 -2
- data/lib/active_record/attribute_assignment.rb +5 -2
- data/lib/active_record/attribute_methods.rb +45 -40
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -1
- data/lib/active_record/attribute_methods/dirty.rb +8 -22
- data/lib/active_record/attribute_methods/primary_key.rb +1 -7
- data/lib/active_record/attribute_methods/read.rb +55 -28
- data/lib/active_record/attribute_methods/serialization.rb +12 -33
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -13
- data/lib/active_record/attribute_methods/write.rb +37 -12
- data/lib/active_record/autosave_association.rb +207 -207
- data/lib/active_record/base.rb +5 -1
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +2 -7
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +11 -22
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +12 -14
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -5
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +84 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +9 -8
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +52 -83
- data/lib/active_record/connection_adapters/abstract/transaction.rb +0 -5
- data/lib/active_record/connection_adapters/abstract_adapter.rb +14 -97
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +58 -60
- data/lib/active_record/connection_adapters/column.rb +1 -35
- data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +3 -4
- data/lib/active_record/connection_adapters/mysql_adapter.rb +16 -15
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +24 -18
- data/lib/active_record/connection_adapters/postgresql/cast.rb +20 -16
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +23 -43
- data/lib/active_record/connection_adapters/postgresql/oid.rb +19 -12
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +28 -23
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +8 -30
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +92 -75
- data/lib/active_record/connection_adapters/schema_cache.rb +8 -29
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +31 -64
- data/lib/active_record/connection_handling.rb +2 -2
- data/lib/active_record/core.rb +22 -43
- data/lib/active_record/counter_cache.rb +7 -7
- data/lib/active_record/enum.rb +100 -0
- data/lib/active_record/errors.rb +10 -5
- data/lib/active_record/fixture_set/file.rb +2 -1
- data/lib/active_record/fixtures.rb +171 -74
- data/lib/active_record/inheritance.rb +16 -22
- data/lib/active_record/integration.rb +52 -1
- data/lib/active_record/locking/optimistic.rb +7 -2
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +5 -12
- data/lib/active_record/migration.rb +62 -46
- data/lib/active_record/migration/command_recorder.rb +7 -13
- data/lib/active_record/model_schema.rb +7 -14
- data/lib/active_record/nested_attributes.rb +10 -8
- data/lib/active_record/no_touching.rb +52 -0
- data/lib/active_record/null_relation.rb +3 -3
- data/lib/active_record/persistence.rb +16 -34
- data/lib/active_record/querying.rb +14 -12
- data/lib/active_record/railtie.rb +0 -50
- data/lib/active_record/railties/databases.rake +12 -15
- data/lib/active_record/readonly_attributes.rb +0 -6
- data/lib/active_record/reflection.rb +189 -75
- data/lib/active_record/relation.rb +69 -94
- data/lib/active_record/relation/batches.rb +57 -23
- data/lib/active_record/relation/calculations.rb +36 -43
- data/lib/active_record/relation/delegation.rb +54 -39
- data/lib/active_record/relation/finder_methods.rb +107 -62
- data/lib/active_record/relation/merger.rb +7 -20
- data/lib/active_record/relation/predicate_builder.rb +57 -38
- data/lib/active_record/relation/predicate_builder/array_handler.rb +29 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
- data/lib/active_record/relation/query_methods.rb +110 -98
- data/lib/active_record/relation/spawn_methods.rb +1 -2
- data/lib/active_record/result.rb +45 -6
- data/lib/active_record/runtime_registry.rb +5 -0
- data/lib/active_record/sanitization.rb +6 -8
- data/lib/active_record/schema_dumper.rb +16 -5
- data/lib/active_record/schema_migration.rb +24 -25
- data/lib/active_record/scoping/default.rb +5 -18
- data/lib/active_record/scoping/named.rb +8 -29
- data/lib/active_record/store.rb +56 -28
- data/lib/active_record/tasks/database_tasks.rb +8 -4
- data/lib/active_record/timestamp.rb +4 -4
- data/lib/active_record/transactions.rb +8 -10
- data/lib/active_record/validations/presence.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +1 -6
- data/lib/active_record/version.rb +1 -1
- data/lib/rails/generators/active_record.rb +2 -8
- data/lib/rails/generators/active_record/migration.rb +18 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +4 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +4 -0
- metadata +32 -45
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -65
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
- data/lib/active_record/tasks/firebird_database_tasks.rb +0 -56
- data/lib/active_record/tasks/oracle_database_tasks.rb +0 -45
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +0 -48
- data/lib/active_record/test_case.rb +0 -102
data/lib/active_record/base.rb
CHANGED
@@ -4,7 +4,7 @@ require 'active_support/benchmarkable'
|
|
4
4
|
require 'active_support/dependencies'
|
5
5
|
require 'active_support/descendants_tracker'
|
6
6
|
require 'active_support/time'
|
7
|
-
require 'active_support/core_ext/
|
7
|
+
require 'active_support/core_ext/module/attribute_accessors'
|
8
8
|
require 'active_support/core_ext/class/delegating_attributes'
|
9
9
|
require 'active_support/core_ext/array/extract_options'
|
10
10
|
require 'active_support/core_ext/hash/deep_merge'
|
@@ -18,6 +18,7 @@ require 'arel'
|
|
18
18
|
require 'active_record/errors'
|
19
19
|
require 'active_record/log_subscriber'
|
20
20
|
require 'active_record/explain_subscriber'
|
21
|
+
require 'active_record/relation/delegation'
|
21
22
|
|
22
23
|
module ActiveRecord #:nodoc:
|
23
24
|
# = Active Record
|
@@ -290,8 +291,11 @@ module ActiveRecord #:nodoc:
|
|
290
291
|
extend Translation
|
291
292
|
extend DynamicMatchers
|
292
293
|
extend Explain
|
294
|
+
extend Enum
|
295
|
+
extend Delegation::DelegateCache
|
293
296
|
|
294
297
|
include Persistence
|
298
|
+
include NoTouching
|
295
299
|
include ReadonlyAttributes
|
296
300
|
include ModelSchema
|
297
301
|
include Inheritance
|
@@ -302,11 +302,11 @@ module ActiveRecord
|
|
302
302
|
run_callbacks(:save) { super }
|
303
303
|
end
|
304
304
|
|
305
|
-
def
|
305
|
+
def create_record #:nodoc:
|
306
306
|
run_callbacks(:create) { super }
|
307
307
|
end
|
308
308
|
|
309
|
-
def
|
309
|
+
def update_record(*) #:nodoc:
|
310
310
|
run_callbacks(:update) { super }
|
311
311
|
end
|
312
312
|
end
|
@@ -332,11 +332,6 @@ module ActiveRecord
|
|
332
332
|
end
|
333
333
|
end
|
334
334
|
|
335
|
-
def clear_stale_cached_connections! # :nodoc:
|
336
|
-
reap
|
337
|
-
end
|
338
|
-
deprecate :clear_stale_cached_connections! => "Please use #reap instead"
|
339
|
-
|
340
335
|
# Check-out a database connection from the pool, indicating that you want
|
341
336
|
# to use it. You should call #checkin when you no longer need this.
|
342
337
|
#
|
@@ -398,7 +393,7 @@ module ActiveRecord
|
|
398
393
|
synchronize do
|
399
394
|
stale = Time.now - @dead_connection_timeout
|
400
395
|
connections.dup.each do |conn|
|
401
|
-
if conn.in_use? && stale > conn.last_use && !conn.
|
396
|
+
if conn.in_use? && stale > conn.last_use && !conn.active?
|
402
397
|
remove conn
|
403
398
|
end
|
404
399
|
end
|
@@ -629,7 +624,7 @@ module ActiveRecord
|
|
629
624
|
end
|
630
625
|
|
631
626
|
response
|
632
|
-
rescue
|
627
|
+
rescue Exception
|
633
628
|
ActiveRecord::Base.clear_active_connections! unless testing
|
634
629
|
raise
|
635
630
|
end
|
@@ -18,18 +18,15 @@ module ActiveRecord
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
# Returns an
|
22
|
-
# column values as values.
|
21
|
+
# Returns an ActiveRecord::Result instance.
|
23
22
|
def select_all(arel, name = nil, binds = [])
|
24
|
-
arel, binds = binds_from_relation arel, binds
|
25
23
|
select(to_sql(arel, binds), name, binds)
|
26
24
|
end
|
27
25
|
|
28
26
|
# Returns a record hash with the column names as keys and column values
|
29
27
|
# as values.
|
30
28
|
def select_one(arel, name = nil, binds = [])
|
31
|
-
|
32
|
-
result.first if result
|
29
|
+
select_all(arel, name, binds).first
|
33
30
|
end
|
34
31
|
|
35
32
|
# Returns a single value from a record
|
@@ -42,13 +39,13 @@ module ActiveRecord
|
|
42
39
|
# Returns an array of the values of the first column in a select:
|
43
40
|
# select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
|
44
41
|
def select_values(arel, name = nil)
|
45
|
-
arel,
|
46
|
-
|
42
|
+
select_rows(to_sql(arel, []), name)
|
43
|
+
.map { |v| v[0] }
|
47
44
|
end
|
48
45
|
|
49
46
|
# Returns an array of arrays containing the field values.
|
50
47
|
# Order is the same as that returned by +columns+.
|
51
|
-
def select_rows(sql, name = nil
|
48
|
+
def select_rows(sql, name = nil)
|
52
49
|
end
|
53
50
|
undef_method :select_rows
|
54
51
|
|
@@ -289,6 +286,10 @@ module ActiveRecord
|
|
289
286
|
# Inserts the given fixture into the table. Overridden in adapters that require
|
290
287
|
# something beyond a simple insert (eg. Oracle).
|
291
288
|
def insert_fixture(fixture, table_name)
|
289
|
+
execute fixture_sql(fixture, table_name), 'Fixture Insert'
|
290
|
+
end
|
291
|
+
|
292
|
+
def fixture_sql(fixture, table_name)
|
292
293
|
columns = schema_cache.columns_hash(table_name)
|
293
294
|
|
294
295
|
key_list = []
|
@@ -297,17 +298,13 @@ module ActiveRecord
|
|
297
298
|
quote(value, columns[name])
|
298
299
|
end
|
299
300
|
|
300
|
-
|
301
|
+
"INSERT INTO #{quote_table_name(table_name)} (#{key_list.join(', ')}) VALUES (#{value_list.join(', ')})"
|
301
302
|
end
|
302
303
|
|
303
304
|
def empty_insert_statement_value
|
304
305
|
"DEFAULT VALUES"
|
305
306
|
end
|
306
307
|
|
307
|
-
def case_sensitive_equality_operator
|
308
|
-
"="
|
309
|
-
end
|
310
|
-
|
311
308
|
def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
|
312
309
|
"WHERE #{quoted_primary_key} IN (SELECT #{quoted_primary_key} FROM #{quoted_table_name} #{where_sql})"
|
313
310
|
end
|
@@ -356,8 +353,7 @@ module ActiveRecord
|
|
356
353
|
subselect
|
357
354
|
end
|
358
355
|
|
359
|
-
# Returns an
|
360
|
-
# column values as values.
|
356
|
+
# Returns an ActiveRecord::Result instance.
|
361
357
|
def select(sql, name = nil, binds = [])
|
362
358
|
end
|
363
359
|
undef_method :select
|
@@ -386,13 +382,6 @@ module ActiveRecord
|
|
386
382
|
row = result.rows.first
|
387
383
|
row && row.first
|
388
384
|
end
|
389
|
-
|
390
|
-
def binds_from_relation(relation, binds)
|
391
|
-
if relation.is_a?(Relation) && binds.blank?
|
392
|
-
relation, binds = relation.arel, relation.bind_values
|
393
|
-
end
|
394
|
-
[relation, binds]
|
395
|
-
end
|
396
385
|
end
|
397
386
|
end
|
398
387
|
end
|
@@ -9,10 +9,10 @@ module ActiveRecord
|
|
9
9
|
def dirties_query_cache(base, *method_names)
|
10
10
|
method_names.each do |method_name|
|
11
11
|
base.class_eval <<-end_code, __FILE__, __LINE__ + 1
|
12
|
-
def #{method_name}(*)
|
13
|
-
clear_query_cache if @query_cache_enabled
|
14
|
-
super
|
15
|
-
end
|
12
|
+
def #{method_name}(*)
|
13
|
+
clear_query_cache if @query_cache_enabled
|
14
|
+
super
|
15
|
+
end
|
16
16
|
end_code
|
17
17
|
end
|
18
18
|
end
|
@@ -20,13 +20,19 @@ module ActiveRecord
|
|
20
20
|
|
21
21
|
attr_reader :query_cache, :query_cache_enabled
|
22
22
|
|
23
|
+
def initialize(*)
|
24
|
+
super
|
25
|
+
@query_cache = Hash.new { |h,sql| h[sql] = {} }
|
26
|
+
@query_cache_enabled = false
|
27
|
+
end
|
28
|
+
|
23
29
|
# Enable the query cache within the block.
|
24
30
|
def cache
|
25
31
|
old, @query_cache_enabled = @query_cache_enabled, true
|
26
32
|
yield
|
27
33
|
ensure
|
28
|
-
clear_query_cache
|
29
34
|
@query_cache_enabled = old
|
35
|
+
clear_query_cache unless @query_cache_enabled
|
30
36
|
end
|
31
37
|
|
32
38
|
def enable_query_cache!
|
@@ -57,7 +63,6 @@ module ActiveRecord
|
|
57
63
|
|
58
64
|
def select_all(arel, name = nil, binds = [])
|
59
65
|
if @query_cache_enabled && !locked?(arel)
|
60
|
-
arel, binds = binds_from_relation arel, binds
|
61
66
|
sql = to_sql(arel, binds)
|
62
67
|
cache_sql(sql, binds) { super(sql, name, binds) }
|
63
68
|
else
|
@@ -76,14 +81,7 @@ module ActiveRecord
|
|
76
81
|
else
|
77
82
|
@query_cache[sql][binds] = yield
|
78
83
|
end
|
79
|
-
|
80
|
-
# FIXME: we should guarantee that all cached items are Result
|
81
|
-
# objects. Then we can avoid this conditional
|
82
|
-
if ActiveRecord::Result === result
|
83
|
-
result.dup
|
84
|
-
else
|
85
|
-
result.collect { |row| row.dup }
|
86
|
-
end
|
84
|
+
result.dup
|
87
85
|
end
|
88
86
|
|
89
87
|
# If arel is locked this is a SELECT ... FOR UPDATE or somesuch. Such
|
@@ -15,7 +15,6 @@ module ActiveRecord
|
|
15
15
|
return "'#{quote_string(value)}'" unless column
|
16
16
|
|
17
17
|
case column.type
|
18
|
-
when :binary then "'#{quote_string(column.string_to_binary(value))}'"
|
19
18
|
when :integer then value.to_i.to_s
|
20
19
|
when :float then value.to_f.to_s
|
21
20
|
else
|
@@ -44,9 +43,7 @@ module ActiveRecord
|
|
44
43
|
# SQLite does not understand dates, so this method will convert a Date
|
45
44
|
# to a String.
|
46
45
|
def type_cast(value, column)
|
47
|
-
|
48
|
-
return value.id
|
49
|
-
end
|
46
|
+
return value.id if value.respond_to?(:quoted_id)
|
50
47
|
|
51
48
|
case value
|
52
49
|
when String, ActiveSupport::Multibyte::Chars
|
@@ -54,7 +51,6 @@ module ActiveRecord
|
|
54
51
|
return value unless column
|
55
52
|
|
56
53
|
case column.type
|
57
|
-
when :binary then value
|
58
54
|
when :integer then value.to_i
|
59
55
|
when :float then value.to_f
|
60
56
|
else
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module Savepoints #:nodoc:
|
4
|
+
def supports_savepoints?
|
5
|
+
true
|
6
|
+
end
|
7
|
+
|
8
|
+
def create_savepoint(name = current_savepoint_name)
|
9
|
+
execute("SAVEPOINT #{name}")
|
10
|
+
end
|
11
|
+
|
12
|
+
def rollback_to_savepoint(name = current_savepoint_name)
|
13
|
+
execute("ROLLBACK TO SAVEPOINT #{name}")
|
14
|
+
end
|
15
|
+
|
16
|
+
def release_savepoint(name = current_savepoint_name)
|
17
|
+
execute("RELEASE SAVEPOINT #{name}")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
class AbstractAdapter
|
4
|
+
class SchemaCreation # :nodoc:
|
5
|
+
def initialize(conn)
|
6
|
+
@conn = conn
|
7
|
+
@cache = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def accept(o)
|
11
|
+
m = @cache[o.class] ||= "visit_#{o.class.name.split('::').last}"
|
12
|
+
send m, o
|
13
|
+
end
|
14
|
+
|
15
|
+
def visit_AddColumn(o)
|
16
|
+
sql_type = type_to_sql(o.type.to_sym, o.limit, o.precision, o.scale)
|
17
|
+
sql = "ADD #{quote_column_name(o.name)} #{sql_type}"
|
18
|
+
add_column_options!(sql, column_options(o))
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def visit_AlterTable(o)
|
24
|
+
sql = "ALTER TABLE #{quote_table_name(o.name)} "
|
25
|
+
sql << o.adds.map { |col| visit_AddColumn col }.join(' ')
|
26
|
+
end
|
27
|
+
|
28
|
+
def visit_ColumnDefinition(o)
|
29
|
+
sql_type = type_to_sql(o.type.to_sym, o.limit, o.precision, o.scale)
|
30
|
+
column_sql = "#{quote_column_name(o.name)} #{sql_type}"
|
31
|
+
add_column_options!(column_sql, column_options(o)) unless o.primary_key?
|
32
|
+
column_sql
|
33
|
+
end
|
34
|
+
|
35
|
+
def visit_TableDefinition(o)
|
36
|
+
create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE "
|
37
|
+
create_sql << "#{quote_table_name(o.name)} "
|
38
|
+
create_sql << "(#{o.columns.map { |c| accept c }.join(', ')}) " unless o.as
|
39
|
+
create_sql << "#{o.options}"
|
40
|
+
create_sql << " AS #{@conn.to_sql(o.as)}" if o.as
|
41
|
+
create_sql
|
42
|
+
end
|
43
|
+
|
44
|
+
def column_options(o)
|
45
|
+
column_options = {}
|
46
|
+
column_options[:null] = o.null unless o.null.nil?
|
47
|
+
column_options[:default] = o.default unless o.default.nil?
|
48
|
+
column_options[:column] = o
|
49
|
+
column_options[:first] = o.first
|
50
|
+
column_options[:after] = o.after
|
51
|
+
column_options
|
52
|
+
end
|
53
|
+
|
54
|
+
def quote_column_name(name)
|
55
|
+
@conn.quote_column_name name
|
56
|
+
end
|
57
|
+
|
58
|
+
def quote_table_name(name)
|
59
|
+
@conn.quote_table_name name
|
60
|
+
end
|
61
|
+
|
62
|
+
def type_to_sql(type, limit, precision, scale)
|
63
|
+
@conn.type_to_sql type.to_sym, limit, precision, scale
|
64
|
+
end
|
65
|
+
|
66
|
+
def add_column_options!(sql, options)
|
67
|
+
sql << " DEFAULT #{@conn.quote(options[:default], options[:column])}" if options_include_default?(options)
|
68
|
+
# must explicitly check for :null to allow change_column to work on migrations
|
69
|
+
if options[:null] == false
|
70
|
+
sql << " NOT NULL"
|
71
|
+
end
|
72
|
+
if options[:auto_increment] == true
|
73
|
+
sql << " AUTO_INCREMENT"
|
74
|
+
end
|
75
|
+
sql
|
76
|
+
end
|
77
|
+
|
78
|
+
def options_include_default?(options)
|
79
|
+
options.include?(:default) && !(options[:null] == false && options[:default].nil?)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -15,16 +15,16 @@ module ActiveRecord
|
|
15
15
|
# are typically created by methods in TableDefinition, and added to the
|
16
16
|
# +columns+ attribute of said TableDefinition object, in order to be used
|
17
17
|
# for generating a number of table creation or table changing SQL statements.
|
18
|
-
class ColumnDefinition < Struct.new(:name, :type, :limit, :precision, :scale, :default, :null, :first, :after, :primary_key
|
19
|
-
def string_to_binary(value)
|
20
|
-
value
|
21
|
-
end
|
18
|
+
class ColumnDefinition < Struct.new(:name, :type, :limit, :precision, :scale, :default, :null, :first, :after, :primary_key) #:nodoc:
|
22
19
|
|
23
20
|
def primary_key?
|
24
21
|
primary_key || type.to_sym == :primary_key
|
25
22
|
end
|
26
23
|
end
|
27
24
|
|
25
|
+
class ChangeColumnDefinition < Struct.new(:column, :type, :options) #:nodoc:
|
26
|
+
end
|
27
|
+
|
28
28
|
# Represents the schema of an SQL table in an abstract way. This class
|
29
29
|
# provides methods for manipulating the schema representation.
|
30
30
|
#
|
@@ -49,14 +49,15 @@ module ActiveRecord
|
|
49
49
|
# An array of ColumnDefinition objects, representing the column changes
|
50
50
|
# that have been defined.
|
51
51
|
attr_accessor :indexes
|
52
|
-
attr_reader :name, :temporary, :options
|
52
|
+
attr_reader :name, :temporary, :options, :as
|
53
53
|
|
54
|
-
def initialize(types, name, temporary, options)
|
54
|
+
def initialize(types, name, temporary, options, as = nil)
|
55
55
|
@columns_hash = {}
|
56
56
|
@indexes = {}
|
57
57
|
@native = types
|
58
58
|
@temporary = temporary
|
59
59
|
@options = options
|
60
|
+
@as = as
|
60
61
|
@name = name
|
61
62
|
end
|
62
63
|
|
@@ -393,8 +394,8 @@ module ActiveRecord
|
|
393
394
|
# Adds timestamps (+created_at+ and +updated_at+) columns to the table. See SchemaStatements#add_timestamps
|
394
395
|
#
|
395
396
|
# t.timestamps
|
396
|
-
def timestamps
|
397
|
-
@base.add_timestamps(@table_name
|
397
|
+
def timestamps
|
398
|
+
@base.add_timestamps(@table_name)
|
398
399
|
end
|
399
400
|
|
400
401
|
# Changes the column's definition according to the new options.
|
@@ -120,9 +120,9 @@ module ActiveRecord
|
|
120
120
|
# The name of the primary key, if one is to be added automatically.
|
121
121
|
# Defaults to +id+. If <tt>:id</tt> is false this option is ignored.
|
122
122
|
#
|
123
|
-
#
|
124
|
-
# primary key
|
125
|
-
#
|
123
|
+
# Also note that this just sets the primary key in the table. You additionally
|
124
|
+
# need to configure the primary key in the model via +self.primary_key=+.
|
125
|
+
# Models do NOT auto-detect the primary key from their table definition.
|
126
126
|
#
|
127
127
|
# [<tt>:options</tt>]
|
128
128
|
# Any extra options you want appended to the table definition.
|
@@ -131,6 +131,9 @@ module ActiveRecord
|
|
131
131
|
# [<tt>:force</tt>]
|
132
132
|
# Set to true to drop the table before creating it.
|
133
133
|
# Defaults to false.
|
134
|
+
# [<tt>:as</tt>]
|
135
|
+
# SQL to use to generate the table. When this option is used, the block is
|
136
|
+
# ignored, as are the <tt>:id</tt> and <tt>:primary_key</tt> options.
|
134
137
|
#
|
135
138
|
# ====== Add a backend specific option to the generated SQL (MySQL)
|
136
139
|
#
|
@@ -169,19 +172,31 @@ module ActiveRecord
|
|
169
172
|
# supplier_id int
|
170
173
|
# )
|
171
174
|
#
|
175
|
+
# ====== Create a temporary table based on a query
|
176
|
+
#
|
177
|
+
# create_table(:long_query, temporary: true,
|
178
|
+
# as: "SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id")
|
179
|
+
#
|
180
|
+
# generates:
|
181
|
+
#
|
182
|
+
# CREATE TEMPORARY TABLE long_query AS
|
183
|
+
# SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
|
184
|
+
#
|
172
185
|
# See also TableDefinition#column for details on how to create columns.
|
173
186
|
def create_table(table_name, options = {})
|
174
|
-
td = create_table_definition table_name, options[:temporary], options[:options]
|
187
|
+
td = create_table_definition table_name, options[:temporary], options[:options], options[:as]
|
175
188
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
189
|
+
if !options[:as]
|
190
|
+
unless options[:id] == false
|
191
|
+
pk = options.fetch(:primary_key) {
|
192
|
+
Base.get_primary_key table_name.to_s.singularize
|
193
|
+
}
|
180
194
|
|
181
|
-
|
182
|
-
|
195
|
+
td.primary_key pk, options.fetch(:id, :primary_key), options
|
196
|
+
end
|
183
197
|
|
184
|
-
|
198
|
+
yield td if block_given?
|
199
|
+
end
|
185
200
|
|
186
201
|
if options[:force] && table_exists?(table_name)
|
187
202
|
drop_table(table_name, options)
|
@@ -558,8 +573,8 @@ module ActiveRecord
|
|
558
573
|
# this is a naive implementation; some DBs may support this more efficiently (Postgres, for instance)
|
559
574
|
old_index_def = indexes(table_name).detect { |i| i.name == old_name }
|
560
575
|
return unless old_index_def
|
561
|
-
|
562
|
-
|
576
|
+
add_index(table_name, old_index_def.columns, name: new_name, unique: old_index_def.unique)
|
577
|
+
remove_index(table_name, name: old_name)
|
563
578
|
end
|
564
579
|
|
565
580
|
def index_name(table_name, options) #:nodoc:
|
@@ -606,7 +621,7 @@ module ActiveRecord
|
|
606
621
|
index_options = options.delete(:index)
|
607
622
|
add_column(table_name, "#{ref_name}_id", :integer, options)
|
608
623
|
add_column(table_name, "#{ref_name}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic
|
609
|
-
add_index(table_name, polymorphic ? %w[id type].map{ |t| "#{ref_name}_#{t}" } : "#{ref_name}_id", index_options.is_a?(Hash) ? index_options :
|
624
|
+
add_index(table_name, polymorphic ? %w[id type].map{ |t| "#{ref_name}_#{t}" } : "#{ref_name}_id", index_options.is_a?(Hash) ? index_options : {}) if index_options
|
610
625
|
end
|
611
626
|
alias :add_belongs_to :add_reference
|
612
627
|
|
@@ -694,32 +709,12 @@ module ActiveRecord
|
|
694
709
|
end
|
695
710
|
end
|
696
711
|
|
697
|
-
def add_column_options!(sql, options) #:nodoc:
|
698
|
-
sql << " DEFAULT #{quote(options[:default], options[:column])}" if options_include_default?(options)
|
699
|
-
# must explicitly check for :null to allow change_column to work on migrations
|
700
|
-
if options[:null] == false
|
701
|
-
sql << " NOT NULL"
|
702
|
-
end
|
703
|
-
if options[:auto_increment] == true
|
704
|
-
sql << " AUTO_INCREMENT"
|
705
|
-
end
|
706
|
-
end
|
707
|
-
|
708
|
-
# SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
|
709
|
-
#
|
710
|
-
# distinct("posts.id", ["posts.created_at desc"])
|
711
|
-
#
|
712
|
-
def distinct(columns, order_by)
|
713
|
-
ActiveSupport::Deprecation.warn("#distinct is deprecated and shall be removed from future releases.")
|
714
|
-
"DISTINCT #{columns_for_distinct(columns, order_by)}"
|
715
|
-
end
|
716
|
-
|
717
712
|
# Given a set of columns and an ORDER BY clause, returns the columns for a SELECT DISTINCT.
|
718
713
|
# Both PostgreSQL and Oracle overrides this for custom DISTINCT syntax - they
|
719
714
|
# require the order columns appear in the SELECT.
|
720
715
|
#
|
721
716
|
# columns_for_distinct("posts.id", ["posts.created_at desc"])
|
722
|
-
def columns_for_distinct(columns, orders)
|
717
|
+
def columns_for_distinct(columns, orders) # :nodoc:
|
723
718
|
columns
|
724
719
|
end
|
725
720
|
|
@@ -727,9 +722,9 @@ module ActiveRecord
|
|
727
722
|
#
|
728
723
|
# add_timestamps(:suppliers)
|
729
724
|
#
|
730
|
-
def add_timestamps(table_name
|
731
|
-
add_column table_name, :created_at, :datetime
|
732
|
-
add_column table_name, :updated_at, :datetime
|
725
|
+
def add_timestamps(table_name)
|
726
|
+
add_column table_name, :created_at, :datetime
|
727
|
+
add_column table_name, :updated_at, :datetime
|
733
728
|
end
|
734
729
|
|
735
730
|
# Removes the timestamp columns (+created_at+ and +updated_at+) from the table definition.
|
@@ -741,10 +736,6 @@ module ActiveRecord
|
|
741
736
|
remove_column table_name, :created_at
|
742
737
|
end
|
743
738
|
|
744
|
-
def update_table_definition(table_name, base) #:nodoc:
|
745
|
-
Table.new(table_name, base)
|
746
|
-
end
|
747
|
-
|
748
739
|
protected
|
749
740
|
def add_index_sort_order(option_strings, column_names, options = {})
|
750
741
|
if options.is_a?(Hash) && order = options[:order]
|
@@ -779,37 +770,23 @@ module ActiveRecord
|
|
779
770
|
column_names = Array(column_name)
|
780
771
|
index_name = index_name(table_name, column: column_names)
|
781
772
|
|
782
|
-
|
783
|
-
options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type)
|
784
|
-
|
785
|
-
index_type = options[:unique] ? "UNIQUE" : ""
|
786
|
-
index_type = options[:type].to_s if options.key?(:type)
|
787
|
-
index_name = options[:name].to_s if options.key?(:name)
|
788
|
-
max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
|
789
|
-
|
790
|
-
if options.key?(:algorithm)
|
791
|
-
algorithm = index_algorithms.fetch(options[:algorithm]) {
|
792
|
-
raise ArgumentError.new("Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}")
|
793
|
-
}
|
794
|
-
end
|
773
|
+
options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type)
|
795
774
|
|
796
|
-
|
775
|
+
index_type = options[:unique] ? "UNIQUE" : ""
|
776
|
+
index_type = options[:type].to_s if options.key?(:type)
|
777
|
+
index_name = options[:name].to_s if options.key?(:name)
|
778
|
+
max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
|
797
779
|
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
message = "Passing a string as third argument of `add_index` is deprecated and will" +
|
804
|
-
" be removed in Rails 4.1." +
|
805
|
-
" Use add_index(#{table_name.inspect}, #{column_name.inspect}, unique: true) instead"
|
780
|
+
if options.key?(:algorithm)
|
781
|
+
algorithm = index_algorithms.fetch(options[:algorithm]) {
|
782
|
+
raise ArgumentError.new("Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}")
|
783
|
+
}
|
784
|
+
end
|
806
785
|
|
807
|
-
|
808
|
-
end
|
786
|
+
using = "USING #{options[:using]}" if options[:using].present?
|
809
787
|
|
810
|
-
|
811
|
-
|
812
|
-
algorithm = using = nil
|
788
|
+
if supports_partial_index?
|
789
|
+
index_options = options[:where] ? " WHERE #{options[:where]}" : ""
|
813
790
|
end
|
814
791
|
|
815
792
|
if index_name.length > max_index_length
|
@@ -841,12 +818,6 @@ module ActiveRecord
|
|
841
818
|
index_name
|
842
819
|
end
|
843
820
|
|
844
|
-
def columns_for_remove(table_name, *column_names)
|
845
|
-
ActiveSupport::Deprecation.warn("columns_for_remove is deprecated and will be removed in the future")
|
846
|
-
raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)") if column_names.blank?
|
847
|
-
column_names.map {|column_name| quote_column_name(column_name) }
|
848
|
-
end
|
849
|
-
|
850
821
|
def rename_table_indexes(table_name, new_name)
|
851
822
|
indexes(new_name).each do |index|
|
852
823
|
generated_index_name = index_name(table_name, column: index.columns)
|
@@ -869,20 +840,18 @@ module ActiveRecord
|
|
869
840
|
end
|
870
841
|
end
|
871
842
|
|
872
|
-
def quote_value(value, column)
|
873
|
-
column.sql_type ||= type_to_sql(column.type, column.limit, column.precision, column.scale)
|
874
|
-
|
875
|
-
quote(value, column)
|
876
|
-
end
|
877
|
-
|
878
843
|
private
|
879
|
-
def create_table_definition(name, temporary, options)
|
880
|
-
TableDefinition.new native_database_types, name, temporary, options
|
844
|
+
def create_table_definition(name, temporary, options, as = nil)
|
845
|
+
TableDefinition.new native_database_types, name, temporary, options, as
|
881
846
|
end
|
882
847
|
|
883
848
|
def create_alter_table(name)
|
884
849
|
AlterTable.new create_table_definition(name, false, {})
|
885
850
|
end
|
851
|
+
|
852
|
+
def update_table_definition(table_name, base)
|
853
|
+
Table.new(table_name, base)
|
854
|
+
end
|
886
855
|
end
|
887
856
|
end
|
888
857
|
end
|