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.

Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +24 -0
  3. data/lib/active_record/associations.rb +1 -1
  4. data/lib/active_record/associations/collection_association.rb +32 -35
  5. data/lib/active_record/associations/has_many_association.rb +1 -6
  6. data/lib/active_record/associations/has_many_through_association.rb +1 -5
  7. data/lib/active_record/autosave_association.rb +5 -0
  8. data/lib/active_record/connection_adapters/abstract/quoting.rb +7 -8
  9. data/lib/active_record/connection_adapters/abstract/transaction.rb +49 -39
  10. data/lib/active_record/connection_adapters/abstract_adapter.rb +2 -4
  11. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +0 -8
  12. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +4 -3
  13. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +10 -0
  14. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
  15. data/lib/active_record/connection_adapters/postgresql/quoting.rb +3 -3
  16. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -4
  17. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +11 -9
  18. data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -2
  19. data/lib/active_record/connection_adapters/postgresql_adapter.rb +35 -21
  20. data/lib/active_record/gem_version.rb +1 -1
  21. data/lib/active_record/log_subscriber.rb +7 -11
  22. data/lib/active_record/persistence.rb +20 -6
  23. data/lib/active_record/railties/databases.rake +5 -2
  24. data/lib/active_record/relation.rb +3 -1
  25. data/lib/active_record/relation/finder_methods.rb +15 -12
  26. data/lib/active_record/relation/where_clause.rb +2 -2
  27. data/lib/active_record/tasks/database_tasks.rb +3 -1
  28. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  29. data/lib/active_record/transactions.rb +1 -1
  30. metadata +6 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 56124430c2b0a3ea8b0d63b972a18f7818d5f044
4
- data.tar.gz: 2b59776ce9564b180d0107fd7acd2155519d3058
3
+ metadata.gz: 68113d7968e655698251c14372de192d2e8e38da
4
+ data.tar.gz: 80fdb87c4ba3d63700799dd66b24d078c9ab9748
5
5
  SHA512:
6
- metadata.gz: 06e06443ed8dc3bfb78b8685ac892b032f065d06712570ba8c318f670f2b489efc2b2c2500fb68eea55d661077a1b626aa6135413b84d1e7ee18909dadbbc3d3
7
- data.tar.gz: d37ce0bce1c266f7b5c283d203fc3466478a572067b104dd239d528c33cc50929ed8b7bee61365e00eb0e051081741ff014f1ead3983fbe1d26a40321a861d53
6
+ metadata.gz: 84e1eb324d24437342da23fc2d7c255ce23a2fab0888e5d347f98eb5bdfa9d8ef1e066800d7383f8cea1c76b006c965e1aa61599937502580da69821eea21844
7
+ data.tar.gz: de2d5f8de26cf8593c3ad320f9793d1df65260501b097dbbe8fadc122b97588a42c85cdc8ed7cec21e33106d076390dff00fa2cd5073eaf390eb3bd561f95e19
@@ -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 NotImplementedError
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, should_raise = false)
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 |rec|
457
- result &&= insert_record(rec, true, should_raise) unless owner.new_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
- if raise
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
- private
156
-
157
- def type_casted_binds(binds)
158
- if binds.first.is_a?(Array)
159
- binds.map { |column, value| type_cast(value, column) }
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
- run_commit_callbacks = !current_transaction.joinable?
153
- transaction =
154
- if @stack.empty?
155
- RealTransaction.new(@connection, options, run_commit_callbacks: run_commit_callbacks)
156
- else
157
- SavepointTransaction.new(@connection, "active_record_#{@stack.size}", options,
158
- run_commit_callbacks: run_commit_callbacks)
159
- end
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
- @stack.push(transaction)
162
- transaction
162
+ @stack.push(transaction)
163
+ transaction
164
+ end
163
165
  end
164
166
 
165
167
  def commit_transaction
166
- transaction = @stack.last
168
+ @connection.lock.synchronize do
169
+ transaction = @stack.last
167
170
 
168
- begin
169
- transaction.before_commit_records
170
- ensure
171
- @stack.pop
172
- end
171
+ begin
172
+ transaction.before_commit_records
173
+ ensure
174
+ @stack.pop
175
+ end
173
176
 
174
- transaction.commit
175
- transaction.commit_records
177
+ transaction.commit
178
+ transaction.commit_records
179
+ end
176
180
  end
177
181
 
178
182
  def rollback_transaction(transaction = nil)
