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
data/lib/active_record/result.rb
CHANGED
@@ -8,12 +8,13 @@ module ActiveRecord
|
|
8
8
|
class Result
|
9
9
|
include Enumerable
|
10
10
|
|
11
|
-
attr_reader :columns, :rows
|
11
|
+
attr_reader :columns, :rows, :column_types
|
12
12
|
|
13
|
-
def initialize(columns, rows)
|
14
|
-
@columns
|
15
|
-
@rows
|
16
|
-
@hash_rows
|
13
|
+
def initialize(columns, rows, column_types = {})
|
14
|
+
@columns = columns
|
15
|
+
@rows = rows
|
16
|
+
@hash_rows = nil
|
17
|
+
@column_types = column_types
|
17
18
|
end
|
18
19
|
|
19
20
|
def each
|
@@ -24,15 +25,55 @@ module ActiveRecord
|
|
24
25
|
hash_rows
|
25
26
|
end
|
26
27
|
|
28
|
+
alias :map! :map
|
29
|
+
alias :collect! :map
|
30
|
+
|
31
|
+
# Returns true if there are no records.
|
32
|
+
def empty?
|
33
|
+
rows.empty?
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_ary
|
37
|
+
hash_rows
|
38
|
+
end
|
39
|
+
|
40
|
+
def [](idx)
|
41
|
+
hash_rows[idx]
|
42
|
+
end
|
43
|
+
|
44
|
+
def last
|
45
|
+
hash_rows.last
|
46
|
+
end
|
47
|
+
|
48
|
+
def initialize_copy(other)
|
49
|
+
@columns = columns.dup
|
50
|
+
@rows = rows.dup
|
51
|
+
@hash_rows = nil
|
52
|
+
end
|
53
|
+
|
27
54
|
private
|
28
55
|
def hash_rows
|
29
56
|
@hash_rows ||=
|
30
57
|
begin
|
31
58
|
# We freeze the strings to prevent them getting duped when
|
32
|
-
# used as keys in ActiveRecord::
|
59
|
+
# used as keys in ActiveRecord::Base's @attributes hash
|
33
60
|
columns = @columns.map { |c| c.dup.freeze }
|
34
61
|
@rows.map { |row|
|
35
|
-
Hash[columns.zip(row)]
|
62
|
+
# In the past we used Hash[columns.zip(row)]
|
63
|
+
# though elegant, the verbose way is much more efficient
|
64
|
+
# both time and memory wise cause it avoids a big array allocation
|
65
|
+
# this method is called a lot and needs to be micro optimised
|
66
|
+
hash = {}
|
67
|
+
|
68
|
+
index = 0
|
69
|
+
length = columns.length
|
70
|
+
|
71
|
+
while index < length
|
72
|
+
hash[columns[index]] = row[index]
|
73
|
+
index += 1
|
74
|
+
end
|
75
|
+
|
76
|
+
hash
|
36
77
|
}
|
37
78
|
end
|
38
79
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'active_support/per_thread_registry'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
# This is a thread locals registry for Active Record. For example:
|
5
|
+
#
|
6
|
+
# ActiveRecord::RuntimeRegistry.connection_handler
|
7
|
+
#
|
8
|
+
# returns the connection handler local to the current thread.
|
9
|
+
#
|
10
|
+
# See the documentation of <tt>ActiveSupport::PerThreadRegistry</tt>
|
11
|
+
# for further details.
|
12
|
+
class RuntimeRegistry # :nodoc:
|
13
|
+
extend ActiveSupport::PerThreadRegistry
|
14
|
+
|
15
|
+
attr_accessor :connection_handler, :sql_runtime, :connection_id
|
16
|
+
end
|
17
|
+
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'active_support/concern'
|
2
|
-
|
3
1
|
module ActiveRecord
|
4
2
|
module Sanitization
|
5
3
|
extend ActiveSupport::Concern
|
@@ -19,7 +17,7 @@ module ActiveRecord
|
|
19
17
|
# Accepts an array, hash, or string of SQL conditions and sanitizes
|
20
18
|
# them into a valid SQL fragment for a WHERE clause.
|
21
19
|
# ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
|
22
|
-
# { :
|
20
|
+
# { name: "foo'bar", group_id: 4 } returns "name='foo''bar' and group_id='4'"
|
23
21
|
# "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'"
|
24
22
|
def sanitize_sql_for_conditions(condition, table_name = self.table_name)
|
25
23
|
return nil if condition.blank?
|
@@ -34,12 +32,12 @@ module ActiveRecord
|
|
34
32
|
|
35
33
|
# Accepts an array, hash, or string of SQL conditions and sanitizes
|
36
34
|
# them into a valid SQL fragment for a SET clause.
|
37
|
-
# { :
|
38
|
-
def sanitize_sql_for_assignment(assignments)
|
35
|
+
# { name: nil, group_id: 4 } returns "name = NULL , group_id='4'"
|
36
|
+
def sanitize_sql_for_assignment(assignments, default_table_name = self.table_name)
|
39
37
|
case assignments
|
40
|
-
|
41
|
-
|
42
|
-
|
38
|
+
when Array; sanitize_sql_array(assignments)
|
39
|
+
when Hash; sanitize_sql_hash_for_assignment(assignments, default_table_name)
|
40
|
+
else assignments
|
43
41
|
end
|
44
42
|
end
|
45
43
|
|
@@ -48,17 +46,17 @@ module ActiveRecord
|
|
48
46
|
# aggregate attribute values.
|
49
47
|
# Given:
|
50
48
|
# class Person < ActiveRecord::Base
|
51
|
-
# composed_of :address, :
|
52
|
-
# :
|
49
|
+
# composed_of :address, class_name: "Address",
|
50
|
+
# mapping: [%w(address_street street), %w(address_city city)]
|
53
51
|
# end
|
54
52
|
# Then:
|
55
|
-
# { :
|
56
|
-
# # => { :
|
53
|
+
# { address: Address.new("813 abc st.", "chicago") }
|
54
|
+
# # => { address_street: "813 abc st.", address_city: "chicago" }
|
57
55
|
def expand_hash_conditions_for_aggregates(attrs)
|
58
56
|
expanded_attrs = {}
|
59
57
|
attrs.each do |attr, value|
|
60
|
-
|
61
|
-
mapping =
|
58
|
+
if aggregation = reflect_on_aggregation(attr.to_sym)
|
59
|
+
mapping = aggregation.mapping
|
62
60
|
mapping.each do |field_attr, aggregate_attr|
|
63
61
|
if mapping.size == 1 && !value.respond_to?(aggregate_attr)
|
64
62
|
expanded_attrs[field_attr] = value
|
@@ -74,35 +72,36 @@ module ActiveRecord
|
|
74
72
|
end
|
75
73
|
|
76
74
|
# Sanitizes a hash of attribute/value pairs into SQL conditions for a WHERE clause.
|
77
|
-
# { :
|
75
|
+
# { name: "foo'bar", group_id: 4 }
|
78
76
|
# # => "name='foo''bar' and group_id= 4"
|
79
|
-
# { :
|
77
|
+
# { status: nil, group_id: [1,2,3] }
|
80
78
|
# # => "status IS NULL and group_id IN (1,2,3)"
|
81
|
-
# { :
|
79
|
+
# { age: 13..18 }
|
82
80
|
# # => "age BETWEEN 13 AND 18"
|
83
81
|
# { 'other_records.id' => 7 }
|
84
82
|
# # => "`other_records`.`id` = 7"
|
85
|
-
# { :
|
83
|
+
# { other_records: { id: 7 } }
|
86
84
|
# # => "`other_records`.`id` = 7"
|
87
85
|
# And for value objects on a composed_of relationship:
|
88
|
-
# { :
|
86
|
+
# { address: Address.new("123 abc st.", "chicago") }
|
89
87
|
# # => "address_street='123 abc st.' and address_city='chicago'"
|
90
88
|
def sanitize_sql_hash_for_conditions(attrs, default_table_name = self.table_name)
|
91
89
|
attrs = expand_hash_conditions_for_aggregates(attrs)
|
92
90
|
|
93
|
-
table = Arel::Table.new(table_name).alias(default_table_name)
|
94
|
-
PredicateBuilder.build_from_hash(
|
91
|
+
table = Arel::Table.new(table_name, arel_engine).alias(default_table_name)
|
92
|
+
PredicateBuilder.build_from_hash(self, attrs, table).map { |b|
|
95
93
|
connection.visitor.accept b
|
96
94
|
}.join(' AND ')
|
97
95
|
end
|
98
96
|
alias_method :sanitize_sql_hash, :sanitize_sql_hash_for_conditions
|
99
97
|
|
100
98
|
# Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
|
101
|
-
# { :
|
99
|
+
# { status: nil, group_id: 1 }
|
102
100
|
# # => "status = NULL , group_id = 1"
|
103
|
-
def sanitize_sql_hash_for_assignment(attrs)
|
101
|
+
def sanitize_sql_hash_for_assignment(attrs, table)
|
102
|
+
c = connection
|
104
103
|
attrs.map do |attr, value|
|
105
|
-
"#{
|
104
|
+
"#{c.quote_table_name_for_assignment(table, attr)} = #{quote_bound_value(value, c, columns_hash[attr.to_s])}"
|
106
105
|
end.join(', ')
|
107
106
|
end
|
108
107
|
|
@@ -128,7 +127,17 @@ module ActiveRecord
|
|
128
127
|
raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
|
129
128
|
bound = values.dup
|
130
129
|
c = connection
|
131
|
-
statement.gsub('?')
|
130
|
+
statement.gsub('?') do
|
131
|
+
replace_bind_variable(bound.shift, c)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def replace_bind_variable(value, c = connection) #:nodoc:
|
136
|
+
if ActiveRecord::Relation === value
|
137
|
+
value.to_sql
|
138
|
+
else
|
139
|
+
quote_bound_value(value, c)
|
140
|
+
end
|
132
141
|
end
|
133
142
|
|
134
143
|
def replace_named_bind_variables(statement, bind_vars) #:nodoc:
|
@@ -136,32 +145,17 @@ module ActiveRecord
|
|
136
145
|
if $1 == ':' # skip postgresql casts
|
137
146
|
$& # return the whole match
|
138
147
|
elsif bind_vars.include?(match = $2.to_sym)
|
139
|
-
|
148
|
+
replace_bind_variable(bind_vars[match])
|
140
149
|
else
|
141
150
|
raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
|
142
151
|
end
|
143
152
|
end
|
144
153
|
end
|
145
154
|
|
146
|
-
def
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
next if var.is_a?(Hash)
|
151
|
-
|
152
|
-
if var.is_a?(Range)
|
153
|
-
expanded << var.first
|
154
|
-
expanded << var.last
|
155
|
-
else
|
156
|
-
expanded << var
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
expanded
|
161
|
-
end
|
162
|
-
|
163
|
-
def quote_bound_value(value, c = connection) #:nodoc:
|
164
|
-
if value.respond_to?(:map) && !value.acts_like?(:string)
|
155
|
+
def quote_bound_value(value, c = connection, column = nil) #:nodoc:
|
156
|
+
if column
|
157
|
+
c.quote(value, column)
|
158
|
+
elsif value.respond_to?(:map) && !value.acts_like?(:string)
|
165
159
|
if value.respond_to?(:empty?) && value.empty?
|
166
160
|
c.quote(nil)
|
167
161
|
else
|
@@ -180,15 +174,8 @@ module ActiveRecord
|
|
180
174
|
end
|
181
175
|
|
182
176
|
# TODO: Deprecate this
|
183
|
-
def quoted_id
|
184
|
-
quote_value(id, column_for_attribute(self.class.primary_key))
|
185
|
-
end
|
186
|
-
|
187
|
-
private
|
188
|
-
|
189
|
-
# Quote strings appropriately for SQL statements.
|
190
|
-
def quote_value(value, column = nil)
|
191
|
-
self.class.connection.quote(value, column)
|
177
|
+
def quoted_id
|
178
|
+
self.class.quote_value(id, column_for_attribute(self.class.primary_key))
|
192
179
|
end
|
193
180
|
end
|
194
181
|
end
|
data/lib/active_record/schema.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'active_support/core_ext/object/blank'
|
2
1
|
|
3
2
|
module ActiveRecord
|
4
3
|
# = Active Record Schema
|
@@ -12,16 +11,16 @@ module ActiveRecord
|
|
12
11
|
#
|
13
12
|
# ActiveRecord::Schema.define do
|
14
13
|
# create_table :authors do |t|
|
15
|
-
# t.string :name, :
|
14
|
+
# t.string :name, null: false
|
16
15
|
# end
|
17
16
|
#
|
18
17
|
# add_index :authors, :name, :unique
|
19
18
|
#
|
20
19
|
# create_table :posts do |t|
|
21
|
-
# t.integer :author_id, :
|
20
|
+
# t.integer :author_id, null: false
|
22
21
|
# t.string :subject
|
23
22
|
# t.text :body
|
24
|
-
# t.boolean :private, :
|
23
|
+
# t.boolean :private, default: false
|
25
24
|
# end
|
26
25
|
#
|
27
26
|
# add_index :posts, :author_id
|
@@ -30,10 +29,24 @@ module ActiveRecord
|
|
30
29
|
# ActiveRecord::Schema is only supported by database adapters that also
|
31
30
|
# support migrations, the two features being very similar.
|
32
31
|
class Schema < Migration
|
32
|
+
|
33
|
+
# Returns the migrations paths.
|
34
|
+
#
|
35
|
+
# ActiveRecord::Schema.new.migrations_paths
|
36
|
+
# # => ["db/migrate"] # Rails migration path by default.
|
33
37
|
def migrations_paths
|
34
38
|
ActiveRecord::Migrator.migrations_paths
|
35
39
|
end
|
36
40
|
|
41
|
+
def define(info, &block) # :nodoc:
|
42
|
+
instance_eval(&block)
|
43
|
+
|
44
|
+
unless info[:version].blank?
|
45
|
+
initialize_schema_migrations_table
|
46
|
+
connection.assume_migrated_upto_version(info[:version], migrations_paths)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
37
50
|
# Eval the given block. All methods available to the current connection
|
38
51
|
# adapter are available within the block, so you can easily use the
|
39
52
|
# database definition DSL to build up your schema (+create_table+,
|
@@ -42,17 +55,11 @@ module ActiveRecord
|
|
42
55
|
# The +info+ hash is optional, and if given is used to define metadata
|
43
56
|
# about the current schema (currently, only the schema's version):
|
44
57
|
#
|
45
|
-
# ActiveRecord::Schema.define(:
|
58
|
+
# ActiveRecord::Schema.define(version: 20380119000001) do
|
46
59
|
# ...
|
47
60
|
# end
|
48
61
|
def self.define(info={}, &block)
|
49
|
-
|
50
|
-
schema.instance_eval(&block)
|
51
|
-
|
52
|
-
unless info[:version].blank?
|
53
|
-
initialize_schema_migrations_table
|
54
|
-
assume_migrated_upto_version(info[:version], schema.migrations_paths)
|
55
|
-
end
|
62
|
+
new.define(info, &block)
|
56
63
|
end
|
57
64
|
end
|
58
65
|
end
|
@@ -24,6 +24,7 @@ module ActiveRecord
|
|
24
24
|
|
25
25
|
def dump(stream)
|
26
26
|
header(stream)
|
27
|
+
extensions(stream)
|
27
28
|
tables(stream)
|
28
29
|
trailer(stream)
|
29
30
|
stream
|
@@ -38,7 +39,7 @@ module ActiveRecord
|
|
38
39
|
end
|
39
40
|
|
40
41
|
def header(stream)
|
41
|
-
define_params = @version ? ":
|
42
|
+
define_params = @version ? "version: #{@version}" : ""
|
42
43
|
|
43
44
|
if stream.respond_to?(:external_encoding) && stream.external_encoding
|
44
45
|
stream.puts "# encoding: #{stream.external_encoding.name}"
|
@@ -55,7 +56,7 @@ module ActiveRecord
|
|
55
56
|
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
|
56
57
|
# you'll amass, the slower it'll run and the greater likelihood for issues).
|
57
58
|
#
|
58
|
-
# It's strongly recommended
|
59
|
+
# It's strongly recommended that you check this file into your version control system.
|
59
60
|
|
60
61
|
ActiveRecord::Schema.define(#{define_params}) do
|
61
62
|
|
@@ -66,6 +67,18 @@ HEADER
|
|
66
67
|
stream.puts "end"
|
67
68
|
end
|
68
69
|
|
70
|
+
def extensions(stream)
|
71
|
+
return unless @connection.supports_extensions?
|
72
|
+
extensions = @connection.extensions
|
73
|
+
if extensions.any?
|
74
|
+
stream.puts " # These are extensions that must be enabled in order to support this database"
|
75
|
+
extensions.each do |extension|
|
76
|
+
stream.puts " enable_extension #{extension.inspect}"
|
77
|
+
end
|
78
|
+
stream.puts
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
69
82
|
def tables(stream)
|
70
83
|
@connection.tables.sort.each do |tbl|
|
71
84
|
next if ['schema_migrations', ignore_tables].flatten.any? do |ignored|
|
@@ -93,44 +106,36 @@ HEADER
|
|
93
106
|
end
|
94
107
|
|
95
108
|
tbl.print " create_table #{remove_prefix_and_suffix(table).inspect}"
|
96
|
-
|
109
|
+
pkcol = columns.detect { |c| c.name == pk }
|
110
|
+
if pkcol
|
97
111
|
if pk != 'id'
|
98
|
-
tbl.print %Q(, :
|
112
|
+
tbl.print %Q(, primary_key: "#{pk}")
|
113
|
+
elsif pkcol.sql_type == 'uuid'
|
114
|
+
tbl.print ", id: :uuid"
|
115
|
+
tbl.print %Q(, default: "#{pkcol.default_function}") if pkcol.default_function
|
99
116
|
end
|
100
117
|
else
|
101
|
-
tbl.print ", :
|
118
|
+
tbl.print ", id: false"
|
102
119
|
end
|
103
|
-
tbl.print ", :
|
120
|
+
tbl.print ", force: true"
|
104
121
|
tbl.puts " do |t|"
|
105
122
|
|
106
123
|
# then dump all non-primary key columns
|
107
124
|
column_specs = columns.map do |column|
|
108
|
-
raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'"
|
125
|
+
raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" unless @connection.valid_type?(column.type)
|
109
126
|
next if column.name == pk
|
110
|
-
|
111
|
-
spec[:name] = column.name.inspect
|
112
|
-
|
113
|
-
# AR has an optimization which handles zero-scale decimals as integers. This
|
114
|
-
# code ensures that the dumper still dumps the column as a decimal.
|
115
|
-
spec[:type] = if column.type == :integer && [/^numeric/, /^decimal/].any? { |e| e.match(column.sql_type) }
|
116
|
-
'decimal'
|
117
|
-
else
|
118
|
-
column.type.to_s
|
119
|
-
end
|
120
|
-
spec[:limit] = column.limit.inspect if column.limit != @types[column.type][:limit] && spec[:type] != 'decimal'
|
121
|
-
spec[:precision] = column.precision.inspect if column.precision
|
122
|
-
spec[:scale] = column.scale.inspect if column.scale
|
123
|
-
spec[:null] = 'false' unless column.null
|
124
|
-
spec[:default] = default_string(column.default) if column.has_default?
|
125
|
-
(spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k.inspect} => ")}
|
126
|
-
spec
|
127
|
+
@connection.column_spec(column, @types)
|
127
128
|
end.compact
|
128
129
|
|
129
130
|
# find all migration keys used in this table
|
130
|
-
keys =
|
131
|
+
keys = @connection.migration_keys
|
131
132
|
|
132
133
|
# figure out the lengths for each column based on above keys
|
133
|
-
lengths = keys.map{ |key|
|
134
|
+
lengths = keys.map { |key|
|
135
|
+
column_specs.map { |spec|
|
136
|
+
spec[key] ? spec[key].length + 2 : 0
|
137
|
+
}.max
|
138
|
+
}
|
134
139
|
|
135
140
|
# the string we're going to sprintf our values against, with standardized column widths
|
136
141
|
format_string = lengths.map{ |len| "%-#{len}s" }
|
@@ -166,32 +171,27 @@ HEADER
|
|
166
171
|
stream
|
167
172
|
end
|
168
173
|
|
169
|
-
def default_string(value)
|
170
|
-
case value
|
171
|
-
when BigDecimal
|
172
|
-
value.to_s
|
173
|
-
when Date, DateTime, Time
|
174
|
-
"'" + value.to_s(:db) + "'"
|
175
|
-
else
|
176
|
-
value.inspect
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
174
|
def indexes(table, stream)
|
181
175
|
if (indexes = @connection.indexes(table)).any?
|
182
176
|
add_index_statements = indexes.map do |index|
|
183
177
|
statement_parts = [
|
184
178
|
('add_index ' + remove_prefix_and_suffix(index.table).inspect),
|
185
179
|
index.columns.inspect,
|
186
|
-
(':
|
180
|
+
('name: ' + index.name.inspect),
|
187
181
|
]
|
188
|
-
statement_parts << ':
|
182
|
+
statement_parts << 'unique: true' if index.unique
|
189
183
|
|
190
184
|
index_lengths = (index.lengths || []).compact
|
191
|
-
statement_parts << (':
|
185
|
+
statement_parts << ('length: ' + Hash[index.columns.zip(index.lengths)].inspect) unless index_lengths.empty?
|
192
186
|
|
193
187
|
index_orders = (index.orders || {})
|
194
|
-
statement_parts << (':
|
188
|
+
statement_parts << ('order: ' + index.orders.inspect) unless index_orders.empty?
|
189
|
+
|
190
|
+
statement_parts << ('where: ' + index.where.inspect) if index.where
|
191
|
+
|
192
|
+
statement_parts << ('using: ' + index.using.inspect) if index.using
|
193
|
+
|
194
|
+
statement_parts << ('type: ' + index.type.inspect) if index.type
|
195
195
|
|
196
196
|
' ' + statement_parts.join(', ')
|
197
197
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'active_record/scoping/default'
|
2
|
+
require 'active_record/scoping/named'
|
3
|
+
require 'active_record/base'
|
4
|
+
|
5
|
+
module ActiveRecord
|
6
|
+
class SchemaMigration < ActiveRecord::Base
|
7
|
+
def self.primary_key
|
8
|
+
nil
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.table_name
|
12
|
+
"#{Base.table_name_prefix}schema_migrations#{Base.table_name_suffix}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.index_name
|
16
|
+
"#{Base.table_name_prefix}unique_schema_migrations#{Base.table_name_suffix}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.create_table(limit=nil)
|
20
|
+
unless connection.table_exists?(table_name)
|
21
|
+
version_options = {null: false}
|
22
|
+
version_options[:limit] = limit if limit
|
23
|
+
|
24
|
+
connection.create_table(table_name, id: false) do |t|
|
25
|
+
t.column :version, :string, version_options
|
26
|
+
end
|
27
|
+
connection.add_index table_name, :version, unique: true, name: index_name
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.drop_table
|
32
|
+
if connection.table_exists?(table_name)
|
33
|
+
connection.remove_index table_name, name: index_name
|
34
|
+
connection.drop_table(table_name)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.normalize_migration_number(number)
|
39
|
+
"%.3d" % number.to_i
|
40
|
+
end
|
41
|
+
|
42
|
+
def version
|
43
|
+
super.to_i
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|