activerecord 5.1.0.rc1 → 5.1.0.rc2
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 +24 -0
- data/lib/active_record/associations.rb +1 -1
- data/lib/active_record/associations/collection_association.rb +32 -35
- data/lib/active_record/associations/has_many_association.rb +1 -6
- data/lib/active_record/associations/has_many_through_association.rb +1 -5
- data/lib/active_record/autosave_association.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +7 -8
- data/lib/active_record/connection_adapters/abstract/transaction.rb +49 -39
- data/lib/active_record/connection_adapters/abstract_adapter.rb +2 -4
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +0 -8
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +4 -3
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -4
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +11 -9
- data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +35 -21
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/log_subscriber.rb +7 -11
- data/lib/active_record/persistence.rb +20 -6
- data/lib/active_record/railties/databases.rake +5 -2
- data/lib/active_record/relation.rb +3 -1
- data/lib/active_record/relation/finder_methods.rb +15 -12
- data/lib/active_record/relation/where_clause.rb +2 -2
- data/lib/active_record/tasks/database_tasks.rb +3 -1
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/transactions.rb +1 -1
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 68113d7968e655698251c14372de192d2e8e38da
|
4
|
+
data.tar.gz: 80fdb87c4ba3d63700799dd66b24d078c9ab9748
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 84e1eb324d24437342da23fc2d7c255ce23a2fab0888e5d347f98eb5bdfa9d8ef1e066800d7383f8cea1c76b006c965e1aa61599937502580da69821eea21844
|
7
|
+
data.tar.gz: de2d5f8de26cf8593c3ad320f9793d1df65260501b097dbbe8fadc122b97588a42c85cdc8ed7cec21e33106d076390dff00fa2cd5073eaf390eb3bd561f95e19
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,27 @@
|
|
1
|
+
## Rails 5.1.0.rc2 (April 20, 2017) ##
|
2
|
+
|
3
|
+
* Quote database name in db:create grant statement (when database_user does not have access to create the database).
|
4
|
+
|
5
|
+
*Rune Philosof*
|
6
|
+
|
7
|
+
* When multiple threads are sharing a database connection inside a test using
|
8
|
+
transactional fixtures, a nested transaction will temporarily lock the
|
9
|
+
connection to the current thread, forcing others to wait.
|
10
|
+
|
11
|
+
Fixes #28197.
|
12
|
+
|
13
|
+
*Matthew Draper*
|
14
|
+
|
15
|
+
* Load only needed records on `ActiveRecord::Relation#inspect`.
|
16
|
+
|
17
|
+
Instead of loading all records and returning only a subset of those, just
|
18
|
+
load the records as needed.
|
19
|
+
|
20
|
+
Fixes #25537.
|
21
|
+
|
22
|
+
*Hendy Tanata*
|
23
|
+
|
24
|
+
|
1
25
|
## Rails 5.1.0.rc1 (March 20, 2017) ##
|
2
26
|
|
3
27
|
* Remove comments from structure.sql when using postgresql adapter to avoid
|
@@ -1649,7 +1649,7 @@ module ActiveRecord
|
|
1649
1649
|
# you don't want to have association presence validated, use <tt>optional: true</tt>.
|
1650
1650
|
# [:default]
|
1651
1651
|
# Provide a callable (i.e. proc or lambda) to specify that the association should
|
1652
|
-
# be initialized with a particular record before validation.
|
1652
|
+
# be initialized with a particular record before validation.
|
1653
1653
|
#
|
1654
1654
|
# Option examples:
|
1655
1655
|
# belongs_to :firm, foreign_key: "client_of"
|
@@ -280,35 +280,6 @@ module ActiveRecord
|
|
280
280
|
replace_on_target(record, index, skip_callbacks, &block)
|
281
281
|
end
|
282
282
|
|
283
|
-
def replace_on_target(record, index, skip_callbacks)
|
284
|
-
callback(:before_add, record) unless skip_callbacks
|
285
|
-
|
286
|
-
begin
|
287
|
-
if index
|
288
|
-
record_was = target[index]
|
289
|
-
target[index] = record
|
290
|
-
else
|
291
|
-
target << record
|
292
|
-
end
|
293
|
-
|
294
|
-
set_inverse_instance(record)
|
295
|
-
|
296
|
-
yield(record) if block_given?
|
297
|
-
rescue
|
298
|
-
if index
|
299
|
-
target[index] = record_was
|
300
|
-
else
|
301
|
-
target.delete(record)
|
302
|
-
end
|
303
|
-
|
304
|
-
raise
|
305
|
-
end
|
306
|
-
|
307
|
-
callback(:after_add, record) unless skip_callbacks
|
308
|
-
|
309
|
-
record
|
310
|
-
end
|
311
|
-
|
312
283
|
def scope
|
313
284
|
scope = super
|
314
285
|
scope.none! if null_scope?
|
@@ -385,15 +356,19 @@ module ActiveRecord
|
|
385
356
|
transaction do
|
386
357
|
add_to_target(build_record(attributes)) do |record|
|
387
358
|
yield(record) if block_given?
|
388
|
-
insert_record(record, true, raise)
|
359
|
+
insert_record(record, true, raise) { @_was_loaded = loaded? }
|
389
360
|
end
|
390
361
|
end
|
391
362
|
end
|
392
363
|
end
|
393
364
|
|
394
365
|
# Do the relevant stuff to insert the given record into the association collection.
|
395
|
-
def insert_record(record, validate = true, raise = false)
|
396
|
-
raise
|
366
|
+
def insert_record(record, validate = true, raise = false, &block)
|
367
|
+
if raise
|
368
|
+
record.save!(validate: validate, &block)
|
369
|
+
else
|
370
|
+
record.save(validate: validate, &block)
|
371
|
+
end
|
397
372
|
end
|
398
373
|
|
399
374
|
def create_scope
|
@@ -448,19 +423,41 @@ module ActiveRecord
|
|
448
423
|
end
|
449
424
|
end
|
450
425
|
|
451
|
-
def concat_records(records,
|
426
|
+
def concat_records(records, raise = false)
|
452
427
|
result = true
|
453
428
|
|
454
429
|
records.each do |record|
|
455
430
|
raise_on_type_mismatch!(record)
|
456
|
-
add_to_target(record) do
|
457
|
-
result &&= insert_record(
|
431
|
+
add_to_target(record) do
|
432
|
+
result &&= insert_record(record, true, raise) { @_was_loaded = loaded? } unless owner.new_record?
|
458
433
|
end
|
459
434
|
end
|
460
435
|
|
461
436
|
result && records
|
462
437
|
end
|
463
438
|
|
439
|
+
def replace_on_target(record, index, skip_callbacks)
|
440
|
+
callback(:before_add, record) unless skip_callbacks
|
441
|
+
|
442
|
+
set_inverse_instance(record)
|
443
|
+
|
444
|
+
@_was_loaded = true
|
445
|
+
|
446
|
+
yield(record) if block_given?
|
447
|
+
|
448
|
+
if index
|
449
|
+
target[index] = record
|
450
|
+
elsif @_was_loaded || !loaded?
|
451
|
+
target << record
|
452
|
+
end
|
453
|
+
|
454
|
+
callback(:after_add, record) unless skip_callbacks
|
455
|
+
|
456
|
+
record
|
457
|
+
ensure
|
458
|
+
@_was_loaded = nil
|
459
|
+
end
|
460
|
+
|
464
461
|
def callback(method, record)
|
465
462
|
callbacks_for(method).each do |callback|
|
466
463
|
callback.call(method, owner, record)
|
@@ -31,12 +31,7 @@ module ActiveRecord
|
|
31
31
|
|
32
32
|
def insert_record(record, validate = true, raise = false)
|
33
33
|
set_owner_attributes(record)
|
34
|
-
|
35
|
-
if raise
|
36
|
-
record.save!(validate: validate)
|
37
|
-
else
|
38
|
-
record.save(validate: validate)
|
39
|
-
end
|
34
|
+
super
|
40
35
|
end
|
41
36
|
|
42
37
|
def empty?
|
@@ -39,11 +39,7 @@ module ActiveRecord
|
|
39
39
|
ensure_not_nested
|
40
40
|
|
41
41
|
if record.new_record? || record.has_changes_to_save?
|
42
|
-
|
43
|
-
record.save!(validate: validate)
|
44
|
-
else
|
45
|
-
return unless record.save(validate: validate)
|
46
|
-
end
|
42
|
+
return unless super
|
47
43
|
end
|
48
44
|
|
49
45
|
save_through_record(record)
|
@@ -181,6 +181,7 @@ module ActiveRecord
|
|
181
181
|
|
182
182
|
if reflection.collection?
|
183
183
|
before_save :before_save_collection_association
|
184
|
+
after_save :after_save_collection_association
|
184
185
|
|
185
186
|
define_non_cyclic_method(save_method) { save_collection_association(reflection) }
|
186
187
|
# Doesn't use after_save as that would save associations added in after_create/after_update twice
|
@@ -371,6 +372,10 @@ module ActiveRecord
|
|
371
372
|
true
|
372
373
|
end
|
373
374
|
|
375
|
+
def after_save_collection_association
|
376
|
+
@new_record_before_save = false
|
377
|
+
end
|
378
|
+
|
374
379
|
# Saves any new associated records, or all loaded autosave associations if
|
375
380
|
# <tt>:autosave</tt> is enabled on the association.
|
376
381
|
#
|
@@ -152,16 +152,15 @@ module ActiveRecord
|
|
152
152
|
"'#{quote_string(value.to_s)}'"
|
153
153
|
end
|
154
154
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
else
|
161
|
-
binds.map { |attr| type_cast(attr.value_for_database) }
|
162
|
-
end
|
155
|
+
def type_casted_binds(binds) # :nodoc:
|
156
|
+
if binds.first.is_a?(Array)
|
157
|
+
binds.map { |column, value| type_cast(value, column) }
|
158
|
+
else
|
159
|
+
binds.map { |attr| type_cast(attr.value_for_database) }
|
163
160
|
end
|
161
|
+
end
|
164
162
|
|
163
|
+
private
|
165
164
|
def id_value_for_database(value)
|
166
165
|
if primary_key = value.class.primary_key
|
167
166
|
value.instance_variable_get(:@attributes)[primary_key].value_for_database
|
@@ -149,57 +149,67 @@ module ActiveRecord
|
|
149
149
|
end
|
150
150
|
|
151
151
|
def begin_transaction(options = {})
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
152
|
+
@connection.lock.synchronize do
|
153
|
+
run_commit_callbacks = !current_transaction.joinable?
|
154
|
+
transaction =
|
155
|
+
if @stack.empty?
|
156
|
+
RealTransaction.new(@connection, options, run_commit_callbacks: run_commit_callbacks)
|
157
|
+
else
|
158
|
+
SavepointTransaction.new(@connection, "active_record_#{@stack.size}", options,
|
159
|
+
run_commit_callbacks: run_commit_callbacks)
|
160
|
+
end
|
160
161
|
|
161
|
-
|
162
|
-
|
162
|
+
@stack.push(transaction)
|
163
|
+
transaction
|
164
|
+
end
|
163
165
|
end
|
164
166
|
|
165
167
|
def commit_transaction
|
166
|
-
|
168
|
+
@connection.lock.synchronize do
|
169
|
+
transaction = @stack.last
|
167
170
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
171
|
+
begin
|
172
|
+
transaction.before_commit_records
|
173
|
+
ensure
|
174
|
+
@stack.pop
|
175
|
+
end
|
173
176
|
|
174
|
-
|
175
|
-
|
177
|
+
transaction.commit
|
178
|
+
transaction.commit_records
|
179
|
+
end
|
176
180
|
end
|
177
181
|
|
178
182
|
def rollback_transaction(transaction = nil)
|
179
|
-
|
180
|
-
|
181
|
-
|
183
|
+
@connection.lock.synchronize do
|
184
|
+
transaction ||= @stack.pop
|
185
|
+
transaction.rollback
|
186
|
+
transaction.rollback_records
|
187
|
+
end
|
182
188
|
end
|
183
189
|
|
184
190
|
def within_new_transaction(options = {})
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
191
|
+
@connection.lock.synchronize do
|
192
|
+
begin
|
193
|
+
transaction = begin_transaction options
|
194
|
+
yield
|
195
|
+
rescue Exception => error
|
196
|
+
if transaction
|
197
|
+
rollback_transaction
|
198
|
+
after_failure_actions(transaction, error)
|
199
|
+
end
|
200
|
+
raise
|
201
|
+
ensure
|
202
|
+
unless error
|
203
|
+
if Thread.current.status == "aborting"
|
204
|
+
rollback_transaction if transaction
|
205
|
+
else
|
206
|
+
begin
|
207
|
+
commit_transaction
|
208
|
+
rescue Exception
|
209
|
+
rollback_transaction(transaction) unless transaction.state.completed?
|
210
|
+
raise
|
211
|
+
end
|
212
|
+
end
|
203
213
|
end
|
204
214
|
end
|
205
215
|
end
|
@@ -74,7 +74,7 @@ module ActiveRecord
|
|
74
74
|
SIMPLE_INT = /\A\d+\z/
|
75
75
|
|
76
76
|
attr_accessor :visitor, :pool
|
77
|
-
attr_reader :schema_cache, :owner, :logger
|
77
|
+
attr_reader :schema_cache, :owner, :logger, :prepared_statements, :lock
|
78
78
|
alias :in_use? :owner
|
79
79
|
|
80
80
|
def self.type_cast_config_to_integer(config)
|
@@ -93,8 +93,6 @@ module ActiveRecord
|
|
93
93
|
end
|
94
94
|
end
|
95
95
|
|
96
|
-
attr_reader :prepared_statements
|
97
|
-
|
98
96
|
def initialize(connection, logger = nil, config = {}) # :nodoc:
|
99
97
|
super()
|
100
98
|
|
@@ -447,7 +445,7 @@ module ActiveRecord
|
|
447
445
|
|
448
446
|
# Provides access to the underlying database driver for this adapter. For
|
449
447
|
# example, this method returns a Mysql2::Client object in case of Mysql2Adapter,
|
450
|
-
# and a
|
448
|
+
# and a PG::Connection object in case of PostgreSQLAdapter.
|
451
449
|
#
|
452
450
|
# This is useful for when you need to call a proprietary method such as
|
453
451
|
# PostgreSQL's lo_* methods.
|
@@ -75,14 +75,6 @@ module ActiveRecord
|
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
78
|
-
CHARSETS_OF_4BYTES_MAXLEN = ["utf8mb4", "utf16", "utf16le", "utf32"]
|
79
|
-
|
80
|
-
def internal_string_options_for_primary_key # :nodoc:
|
81
|
-
super.tap { |options|
|
82
|
-
options[:collation] = collation.sub(/\A[^_]+/, "utf8") if CHARSETS_OF_4BYTES_MAXLEN.include?(charset)
|
83
|
-
}
|
84
|
-
end
|
85
|
-
|
86
78
|
def version #:nodoc:
|
87
79
|
@version ||= Version.new(full_version.match(/^\d+\.\d+\.\d+/)[0])
|
88
80
|
end
|
@@ -55,13 +55,14 @@ module ActiveRecord
|
|
55
55
|
def extract_expression_for_virtual_column(column)
|
56
56
|
if mariadb?
|
57
57
|
create_table_info = create_table_info(column.table_name)
|
58
|
-
if %r/#{quote_column_name(column.name)} #{Regexp.quote(column.sql_type)} AS \((?<expression>.+?)\) #{column.extra}/
|
58
|
+
if %r/#{quote_column_name(column.name)} #{Regexp.quote(column.sql_type)}(?: COLLATE \w+)? AS \((?<expression>.+?)\) #{column.extra}/ =~ create_table_info
|
59
59
|
$~[:expression].inspect
|
60
60
|
end
|
61
61
|
else
|
62
|
+
scope = quoted_scope(column.table_name)
|
62
63
|
sql = "SELECT generation_expression FROM information_schema.columns" \
|
63
|
-
" WHERE table_schema = #{
|
64
|
-
" AND table_name = #{
|
64
|
+
" WHERE table_schema = #{scope[:schema]}" \
|
65
|
+
" AND table_name = #{scope[:name]}" \
|
65
66
|
" AND column_name = #{quote(column.name)}"
|
66
67
|
select_value(sql, "SCHEMA").inspect
|
67
68
|
end
|
@@ -2,7 +2,17 @@ module ActiveRecord
|
|
2
2
|
module ConnectionAdapters
|
3
3
|
module MySQL
|
4
4
|
module SchemaStatements # :nodoc:
|
5
|
+
def internal_string_options_for_primary_key
|
6
|
+
super.tap do |options|
|
7
|
+
if CHARSETS_OF_4BYTES_MAXLEN.include?(charset) && (mariadb? || version < "8.0.0")
|
8
|
+
options[:collation] = collation.sub(/\A[^_]+/, "utf8")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
5
13
|
private
|
14
|
+
CHARSETS_OF_4BYTES_MAXLEN = ["utf8mb4", "utf16", "utf16le", "utf32"]
|
15
|
+
|
6
16
|
def data_source_sql(name = nil, type: nil)
|
7
17
|
scope = quoted_scope(name, type: type)
|
8
18
|
|
@@ -33,7 +33,7 @@ module ActiveRecord
|
|
33
33
|
|
34
34
|
# Quotes schema names for use in SQL queries.
|
35
35
|
def quote_schema_name(name)
|
36
|
-
|
36
|
+
PG::Connection.quote_ident(name)
|
37
37
|
end
|
38
38
|
|
39
39
|
def quote_table_name_for_assignment(table, attr)
|
@@ -42,7 +42,7 @@ module ActiveRecord
|
|
42
42
|
|
43
43
|
# Quotes column names for use in SQL queries.
|
44
44
|
def quote_column_name(name) # :nodoc:
|
45
|
-
@quoted_column_names[name] ||=
|
45
|
+
@quoted_column_names[name] ||= PG::Connection.quote_ident(super).freeze
|
46
46
|
end
|
47
47
|
|
48
48
|
# Quote date/time values for use in SQL input.
|
@@ -105,7 +105,7 @@ module ActiveRecord
|
|
105
105
|
case value
|
106
106
|
when Type::Binary::Data
|
107
107
|
# Return a bind param hash with format as binary.
|
108
|
-
# See
|
108
|
+
# See https://deveiate.org/code/pg/PG/Connection.html#method-i-exec_prepared-doc
|
109
109
|
# for more information
|
110
110
|
{ value: value.to_s, format: 1 }
|
111
111
|
when OID::Xml::Data, OID::Bit::Data
|
@@ -6,8 +6,50 @@ module ActiveRecord
|
|
6
6
|
true
|
7
7
|
end
|
8
8
|
|
9
|
-
def disable_referential_integrity # :nodoc:
|
9
|
+
def disable_referential_integrity(&block) # :nodoc:
|
10
10
|
if supports_disable_referential_integrity?
|
11
|
+
if supports_alter_constraint?
|
12
|
+
disable_referential_integrity_with_alter_constraint(&block)
|
13
|
+
else
|
14
|
+
disable_referential_integrity_with_disable_trigger(&block)
|
15
|
+
end
|
16
|
+
else
|
17
|
+
yield
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def disable_referential_integrity_with_alter_constraint
|
24
|
+
tables_constraints = execute(<<-SQL).values
|
25
|
+
SELECT table_name, constraint_name
|
26
|
+
FROM information_schema.table_constraints
|
27
|
+
WHERE constraint_type = 'FOREIGN KEY'
|
28
|
+
AND is_deferrable = 'NO'
|
29
|
+
SQL
|
30
|
+
|
31
|
+
execute(
|
32
|
+
tables_constraints.collect { |table, constraint|
|
33
|
+
"ALTER TABLE #{quote_table_name(table)} ALTER CONSTRAINT #{constraint} DEFERRABLE"
|
34
|
+
}.join(";")
|
35
|
+
)
|
36
|
+
|
37
|
+
begin
|
38
|
+
transaction do
|
39
|
+
execute("SET CONSTRAINTS ALL DEFERRED")
|
40
|
+
|
41
|
+
yield
|
42
|
+
end
|
43
|
+
ensure
|
44
|
+
execute(
|
45
|
+
tables_constraints.collect { |table, constraint|
|
46
|
+
"ALTER TABLE #{quote_table_name(table)} ALTER CONSTRAINT #{constraint} NOT DEFERRABLE"
|
47
|
+
}.join(";")
|
48
|
+
)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def disable_referential_integrity_with_disable_trigger
|
11
53
|
original_exception = nil
|
12
54
|
|
13
55
|
begin
|
@@ -39,10 +81,7 @@ Rails needs superuser privileges to disable referential integrity.
|
|
39
81
|
end
|
40
82
|
rescue ActiveRecord::ActiveRecordError
|
41
83
|
end
|
42
|
-
else
|
43
|
-
yield
|
44
84
|
end
|
45
|
-
end
|
46
85
|
end
|
47
86
|
end
|
48
87
|
end
|
@@ -362,16 +362,18 @@ module ActiveRecord
|
|
362
362
|
end
|
363
363
|
|
364
364
|
def primary_keys(table_name) # :nodoc:
|
365
|
-
scope = quoted_scope(table_name)
|
366
365
|
select_values(<<-SQL.strip_heredoc, "SCHEMA")
|
367
|
-
SELECT
|
368
|
-
FROM
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
366
|
+
SELECT a.attname
|
367
|
+
FROM (
|
368
|
+
SELECT indrelid, indkey, generate_subscripts(indkey, 1) idx
|
369
|
+
FROM pg_index
|
370
|
+
WHERE indrelid = #{quote(quote_table_name(table_name))}::regclass
|
371
|
+
AND indisprimary
|
372
|
+
) i
|
373
|
+
JOIN pg_attribute a
|
374
|
+
ON a.attrelid = i.indrelid
|
375
|
+
AND a.attnum = i.indkey[i.idx]
|
376
|
+
ORDER BY i.idx
|
375
377
|
SQL
|
376
378
|
end
|
377
379
|
|
@@ -19,9 +19,9 @@ module ActiveRecord
|
|
19
19
|
|
20
20
|
def quoted
|
21
21
|
if schema
|
22
|
-
|
22
|
+
PG::Connection.quote_ident(schema) << SEPARATOR << PG::Connection.quote_ident(identifier)
|
23
23
|
else
|
24
|
-
|
24
|
+
PG::Connection.quote_ident(identifier)
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
@@ -29,11 +29,11 @@ module ActiveRecord
|
|
29
29
|
conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
|
30
30
|
conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
|
31
31
|
|
32
|
-
# Forward only valid config params to
|
33
|
-
valid_conn_param_keys =
|
32
|
+
# Forward only valid config params to PG::Connection.connect.
|
33
|
+
valid_conn_param_keys = PG::Connection.conndefaults_hash.keys + [:requiressl]
|
34
34
|
conn_params.slice!(*valid_conn_param_keys)
|
35
35
|
|
36
|
-
# The postgres drivers don't allow the creation of an unconnected
|
36
|
+
# The postgres drivers don't allow the creation of an unconnected PG::Connection object,
|
37
37
|
# so just pass a nil connection object for the time being.
|
38
38
|
ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, conn_params, config)
|
39
39
|
end
|
@@ -201,8 +201,8 @@ module ActiveRecord
|
|
201
201
|
end
|
202
202
|
|
203
203
|
def connection_active?
|
204
|
-
@connection.status ==
|
205
|
-
rescue
|
204
|
+
@connection.status == PG::CONNECTION_OK
|
205
|
+
rescue PG::Error
|
206
206
|
false
|
207
207
|
end
|
208
208
|
end
|
@@ -247,34 +247,42 @@ module ActiveRecord
|
|
247
247
|
|
248
248
|
# Is this connection alive and ready for queries?
|
249
249
|
def active?
|
250
|
-
@
|
250
|
+
@lock.synchronize do
|
251
|
+
@connection.query "SELECT 1"
|
252
|
+
end
|
251
253
|
true
|
252
|
-
rescue
|
254
|
+
rescue PG::Error
|
253
255
|
false
|
254
256
|
end
|
255
257
|
|
256
258
|
# Close then reopen the connection.
|
257
259
|
def reconnect!
|
258
|
-
|
259
|
-
|
260
|
-
|
260
|
+
@lock.synchronize do
|
261
|
+
super
|
262
|
+
@connection.reset
|
263
|
+
configure_connection
|
264
|
+
end
|
261
265
|
end
|
262
266
|
|
263
267
|
def reset!
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
@connection.
|
268
|
+
@lock.synchronize do
|
269
|
+
clear_cache!
|
270
|
+
reset_transaction
|
271
|
+
unless @connection.transaction_status == ::PG::PQTRANS_IDLE
|
272
|
+
@connection.query "ROLLBACK"
|
273
|
+
end
|
274
|
+
@connection.query "DISCARD ALL"
|
275
|
+
configure_connection
|
268
276
|
end
|
269
|
-
@connection.query "DISCARD ALL"
|
270
|
-
configure_connection
|
271
277
|
end
|
272
278
|
|
273
279
|
# Disconnects from the database if already connected. Otherwise, this
|
274
280
|
# method does nothing.
|
275
281
|
def disconnect!
|
276
|
-
|
277
|
-
|
282
|
+
@lock.synchronize do
|
283
|
+
super
|
284
|
+
@connection.close rescue nil
|
285
|
+
end
|
278
286
|
end
|
279
287
|
|
280
288
|
def native_database_types #:nodoc:
|
@@ -314,6 +322,12 @@ module ActiveRecord
|
|
314
322
|
postgresql_version >= 90400
|
315
323
|
end
|
316
324
|
|
325
|
+
def supports_alter_constraint?
|
326
|
+
# PostgreSQL 9.4 introduces ALTER TABLE ... ALTER CONSTRAINT but it has a bug and fixed in 9.4.2
|
327
|
+
# https://www.postgresql.org/docs/9.4/static/release-9-4-2.html
|
328
|
+
postgresql_version >= 90402
|
329
|
+
end
|
330
|
+
|
317
331
|
def get_advisory_lock(lock_id) # :nodoc:
|
318
332
|
unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
|
319
333
|
raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer")
|
@@ -414,7 +428,7 @@ module ActiveRecord
|
|
414
428
|
def translate_exception(exception, message)
|
415
429
|
return exception unless exception.respond_to?(:result)
|
416
430
|
|
417
|
-
case exception.result.try(:error_field,
|
431
|
+
case exception.result.try(:error_field, PG::PG_DIAG_SQLSTATE)
|
418
432
|
when UNIQUE_VIOLATION
|
419
433
|
RecordNotUnique.new(message)
|
420
434
|
when FOREIGN_KEY_VIOLATION
|
@@ -651,7 +665,7 @@ module ActiveRecord
|
|
651
665
|
CACHED_PLAN_HEURISTIC = "cached plan must not change result type".freeze
|
652
666
|
def is_cached_plan_failure?(e)
|
653
667
|
pgerror = e.cause
|
654
|
-
code = pgerror.result.result_error_field(
|
668
|
+
code = pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE)
|
655
669
|
code == FEATURE_NOT_SUPPORTED && pgerror.message.include?(CACHED_PLAN_HEURISTIC)
|
656
670
|
rescue
|
657
671
|
false
|
@@ -690,7 +704,7 @@ module ActiveRecord
|
|
690
704
|
# Connects to a PostgreSQL server and sets up the adapter depending on the
|
691
705
|
# connected server's characteristics.
|
692
706
|
def connect
|
693
|
-
@connection =
|
707
|
+
@connection = PG.connect(@connection_parameters)
|
694
708
|
configure_connection
|
695
709
|
rescue ::PG::Error => error
|
696
710
|
if error.message.include?("does not exist")
|
@@ -44,17 +44,17 @@ module ActiveRecord
|
|
44
44
|
private
|
45
45
|
|
46
46
|
def type_casted_binds(binds, casted_binds)
|
47
|
-
casted_binds ||
|
47
|
+
casted_binds || ActiveRecord::Base.connection.type_casted_binds(binds)
|
48
48
|
end
|
49
49
|
|
50
|
-
def render_bind(attr,
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
50
|
+
def render_bind(attr, value)
|
51
|
+
if attr.is_a?(Array)
|
52
|
+
attr = attr.first
|
53
|
+
elsif attr.type.binary? && attr.value
|
54
|
+
value = "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
|
55
55
|
end
|
56
56
|
|
57
|
-
[attr.name, value]
|
57
|
+
[attr && attr.name, value]
|
58
58
|
end
|
59
59
|
|
60
60
|
def colorize_payload_name(name, payload_name)
|
@@ -89,10 +89,6 @@ module ActiveRecord
|
|
89
89
|
def logger
|
90
90
|
ActiveRecord::Base.logger
|
91
91
|
end
|
92
|
-
|
93
|
-
def type_cast(value)
|
94
|
-
ActiveRecord::Base.connection.type_cast(value)
|
95
|
-
end
|
96
92
|
end
|
97
93
|
end
|
98
94
|
|
@@ -100,6 +100,10 @@ module ActiveRecord
|
|
100
100
|
!(@new_record || @destroyed)
|
101
101
|
end
|
102
102
|
|
103
|
+
##
|
104
|
+
# :call-seq:
|
105
|
+
# save(*args)
|
106
|
+
#
|
103
107
|
# Saves the model.
|
104
108
|
#
|
105
109
|
# If the model is new, a record gets created in the database, otherwise
|
@@ -121,12 +125,16 @@ module ActiveRecord
|
|
121
125
|
#
|
122
126
|
# Attributes marked as readonly are silently ignored if the record is
|
123
127
|
# being updated.
|
124
|
-
def save(*args)
|
125
|
-
create_or_update(*args)
|
128
|
+
def save(*args, &block)
|
129
|
+
create_or_update(*args, &block)
|
126
130
|
rescue ActiveRecord::RecordInvalid
|
127
131
|
false
|
128
132
|
end
|
129
133
|
|
134
|
+
##
|
135
|
+
# :call-seq:
|
136
|
+
# save!(*args)
|
137
|
+
#
|
130
138
|
# Saves the model.
|
131
139
|
#
|
132
140
|
# If the model is new, a record gets created in the database, otherwise
|
@@ -150,8 +158,8 @@ module ActiveRecord
|
|
150
158
|
# being updated.
|
151
159
|
#
|
152
160
|
# Unless an error is raised, returns true.
|
153
|
-
def save!(*args)
|
154
|
-
create_or_update(*args) || raise(RecordNotSaved.new("Failed to save the record", self))
|
161
|
+
def save!(*args, &block)
|
162
|
+
create_or_update(*args, &block) || raise(RecordNotSaved.new("Failed to save the record", self))
|
155
163
|
end
|
156
164
|
|
157
165
|
# Deletes the record in the database and freezes this instance to
|
@@ -550,9 +558,9 @@ module ActiveRecord
|
|
550
558
|
self.class.unscoped.where(self.class.primary_key => id)
|
551
559
|
end
|
552
560
|
|
553
|
-
def create_or_update(*args)
|
561
|
+
def create_or_update(*args, &block)
|
554
562
|
_raise_readonly_record_error if readonly?
|
555
|
-
result = new_record? ? _create_record : _update_record(*args)
|
563
|
+
result = new_record? ? _create_record(&block) : _update_record(*args, &block)
|
556
564
|
result != false
|
557
565
|
end
|
558
566
|
|
@@ -567,6 +575,9 @@ module ActiveRecord
|
|
567
575
|
rows_affected = self.class.unscoped._update_record attributes_values, id, id_in_database
|
568
576
|
@_trigger_update_callback = rows_affected > 0
|
569
577
|
end
|
578
|
+
|
579
|
+
yield(self) if block_given?
|
580
|
+
|
570
581
|
rows_affected
|
571
582
|
end
|
572
583
|
|
@@ -579,6 +590,9 @@ module ActiveRecord
|
|
579
590
|
self.id ||= new_id if self.class.primary_key
|
580
591
|
|
581
592
|
@new_record = false
|
593
|
+
|
594
|
+
yield(self) if block_given?
|
595
|
+
|
582
596
|
id
|
583
597
|
end
|
584
598
|
|
@@ -77,6 +77,8 @@ db_namespace = namespace :db do
|
|
77
77
|
namespace :migrate do
|
78
78
|
# desc 'Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x).'
|
79
79
|
task redo: [:environment, :load_config] do
|
80
|
+
raise "Empty VERSION provided" if ENV["VERSION"] && ENV["VERSION"].empty?
|
81
|
+
|
80
82
|
if ENV["VERSION"]
|
81
83
|
db_namespace["migrate:down"].invoke
|
82
84
|
db_namespace["migrate:up"].invoke
|
@@ -91,16 +93,17 @@ db_namespace = namespace :db do
|
|
91
93
|
|
92
94
|
# desc 'Runs the "up" for a given migration VERSION.'
|
93
95
|
task up: [:environment, :load_config] do
|
96
|
+
raise "VERSION is required" if ENV["VERSION"] && ENV["VERSION"].empty?
|
97
|
+
|
94
98
|
version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
|
95
|
-
raise "VERSION is required" unless version
|
96
99
|
ActiveRecord::Migrator.run(:up, ActiveRecord::Tasks::DatabaseTasks.migrations_paths, version)
|
97
100
|
db_namespace["_dump"].invoke
|
98
101
|
end
|
99
102
|
|
100
103
|
# desc 'Runs the "down" for a given migration VERSION.'
|
101
104
|
task down: [:environment, :load_config] do
|
105
|
+
raise "VERSION is required - To go down one migration, use db:rollback" if ENV["VERSION"] && ENV["VERSION"].empty?
|
102
106
|
version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
|
103
|
-
raise "VERSION is required - To go down one migration, run db:rollback" unless version
|
104
107
|
ActiveRecord::Migrator.run(:down, ActiveRecord::Tasks::DatabaseTasks.migrations_paths, version)
|
105
108
|
db_namespace["_dump"].invoke
|
106
109
|
end
|
@@ -635,7 +635,9 @@ module ActiveRecord
|
|
635
635
|
end
|
636
636
|
|
637
637
|
def inspect
|
638
|
-
|
638
|
+
subject = loaded? ? records : self
|
639
|
+
entries = subject.take([limit_value, 11].compact.min).map!(&:inspect)
|
640
|
+
|
639
641
|
entries[10] = "..." if entries.size == 11
|
640
642
|
|
641
643
|
"#<#{self.class.name} [#{entries.join(', ')}]>"
|
@@ -312,16 +312,7 @@ module ActiveRecord
|
|
312
312
|
relation = apply_join_dependency(self, construct_join_dependency(eager_loading: false))
|
313
313
|
return false if ActiveRecord::NullRelation === relation
|
314
314
|
|
315
|
-
relation = relation
|
316
|
-
|
317
|
-
case conditions
|
318
|
-
when Array, Hash
|
319
|
-
relation = relation.where(conditions)
|
320
|
-
else
|
321
|
-
unless conditions == :none
|
322
|
-
relation = relation.where(primary_key => conditions)
|
323
|
-
end
|
324
|
-
end
|
315
|
+
relation = construct_relation_for_exists(relation, conditions)
|
325
316
|
|
326
317
|
connection.select_value(relation, "#{name} Exists", relation.bound_attributes) ? true : false
|
327
318
|
rescue ::RangeError
|
@@ -391,6 +382,19 @@ module ActiveRecord
|
|
391
382
|
end
|
392
383
|
end
|
393
384
|
|
385
|
+
def construct_relation_for_exists(relation, conditions)
|
386
|
+
relation = relation.except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
|
387
|
+
|
388
|
+
case conditions
|
389
|
+
when Array, Hash
|
390
|
+
relation.where!(conditions)
|
391
|
+
else
|
392
|
+
relation.where!(primary_key => conditions) unless conditions == :none
|
393
|
+
end
|
394
|
+
|
395
|
+
relation
|
396
|
+
end
|
397
|
+
|
394
398
|
def construct_join_dependency(joins = [], eager_loading: true)
|
395
399
|
including = eager_load_values + includes_values
|
396
400
|
ActiveRecord::Associations::JoinDependency.new(@klass, including, joins, eager_loading: eager_loading)
|
@@ -401,8 +405,7 @@ module ActiveRecord
|
|
401
405
|
end
|
402
406
|
|
403
407
|
def apply_join_dependency(relation, join_dependency)
|
404
|
-
relation = relation.except(:includes, :eager_load, :preload)
|
405
|
-
relation = relation.joins join_dependency
|
408
|
+
relation = relation.except(:includes, :eager_load, :preload).joins!(join_dependency)
|
406
409
|
|
407
410
|
if using_limitable_reflections?(join_dependency.reflections)
|
408
411
|
relation
|
@@ -148,10 +148,10 @@ module ActiveRecord
|
|
148
148
|
(binds_index...(binds_index + binds_contains)).each do |i|
|
149
149
|
except_binds[i] = true
|
150
150
|
end
|
151
|
-
|
152
|
-
binds_index += binds_contains
|
153
151
|
end
|
154
152
|
|
153
|
+
binds_index += binds_contains if binds_contains
|
154
|
+
|
155
155
|
except
|
156
156
|
end
|
157
157
|
|
@@ -162,9 +162,11 @@ module ActiveRecord
|
|
162
162
|
end
|
163
163
|
|
164
164
|
def migrate
|
165
|
+
raise "Empty VERSION provided" if ENV["VERSION"] && ENV["VERSION"].empty?
|
166
|
+
|
165
167
|
verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
|
166
168
|
version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
|
167
|
-
scope
|
169
|
+
scope = ENV["SCOPE"]
|
168
170
|
verbose_was, Migration.verbose = Migration.verbose, verbose
|
169
171
|
Migrator.migrate(migrations_paths, version) do |migration|
|
170
172
|
scope.blank? || scope == migration.scope
|
@@ -104,7 +104,7 @@ module ActiveRecord
|
|
104
104
|
|
105
105
|
def grant_statement
|
106
106
|
<<-SQL
|
107
|
-
GRANT ALL PRIVILEGES ON
|
107
|
+
GRANT ALL PRIVILEGES ON `#{configuration['database']}`.*
|
108
108
|
TO '#{configuration['username']}'@'localhost'
|
109
109
|
IDENTIFIED BY '#{configuration['password']}' WITH GRANT OPTION;
|
110
110
|
SQL
|
@@ -123,7 +123,7 @@ module ActiveRecord
|
|
123
123
|
# # statement will cause a PostgreSQL error, even though the unique
|
124
124
|
# # constraint is no longer violated:
|
125
125
|
# Number.create(i: 1)
|
126
|
-
# # => "
|
126
|
+
# # => "PG::Error: ERROR: current transaction is aborted, commands
|
127
127
|
# # ignored until end of transaction block"
|
128
128
|
# end
|
129
129
|
#
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.1.0.
|
4
|
+
version: 5.1.0.rc2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Heinemeier Hansson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-04-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 5.1.0.
|
19
|
+
version: 5.1.0.rc2
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 5.1.0.
|
26
|
+
version: 5.1.0.rc2
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activemodel
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - '='
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 5.1.0.
|
33
|
+
version: 5.1.0.rc2
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - '='
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 5.1.0.
|
40
|
+
version: 5.1.0.rc2
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: arel
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|