179
- transaction ||= @stack.pop
180
- transaction.rollback
181
- transaction.rollback_records
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
- transaction = begin_transaction options
186
- yield
187
- rescue Exception => error
188
- if transaction
189
- rollback_transaction
190
- after_failure_actions(transaction, error)
191
- end
192
- raise
193
- ensure
194
- unless error
195
- if Thread.current.status == "aborting"
196
- rollback_transaction if transaction
197
- else
198
- begin
199
- commit_transaction
200
- rescue Exception
201
- rollback_transaction(transaction) unless transaction.state.completed?
202
- raise
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 PGconn object in case of PostgreSQLAdapter.
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}/m =~ create_table_info
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 = #{quote(@config[:database])}" \
64
- " AND table_name = #{quote(column.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
 
@@ -6,7 +6,7 @@ module ActiveRecord
6
6
  def deserialize(value)
7
7
  return if value.nil?
8
8
  return value.to_s if value.is_a?(Type::Binary::Data)
9
- PGconn.unescape_bytea(super)
9
+ PG::Connection.unescape_bytea(super)
10
10
  end
11
11
  end
12
12
  end
@@ -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
- PGconn.quote_ident(name)
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] ||= PGconn.quote_ident(super).freeze
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 http://deveiate.org/code/pg/PGconn.html#method-i-exec_prepared-doc
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 column_name
368
- FROM information_schema.key_column_usage kcu
369
- JOIN information_schema.table_constraints tc
370
- USING (table_schema, table_name, constraint_name)
371
- WHERE constraint_type = 'PRIMARY KEY'
372
- AND kcu.table_name = #{scope[:name]}
373
- AND kcu.table_schema = #{scope[:schema]}
374
- ORDER BY kcu.ordinal_position
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
- PGconn.quote_ident(schema) << SEPARATOR << PGconn.quote_ident(identifier)
22
+ PG::Connection.quote_ident(schema) << SEPARATOR << PG::Connection.quote_ident(identifier)
23
23
  else
24
- PGconn.quote_ident(identifier)
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 PGconn.connect.
33
- valid_conn_param_keys = PGconn.conndefaults_hash.keys + [:requiressl]
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 PGconn object,
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 == PGconn::CONNECTION_OK
205
- rescue PGError
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
- @connection.query "SELECT 1"
250
+ @lock.synchronize do
251
+ @connection.query "SELECT 1"
252
+ end
251
253
  true
252
- rescue PGError
254
+ rescue PG::Error
253
255
  false
254
256
  end
255
257
 
256
258
  # Close then reopen the connection.
257
259
  def reconnect!
258
- super
259
- @connection.reset
260
- configure_connection
260
+ @lock.synchronize do
261
+ super
262
+ @connection.reset
263
+ configure_connection
264
+ end
261
265
  end
262
266
 
263
267
  def reset!
264
- clear_cache!
265
- reset_transaction
266
- unless @connection.transaction_status == ::PG::PQTRANS_IDLE
267
- @connection.query "ROLLBACK"
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
- super
277
- @connection.close rescue nil
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, PGresult::PG_DIAG_SQLSTATE)
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(PGresult::PG_DIAG_SQLSTATE)
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 = PGconn.connect(@connection_parameters)
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")
@@ -8,7 +8,7 @@ module ActiveRecord
8
8
  MAJOR = 5
9
9
  MINOR = 1
10
10
  TINY = 0
11
- PRE = "rc1"
11
+ PRE = "rc2"
12
12
 
13
13
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
14
14
  end
@@ -44,17 +44,17 @@ module ActiveRecord
44
44
  private
45
45
 
46
46
  def type_casted_binds(binds, casted_binds)
47
- casted_binds || binds.map { |attr| type_cast attr.value_for_database }
47
+ casted_binds || ActiveRecord::Base.connection.type_casted_binds(binds)
48
48
  end
49
49
 
50
- def render_bind(attr, type_casted_value)
51
- value = if attr.type.binary? && attr.value
52
- "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
53
- else
54
- type_casted_value
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
- entries = records.take([limit_value, 11].compact.min).map!(&:inspect)
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.except(:select, :distinct).select(ONE_AS_ONE).limit(1)
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 = ENV["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 #{configuration['database']}.*
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
- # # => "PGError: ERROR: current transaction is aborted, commands
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.rc1
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-03-20 00:00:00.000000000 Z
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.rc1
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.rc1
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.rc1
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.rc1
40
+ version: 5.1.0.rc2
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: arel
43
43
  requirement: !ruby/object:Gem::Requirement