activerecord 5.0.0.beta3 → 5.0.0.beta4
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 +225 -8
- data/examples/performance.rb +0 -1
- data/examples/simple.rb +0 -1
- data/lib/active_record.rb +0 -1
- data/lib/active_record/associations.rb +10 -6
- data/lib/active_record/associations/association.rb +1 -1
- data/lib/active_record/associations/builder/collection_association.rb +5 -1
- data/lib/active_record/associations/join_dependency.rb +1 -1
- data/lib/active_record/associations/preloader.rb +1 -0
- data/lib/active_record/associations/preloader/through_association.rb +15 -8
- data/lib/active_record/attribute/user_provided_default.rb +10 -5
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
- data/lib/active_record/attributes.rb +3 -3
- data/lib/active_record/autosave_association.rb +1 -1
- data/lib/active_record/base.rb +2 -1
- data/lib/active_record/collection_cache_key.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +0 -19
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +7 -8
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +7 -1
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +16 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +9 -4
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +20 -10
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +85 -20
- data/lib/active_record/connection_adapters/abstract/transaction.rb +13 -1
- data/lib/active_record/connection_adapters/abstract_adapter.rb +37 -16
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +119 -108
- data/lib/active_record/connection_adapters/column.rb +5 -6
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +27 -6
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -13
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +18 -55
- data/lib/active_record/connection_adapters/postgresql/column.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +7 -10
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +65 -25
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +59 -30
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +7 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +19 -56
- data/lib/active_record/connection_adapters/statement_pool.rb +6 -4
- data/lib/active_record/core.rb +13 -4
- data/lib/active_record/enum.rb +1 -1
- data/lib/active_record/errors.rb +9 -0
- data/lib/active_record/fixture_set/file.rb +7 -1
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/locking/optimistic.rb +4 -0
- data/lib/active_record/log_subscriber.rb +1 -1
- data/lib/active_record/migration.rb +4 -4
- data/lib/active_record/migration/compatibility.rb +1 -1
- data/lib/active_record/model_schema.rb +12 -0
- data/lib/active_record/nested_attributes.rb +1 -1
- data/lib/active_record/persistence.rb +1 -1
- data/lib/active_record/query_cache.rb +13 -16
- data/lib/active_record/querying.rb +1 -1
- data/lib/active_record/railtie.rb +8 -11
- data/lib/active_record/railties/databases.rake +5 -5
- data/lib/active_record/reflection.rb +21 -4
- data/lib/active_record/relation.rb +10 -10
- data/lib/active_record/relation/batches.rb +29 -9
- data/lib/active_record/relation/delegation.rb +0 -1
- data/lib/active_record/relation/finder_methods.rb +27 -8
- data/lib/active_record/relation/predicate_builder.rb +4 -2
- data/lib/active_record/relation/where_clause.rb +2 -1
- data/lib/active_record/schema_dumper.rb +39 -24
- data/lib/active_record/scoping/default.rb +2 -1
- data/lib/active_record/suppressor.rb +5 -1
- data/lib/active_record/tasks/database_tasks.rb +6 -4
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/type.rb +1 -1
- data/lib/active_record/type/internal/abstract_json.rb +1 -5
- data/lib/active_record/type/time.rb +12 -0
- data/lib/active_record/validations/uniqueness.rb +1 -4
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -11
- data/lib/rails/generators/active_record/model/templates/application_record.rb +2 -0
- metadata +11 -8
@@ -119,7 +119,7 @@ module ActiveRecord
|
|
119
119
|
#
|
120
120
|
# class MoneyType < ActiveRecord::Type::Integer
|
121
121
|
# def cast(value)
|
122
|
-
# if !value.kind_of(Numeric) && value.include?('$')
|
122
|
+
# if !value.kind_of?(Numeric) && value.include?('$')
|
123
123
|
# price_in_dollars = value.gsub(/\$/, '').to_f
|
124
124
|
# super(price_in_dollars * 100)
|
125
125
|
# else
|
@@ -154,7 +154,7 @@ module ActiveRecord
|
|
154
154
|
# end
|
155
155
|
#
|
156
156
|
# class MoneyType < Type::Value
|
157
|
-
# def initialize(currency_converter)
|
157
|
+
# def initialize(currency_converter:)
|
158
158
|
# @currency_converter = currency_converter
|
159
159
|
# end
|
160
160
|
#
|
@@ -171,7 +171,7 @@ module ActiveRecord
|
|
171
171
|
#
|
172
172
|
# class Product < ActiveRecord::Base
|
173
173
|
# currency_converter = ConversionRatesFromTheInternet.new
|
174
|
-
# attribute :price_in_bitcoins, :money, currency_converter
|
174
|
+
# attribute :price_in_bitcoins, :money, currency_converter: currency_converter
|
175
175
|
# end
|
176
176
|
#
|
177
177
|
# Product.where(price_in_bitcoins: Money.new(5, "USD"))
|
data/lib/active_record/base.rb
CHANGED
@@ -169,7 +169,8 @@ module ActiveRecord #:nodoc:
|
|
169
169
|
# ActiveRecord::RecordNotFound error if they do not return any records,
|
170
170
|
# like <tt>Person.find_by_last_name!</tt>.
|
171
171
|
#
|
172
|
-
# It's also possible to use multiple attributes in the same
|
172
|
+
# It's also possible to use multiple attributes in the same <tt>find_by_</tt> by separating them with
|
173
|
+
# "_and_".
|
173
174
|
#
|
174
175
|
# Person.find_by(user_name: user_name, password: password)
|
175
176
|
# Person.find_by_user_name_and_password(user_name, password) # with dynamic finder
|
@@ -16,7 +16,7 @@ module ActiveRecord
|
|
16
16
|
|
17
17
|
query = collection
|
18
18
|
.unscope(:select)
|
19
|
-
.select("COUNT(*) AS size", "MAX(#{column}) AS timestamp")
|
19
|
+
.select("COUNT(*) AS #{connection.quote_column_name("size")}", "MAX(#{column}) AS timestamp")
|
20
20
|
.unscope(:order)
|
21
21
|
result = connection.select_one(query)
|
22
22
|
|
@@ -951,24 +951,5 @@ module ActiveRecord
|
|
951
951
|
owner_to_pool && owner_to_pool[owner.name]
|
952
952
|
end
|
953
953
|
end
|
954
|
-
|
955
|
-
class ConnectionManagement
|
956
|
-
def initialize(app)
|
957
|
-
@app = app
|
958
|
-
end
|
959
|
-
|
960
|
-
def call(env)
|
961
|
-
testing = env['rack.test']
|
962
|
-
|
963
|
-
status, headers, body = @app.call(env)
|
964
|
-
proxy = ::Rack::BodyProxy.new(body) do
|
965
|
-
ActiveRecord::Base.clear_active_connections! unless testing
|
966
|
-
end
|
967
|
-
[status, headers, proxy]
|
968
|
-
rescue Exception
|
969
|
-
ActiveRecord::Base.clear_active_connections! unless testing
|
970
|
-
raise
|
971
|
-
end
|
972
|
-
end
|
973
954
|
end
|
974
955
|
end
|
@@ -66,8 +66,8 @@ module ActiveRecord
|
|
66
66
|
# Returns an array of arrays containing the field values.
|
67
67
|
# Order is the same as that returned by +columns+.
|
68
68
|
def select_rows(sql, name = nil, binds = [])
|
69
|
+
exec_query(sql, name, binds).rows
|
69
70
|
end
|
70
|
-
undef_method :select_rows
|
71
71
|
|
72
72
|
# Executes the SQL statement in the context of this connection and returns
|
73
73
|
# the raw result from the connection adapter.
|
@@ -75,13 +75,14 @@ module ActiveRecord
|
|
75
75
|
# method may be manually memory managed. Consider using the exec_query
|
76
76
|
# wrapper instead.
|
77
77
|
def execute(sql, name = nil)
|
78
|
+
raise NotImplementedError
|
78
79
|
end
|
79
|
-
undef_method :execute
|
80
80
|
|
81
81
|
# Executes +sql+ statement in the context of this connection using
|
82
82
|
# +binds+ as the bind substitutes. +name+ is logged along with
|
83
83
|
# the executed +sql+ statement.
|
84
84
|
def exec_query(sql, name = 'SQL', binds = [], prepare: false)
|
85
|
+
raise NotImplementedError
|
85
86
|
end
|
86
87
|
|
87
88
|
# Executes insert +sql+ statement in the context of this connection using
|
@@ -125,18 +126,21 @@ module ActiveRecord
|
|
125
126
|
end
|
126
127
|
alias create insert
|
127
128
|
alias insert_sql insert
|
129
|
+
deprecate insert_sql: :insert
|
128
130
|
|
129
131
|
# Executes the update statement and returns the number of rows affected.
|
130
132
|
def update(arel, name = nil, binds = [])
|
131
133
|
exec_update(to_sql(arel, binds), name, binds)
|
132
134
|
end
|
133
135
|
alias update_sql update
|
136
|
+
deprecate update_sql: :update
|
134
137
|
|
135
138
|
# Executes the delete statement and returns the number of rows affected.
|
136
139
|
def delete(arel, name = nil, binds = [])
|
137
140
|
exec_delete(to_sql(arel, binds), name, binds)
|
138
141
|
end
|
139
142
|
alias delete_sql delete
|
143
|
+
deprecate delete_sql: :delete
|
140
144
|
|
141
145
|
# Returns +true+ when the connection adapter supports prepared statement
|
142
146
|
# caching, otherwise returns +false+
|
@@ -217,9 +221,7 @@ module ActiveRecord
|
|
217
221
|
# * You are creating a nested (savepoint) transaction
|
218
222
|
#
|
219
223
|
# The mysql2 and postgresql adapters support setting the transaction
|
220
|
-
# isolation level.
|
221
|
-
# because they are affected by a bug[http://bugs.mysql.com/bug.php?id=39170]
|
222
|
-
# which means the isolation level gets persisted outside the transaction.
|
224
|
+
# isolation level.
|
223
225
|
def transaction(requires_new: nil, isolation: nil, joinable: true)
|
224
226
|
if !requires_new && current_transaction.joinable?
|
225
227
|
if isolation
|
@@ -289,9 +291,6 @@ module ActiveRecord
|
|
289
291
|
exec_rollback_to_savepoint(name)
|
290
292
|
end
|
291
293
|
|
292
|
-
def exec_rollback_to_savepoint(name = nil) #:nodoc:
|
293
|
-
end
|
294
|
-
|
295
294
|
def default_sequence_name(table, column)
|
296
295
|
nil
|
297
296
|
end
|
@@ -65,7 +65,7 @@ module ActiveRecord
|
|
65
65
|
if @query_cache_enabled && !locked?(arel)
|
66
66
|
arel, binds = binds_from_relation arel, binds
|
67
67
|
sql = to_sql(arel, binds)
|
68
|
-
cache_sql(sql, binds) { super(sql, name, binds, preparable:
|
68
|
+
cache_sql(sql, binds) { super(sql, name, binds, preparable: preparable) }
|
69
69
|
else
|
70
70
|
super
|
71
71
|
end
|
@@ -82,7 +82,7 @@ module ActiveRecord
|
|
82
82
|
|
83
83
|
# Quotes the column name. Defaults to no quoting.
|
84
84
|
def quote_column_name(column_name)
|
85
|
-
column_name
|
85
|
+
column_name.to_s
|
86
86
|
end
|
87
87
|
|
88
88
|
# Quotes the table name. Defaults to column name quoting.
|
@@ -146,6 +146,10 @@ module ActiveRecord
|
|
146
146
|
end
|
147
147
|
end
|
148
148
|
|
149
|
+
def quoted_time(value) # :nodoc:
|
150
|
+
quoted_date(value).sub(/\A2000-01-01 /, '')
|
151
|
+
end
|
152
|
+
|
149
153
|
def prepare_binds_for_database(binds) # :nodoc:
|
150
154
|
binds.map(&:value_for_database)
|
151
155
|
end
|
@@ -166,6 +170,7 @@ module ActiveRecord
|
|
166
170
|
# BigDecimals need to be put in a non-normalized form and quoted.
|
167
171
|
when BigDecimal then value.to_s('F')
|
168
172
|
when Numeric, ActiveSupport::Duration then value.to_s
|
173
|
+
when Type::Time::Value then "'#{quoted_time(value)}'"
|
169
174
|
when Date, Time then "'#{quoted_date(value)}'"
|
170
175
|
when Symbol then "'#{quote_string(value.to_s)}'"
|
171
176
|
when Class then "'#{value}'"
|
@@ -181,6 +186,7 @@ module ActiveRecord
|
|
181
186
|
when false then unquoted_false
|
182
187
|
# BigDecimals need to be put in a non-normalized form and quoted.
|
183
188
|
when BigDecimal then value.to_s('F')
|
189
|
+
when Type::Time::Value then quoted_time(value)
|
184
190
|
when Date, Time then quoted_date(value)
|
185
191
|
when *types_which_need_no_typecasting
|
186
192
|
value
|
@@ -53,8 +53,8 @@ module ActiveRecord
|
|
53
53
|
statements.concat(o.foreign_keys.map { |to_table, options| foreign_key_in_create(o.name, to_table, options) })
|
54
54
|
end
|
55
55
|
|
56
|
-
create_sql << "(#{statements.join(', ')})
|
57
|
-
create_sql
|
56
|
+
create_sql << "(#{statements.join(', ')})" if statements.present?
|
57
|
+
add_table_options!(create_sql, table_options(o))
|
58
58
|
create_sql << " AS #{@conn.to_sql(o.as)}" if o.as
|
59
59
|
create_sql
|
60
60
|
end
|
@@ -82,6 +82,19 @@ module ActiveRecord
|
|
82
82
|
"DROP CONSTRAINT #{quote_column_name(name)}"
|
83
83
|
end
|
84
84
|
|
85
|
+
def table_options(o)
|
86
|
+
table_options = {}
|
87
|
+
table_options[:comment] = o.comment
|
88
|
+
table_options[:options] = o.options
|
89
|
+
table_options
|
90
|
+
end
|
91
|
+
|
92
|
+
def add_table_options!(create_sql, options)
|
93
|
+
if options_sql = options[:options]
|
94
|
+
create_sql << " #{options_sql}"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
85
98
|
def column_options(o)
|
86
99
|
column_options = {}
|
87
100
|
column_options[:null] = o.null unless o.null.nil?
|
@@ -92,6 +105,7 @@ module ActiveRecord
|
|
92
105
|
column_options[:auto_increment] = o.auto_increment
|
93
106
|
column_options[:primary_key] = o.primary_key
|
94
107
|
column_options[:collation] = o.collation
|
108
|
+
column_options[:comment] = o.comment
|
95
109
|
column_options
|
96
110
|
end
|
97
111
|
|
@@ -3,14 +3,14 @@ module ActiveRecord
|
|
3
3
|
# Abstract representation of an index definition on a table. Instances of
|
4
4
|
# this type are typically created and returned by methods in database
|
5
5
|
# adapters. e.g. ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter#indexes
|
6
|
-
class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths, :orders, :where, :type, :using) #:nodoc:
|
6
|
+
class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths, :orders, :where, :type, :using, :comment) #:nodoc:
|
7
7
|
end
|
8
8
|
|
9
9
|
# Abstract representation of a column definition. Instances of this type
|
10
10
|
# are typically created by methods in TableDefinition, and added to the
|
11
11
|
# +columns+ attribute of said TableDefinition object, in order to be used
|
12
12
|
# for generating a number of table creation or table changing SQL statements.
|
13
|
-
class ColumnDefinition < Struct.new(:name, :type, :limit, :precision, :scale, :default, :null, :first, :after, :auto_increment, :primary_key, :collation, :sql_type) #:nodoc:
|
13
|
+
class ColumnDefinition < Struct.new(:name, :type, :limit, :precision, :scale, :default, :null, :first, :after, :auto_increment, :primary_key, :collation, :sql_type, :comment) #:nodoc:
|
14
14
|
|
15
15
|
def primary_key?
|
16
16
|
primary_key || type.to_sym == :primary_key
|
@@ -207,9 +207,9 @@ module ActiveRecord
|
|
207
207
|
include ColumnMethods
|
208
208
|
|
209
209
|
attr_accessor :indexes
|
210
|
-
attr_reader :name, :temporary, :options, :as, :foreign_keys
|
210
|
+
attr_reader :name, :temporary, :options, :as, :foreign_keys, :comment
|
211
211
|
|
212
|
-
def initialize(name, temporary, options, as = nil)
|
212
|
+
def initialize(name, temporary = false, options = nil, as = nil, comment: nil)
|
213
213
|
@columns_hash = {}
|
214
214
|
@indexes = {}
|
215
215
|
@foreign_keys = []
|
@@ -218,6 +218,7 @@ module ActiveRecord
|
|
218
218
|
@options = options
|
219
219
|
@as = as
|
220
220
|
@name = name
|
221
|
+
@comment = comment
|
221
222
|
end
|
222
223
|
|
223
224
|
def primary_keys(name = nil) # :nodoc:
|
@@ -330,6 +331,9 @@ module ActiveRecord
|
|
330
331
|
end
|
331
332
|
|
332
333
|
def foreign_key(table_name, options = {}) # :nodoc:
|
334
|
+
table_name_prefix = ActiveRecord::Base.table_name_prefix
|
335
|
+
table_name_suffix = ActiveRecord::Base.table_name_suffix
|
336
|
+
table_name = "#{table_name_prefix}#{table_name}#{table_name_suffix}"
|
333
337
|
foreign_keys.push([table_name, options])
|
334
338
|
end
|
335
339
|
|
@@ -373,6 +377,7 @@ module ActiveRecord
|
|
373
377
|
column.auto_increment = options[:auto_increment]
|
374
378
|
column.primary_key = type == :primary_key || options[:primary_key]
|
375
379
|
column.collation = options[:collation]
|
380
|
+
column.comment = options[:comment]
|
376
381
|
column
|
377
382
|
end
|
378
383
|
|
@@ -7,15 +7,16 @@ module ActiveRecord
|
|
7
7
|
# Adapter level by over-writing this code inside the database specific adapters
|
8
8
|
module ColumnDumper
|
9
9
|
def column_spec(column)
|
10
|
-
spec = prepare_column_options(column)
|
11
|
-
|
10
|
+
spec = Hash[prepare_column_options(column).map { |k, v| [k, "#{k}: #{v}"] }]
|
11
|
+
spec[:name] = column.name.inspect
|
12
|
+
spec[:type] = schema_type(column).to_s
|
12
13
|
spec
|
13
14
|
end
|
14
15
|
|
15
16
|
def column_spec_for_primary_key(column)
|
16
|
-
return if column
|
17
|
+
return {} if default_primary_key?(column)
|
17
18
|
spec = { id: schema_type(column).inspect }
|
18
|
-
spec.merge!(prepare_column_options(column).
|
19
|
+
spec.merge!(prepare_column_options(column).except!(:null))
|
19
20
|
end
|
20
21
|
|
21
22
|
# This can be overridden on an Adapter level basis to support other
|
@@ -23,9 +24,6 @@ module ActiveRecord
|
|
23
24
|
# PostgreSQL::ColumnDumper)
|
24
25
|
def prepare_column_options(column)
|
25
26
|
spec = {}
|
26
|
-
spec[:name] = column.name.inspect
|
27
|
-
spec[:type] = schema_type(column).to_s
|
28
|
-
spec[:null] = 'false' unless column.null
|
29
27
|
|
30
28
|
if limit = schema_limit(column)
|
31
29
|
spec[:limit] = limit
|
@@ -42,26 +40,38 @@ module ActiveRecord
|
|
42
40
|
default = schema_default(column) if column.has_default?
|
43
41
|
spec[:default] = default unless default.nil?
|
44
42
|
|
43
|
+
spec[:null] = 'false' unless column.null
|
44
|
+
|
45
45
|
if collation = schema_collation(column)
|
46
46
|
spec[:collation] = collation
|
47
47
|
end
|
48
48
|
|
49
|
+
spec[:comment] = column.comment.inspect if column.comment.present?
|
50
|
+
|
49
51
|
spec
|
50
52
|
end
|
51
53
|
|
52
54
|
# Lists the valid migration options
|
53
55
|
def migration_keys
|
54
|
-
[:name, :limit, :precision, :scale, :default, :null, :collation]
|
56
|
+
[:name, :limit, :precision, :scale, :default, :null, :collation, :comment]
|
55
57
|
end
|
56
58
|
|
57
59
|
private
|
58
60
|
|
61
|
+
def default_primary_key?(column)
|
62
|
+
schema_type(column) == :integer
|
63
|
+
end
|
64
|
+
|
59
65
|
def schema_type(column)
|
60
|
-
column.
|
66
|
+
if column.bigint?
|
67
|
+
:bigint
|
68
|
+
else
|
69
|
+
column.type
|
70
|
+
end
|
61
71
|
end
|
62
72
|
|
63
73
|
def schema_limit(column)
|
64
|
-
limit = column.limit
|
74
|
+
limit = column.limit unless column.bigint?
|
65
75
|
limit.inspect if limit && limit != native_database_types[column.type][:limit]
|
66
76
|
end
|
67
77
|
|
@@ -18,6 +18,11 @@ module ActiveRecord
|
|
18
18
|
nil
|
19
19
|
end
|
20
20
|
|
21
|
+
# Returns the table comment that's stored in database metadata.
|
22
|
+
def table_comment(table_name)
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
|
21
26
|
# Truncates a table alias according to the limits of the current adapter.
|
22
27
|
def table_alias_for(table_name)
|
23
28
|
table_name[0...table_alias_length].tr('.', '_')
|
@@ -254,8 +259,8 @@ module ActiveRecord
|
|
254
259
|
# SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
|
255
260
|
#
|
256
261
|
# See also TableDefinition#column for details on how to create columns.
|
257
|
-
def create_table(table_name,
|
258
|
-
td = create_table_definition table_name, options[:temporary], options[:options], options[:as]
|
262
|
+
def create_table(table_name, comment: nil, **options)
|
263
|
+
td = create_table_definition table_name, options[:temporary], options[:options], options[:as], comment: comment
|
259
264
|
|
260
265
|
if options[:id] != false && !options[:as]
|
261
266
|
pk = options.fetch(:primary_key) do
|
@@ -283,6 +288,14 @@ module ActiveRecord
|
|
283
288
|
end
|
284
289
|
end
|
285
290
|
|
291
|
+
if supports_comments? && !supports_comments_in_create?
|
292
|
+
change_table_comment(table_name, comment) if comment
|
293
|
+
|
294
|
+
td.columns.each do |column|
|
295
|
+
change_column_comment(table_name, column.name, column.comment) if column.comment
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
286
299
|
result
|
287
300
|
end
|
288
301
|
|
@@ -329,12 +342,13 @@ module ActiveRecord
|
|
329
342
|
|
330
343
|
column_options = options.delete(:column_options) || {}
|
331
344
|
column_options.reverse_merge!(null: false)
|
345
|
+
type = column_options.delete(:type) || :integer
|
332
346
|
|
333
347
|
t1_column, t2_column = [table_1, table_2].map{ |t| t.to_s.singularize.foreign_key }
|
334
348
|
|
335
349
|
create_table(join_table_name, options.merge!(id: false)) do |td|
|
336
|
-
td.
|
337
|
-
td.
|
350
|
+
td.send type, t1_column, column_options
|
351
|
+
td.send type, t2_column, column_options
|
338
352
|
yield td if block_given?
|
339
353
|
end
|
340
354
|
end
|
@@ -776,7 +790,8 @@ module ActiveRecord
|
|
776
790
|
# [<tt>:type</tt>]
|
777
791
|
# The reference column type. Defaults to +:integer+.
|
778
792
|
# [<tt>:index</tt>]
|
779
|
-
# Add an appropriate index. Defaults to false.
|
793
|
+
# Add an appropriate index. Defaults to false.
|
794
|
+
# See #add_index for usage of this option.
|
780
795
|
# [<tt>:foreign_key</tt>]
|
781
796
|
# Add an appropriate foreign key constraint. Defaults to false.
|
782
797
|
# [<tt>:polymorphic</tt>]
|
@@ -796,6 +811,14 @@ module ActiveRecord
|
|
796
811
|
#
|
797
812
|
# add_reference(:products, :supplier, polymorphic: true, index: true)
|
798
813
|
#
|
814
|
+
# ====== Create a supplier_id column with a unique index
|
815
|
+
#
|
816
|
+
# add_reference(:products, :supplier, index: { unique: true })
|
817
|
+
#
|
818
|
+
# ====== Create a supplier_id column with a named index
|
819
|
+
#
|
820
|
+
# add_reference(:products, :supplier, index: { name: "my_supplier_index" })
|
821
|
+
#
|
799
822
|
# ====== Create a supplier_id column and appropriate foreign key
|
800
823
|
#
|
801
824
|
# add_reference(:products, :supplier, foreign_key: true)
|
@@ -854,7 +877,7 @@ module ActiveRecord
|
|
854
877
|
#
|
855
878
|
# generates:
|
856
879
|
#
|
857
|
-
# ALTER TABLE "articles" ADD CONSTRAINT
|
880
|
+
# ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id")
|
858
881
|
#
|
859
882
|
# ====== Creating a foreign key on a specific column
|
860
883
|
#
|
@@ -870,7 +893,7 @@ module ActiveRecord
|
|
870
893
|
#
|
871
894
|
# generates:
|
872
895
|
#
|
873
|
-
# ALTER TABLE "articles" ADD CONSTRAINT
|
896
|
+
# ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id") ON DELETE CASCADE
|
874
897
|
#
|
875
898
|
# The +options+ hash can include the following keys:
|
876
899
|
# [<tt>:column</tt>]
|
@@ -962,11 +985,23 @@ module ActiveRecord
|
|
962
985
|
end
|
963
986
|
|
964
987
|
def dump_schema_information #:nodoc:
|
988
|
+
versions = ActiveRecord::SchemaMigration.order('version').pluck(:version)
|
989
|
+
insert_versions_sql(versions)
|
990
|
+
end
|
991
|
+
|
992
|
+
def insert_versions_sql(versions) # :nodoc:
|
965
993
|
sm_table = ActiveRecord::Migrator.schema_migrations_table_name
|
966
994
|
|
967
|
-
|
968
|
-
|
969
|
-
|
995
|
+
if supports_multi_insert?
|
996
|
+
sql = "INSERT INTO #{sm_table} (version) VALUES "
|
997
|
+
sql << versions.map {|v| "('#{v}')" }.join(', ')
|
998
|
+
sql << ";\n\n"
|
999
|
+
sql
|
1000
|
+
else
|
1001
|
+
versions.map { |version|
|
1002
|
+
"INSERT INTO #{sm_table} (version) VALUES ('#{version}');"
|
1003
|
+
}.join "\n\n"
|
1004
|
+
end
|
970
1005
|
end
|
971
1006
|
|
972
1007
|
# Should not be called normally, but this operation is non-destructive.
|
@@ -1003,7 +1038,7 @@ module ActiveRecord
|
|
1003
1038
|
if (duplicate = inserting.detect {|v| inserting.count(v) > 1})
|
1004
1039
|
raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict."
|
1005
1040
|
end
|
1006
|
-
execute
|
1041
|
+
execute insert_versions_sql(inserting)
|
1007
1042
|
end
|
1008
1043
|
end
|
1009
1044
|
|
@@ -1051,9 +1086,9 @@ module ActiveRecord
|
|
1051
1086
|
end
|
1052
1087
|
|
1053
1088
|
# Adds timestamps (+created_at+ and +updated_at+) columns to +table_name+.
|
1054
|
-
# Additional options (like
|
1089
|
+
# Additional options (like +:null+) are forwarded to #add_column.
|
1055
1090
|
#
|
1056
|
-
# add_timestamps(:suppliers, null:
|
1091
|
+
# add_timestamps(:suppliers, null: true)
|
1057
1092
|
#
|
1058
1093
|
def add_timestamps(table_name, options = {})
|
1059
1094
|
options[:null] = false if options[:null].nil?
|
@@ -1075,15 +1110,19 @@ module ActiveRecord
|
|
1075
1110
|
Table.new(table_name, base)
|
1076
1111
|
end
|
1077
1112
|
|
1078
|
-
def add_index_options(table_name, column_name,
|
1079
|
-
|
1113
|
+
def add_index_options(table_name, column_name, comment: nil, **options) # :nodoc:
|
1114
|
+
if column_name.is_a?(String) && /\W/ === column_name
|
1115
|
+
column_names = column_name
|
1116
|
+
else
|
1117
|
+
column_names = Array(column_name)
|
1118
|
+
end
|
1080
1119
|
|
1081
1120
|
options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type)
|
1082
1121
|
|
1083
1122
|
index_type = options[:type].to_s if options.key?(:type)
|
1084
1123
|
index_type ||= options[:unique] ? "UNIQUE" : ""
|
1085
1124
|
index_name = options[:name].to_s if options.key?(:name)
|
1086
|
-
index_name ||= index_name(table_name,
|
1125
|
+
index_name ||= index_name(table_name, index_name_options(column_names))
|
1087
1126
|
max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
|
1088
1127
|
|
1089
1128
|
if options.key?(:algorithm)
|
@@ -1106,13 +1145,23 @@ module ActiveRecord
|
|
1106
1145
|
end
|
1107
1146
|
index_columns = quoted_columns_for_index(column_names, options).join(", ")
|
1108
1147
|
|
1109
|
-
[index_name, index_type, index_columns, index_options, algorithm, using]
|
1148
|
+
[index_name, index_type, index_columns, index_options, algorithm, using, comment]
|
1110
1149
|
end
|
1111
1150
|
|
1112
1151
|
def options_include_default?(options)
|
1113
1152
|
options.include?(:default) && !(options[:null] == false && options[:default].nil?)
|
1114
1153
|
end
|
1115
1154
|
|
1155
|
+
# Changes the comment for a table or removes it if +nil+.
|
1156
|
+
def change_table_comment(table_name, comment)
|
1157
|
+
raise NotImplementedError, "#{self.class} does not support changing table comments"
|
1158
|
+
end
|
1159
|
+
|
1160
|
+
# Changes the comment for a column or removes it if +nil+.
|
1161
|
+
def change_column_comment(table_name, column_name, comment) #:nodoc:
|
1162
|
+
raise NotImplementedError, "#{self.class} does not support changing column comments"
|
1163
|
+
end
|
1164
|
+
|
1116
1165
|
protected
|
1117
1166
|
def add_index_sort_order(option_strings, column_names, options = {})
|
1118
1167
|
if options.is_a?(Hash) && order = options[:order]
|
@@ -1129,6 +1178,8 @@ module ActiveRecord
|
|
1129
1178
|
|
1130
1179
|
# Overridden by the MySQL adapter for supporting index lengths
|
1131
1180
|
def quoted_columns_for_index(column_names, options = {})
|
1181
|
+
return [column_names] if column_names.is_a?(String)
|
1182
|
+
|
1132
1183
|
option_strings = Hash[column_names.map {|name| [name, '']}]
|
1133
1184
|
|
1134
1185
|
# add index sort order if supported
|
@@ -1140,6 +1191,8 @@ module ActiveRecord
|
|
1140
1191
|
end
|
1141
1192
|
|
1142
1193
|
def index_name_for_remove(table_name, options = {})
|
1194
|
+
return options[:name] if can_remove_index_by_name?(options)
|
1195
|
+
|
1143
1196
|
# if the adapter doesn't support the indexes call the best we can do
|
1144
1197
|
# is return the default index name for the options provided
|
1145
1198
|
return index_name(table_name, options) unless respond_to?(:indexes)
|
@@ -1147,7 +1200,7 @@ module ActiveRecord
|
|
1147
1200
|
checks = []
|
1148
1201
|
|
1149
1202
|
if options.is_a?(Hash)
|
1150
|
-
checks << lambda { |i| i.name == options[:name].to_s } if options.
|
1203
|
+
checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
|
1151
1204
|
column_names = Array(options[:column]).map(&:to_s)
|
1152
1205
|
else
|
1153
1206
|
column_names = Array(options).map(&:to_s)
|
@@ -1194,14 +1247,22 @@ module ActiveRecord
|
|
1194
1247
|
end
|
1195
1248
|
|
1196
1249
|
private
|
1197
|
-
def create_table_definition(
|
1198
|
-
TableDefinition.new(
|
1250
|
+
def create_table_definition(*args)
|
1251
|
+
TableDefinition.new(*args)
|
1199
1252
|
end
|
1200
1253
|
|
1201
1254
|
def create_alter_table(name)
|
1202
1255
|
AlterTable.new create_table_definition(name)
|
1203
1256
|
end
|
1204
1257
|
|
1258
|
+
def index_name_options(column_names) # :nodoc:
|
1259
|
+
if column_names.is_a?(String)
|
1260
|
+
column_names = column_names.scan(/\w+/).join('_')
|
1261
|
+
end
|
1262
|
+
|
1263
|
+
{ column: column_names }
|
1264
|
+
end
|
1265
|
+
|
1205
1266
|
def foreign_key_name(table_name, options) # :nodoc:
|
1206
1267
|
identifier = "#{table_name}_#{options.fetch(:column)}_fk"
|
1207
1268
|
hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
|
@@ -1223,6 +1284,10 @@ module ActiveRecord
|
|
1223
1284
|
default_or_changes
|
1224
1285
|
end
|
1225
1286
|
end
|
1287
|
+
|
1288
|
+
def can_remove_index_by_name?(options)
|
1289
|
+
options.is_a?(Hash) && options.key?(:name) && options.except(:name, :algorithm).empty?
|
1290
|
+
end
|
1226
1291
|
end
|
1227
1292
|
end
|
1228
1293
|
end
|