activerecord-pg-extensions 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/lib/active_record/pg_extensions/errors.rb +2 -1
- data/lib/active_record/pg_extensions/postgresql_adapter.rb +32 -6
- data/lib/active_record/pg_extensions/railtie.rb +4 -1
- data/lib/active_record/pg_extensions/transaction.rb +45 -0
- data/lib/active_record/pg_extensions/version.rb +1 -1
- data/spec/postgresql_adapter_spec.rb +93 -6
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3494201ce01b5c6d5104ae9ddb7f993310223ecc94bd902b2484ce6b6a4e525b
|
4
|
+
data.tar.gz: acd5b4c842cbfc275b2a86f009c048e7916709803f3d72c9ed40f42095c47a7d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 91625c727722fb91aa1860960a3f9c5ba7ba4dcab0a0cf459e6b8301a7fa734ff91882f5ae31ac4cb492218eaf17205ed97e54236cf8760d55b6c33b4cd3f7dc
|
7
|
+
data.tar.gz: fbe3e4a073f7f646438b07f1226e5c654a744c3efb421ea38b9c7b7adc5cf9e173fbab479780f765cc41f3c9144294d1ac16fe7f140e68bcdf44128a0bcafd68
|
data/CHANGELOG.md
CHANGED
@@ -236,15 +236,41 @@ module ActiveRecord
|
|
236
236
|
select_value("SELECT pg_is_in_recovery()")
|
237
237
|
end
|
238
238
|
|
239
|
-
def
|
239
|
+
def set(configuration_parameter, value, local: false)
|
240
|
+
value = value.nil? ? "DEFAULT" : quote(value)
|
241
|
+
execute("SET#{' LOCAL' if local} #{configuration_parameter} TO #{value}")
|
242
|
+
end
|
243
|
+
|
244
|
+
def reset(configuration_parameter)
|
245
|
+
execute("RESET #{configuration_parameter}")
|
246
|
+
end
|
247
|
+
|
248
|
+
TIMEOUTS = %i[lock_timeout statement_timeout idle_in_transaction_session_timeout].freeze
|
249
|
+
|
250
|
+
TIMEOUTS.each do |kind|
|
251
|
+
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
252
|
+
def #{kind}
|
253
|
+
current_transaction.#{kind}
|
254
|
+
end
|
255
|
+
|
256
|
+
def #{kind}=(timeout)
|
257
|
+
raise ArgumentError, "Timeouts can only be set inside of a transaction" unless current_transaction.open?
|
258
|
+
|
259
|
+
current_transaction.send(:#{kind}=, timeout)
|
260
|
+
end
|
261
|
+
RUBY
|
262
|
+
end
|
263
|
+
|
264
|
+
# @deprecated: manage the transaction yourself and set statement_timeout directly
|
265
|
+
#
|
266
|
+
# otherwise, if you're already in a transaction, or you nest with_statement_timeout,
|
267
|
+
# the value will unexpectedly "stick" even after the block returns
|
268
|
+
def with_statement_timeout(timeout)
|
240
269
|
timeout = 30 if timeout.nil? || timeout == true
|
270
|
+
|
241
271
|
transaction do
|
242
|
-
|
272
|
+
self.statement_timeout = timeout
|
243
273
|
yield
|
244
|
-
rescue ActiveRecord::StatementInvalid => e
|
245
|
-
raise ActiveRecord::QueryTimeout.new(sql: e.sql, binds: e.binds) if e.cause.is_a?(PG::QueryCanceled)
|
246
|
-
|
247
|
-
raise
|
248
274
|
end
|
249
275
|
end
|
250
276
|
|
@@ -10,8 +10,11 @@ module ActiveRecord
|
|
10
10
|
ActiveSupport.on_load(:active_record) do
|
11
11
|
require "active_record/pg_extensions/errors"
|
12
12
|
require "active_record/pg_extensions/postgresql_adapter"
|
13
|
+
require "active_record/pg_extensions/transaction"
|
13
14
|
|
14
|
-
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(PostgreSQLAdapter)
|
15
|
+
::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(PostgreSQLAdapter)
|
16
|
+
::ActiveRecord::ConnectionAdapters::NullTransaction.prepend(NullTransaction)
|
17
|
+
::ActiveRecord::ConnectionAdapters::Transaction.prepend(Transaction)
|
15
18
|
# if they've already require 'all', then inject now
|
16
19
|
defined?(All) && All.inject
|
17
20
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module PGExtensions
|
5
|
+
# Contains general additions to Transaction
|
6
|
+
module Transaction
|
7
|
+
PostgreSQLAdapter::TIMEOUTS.each do |kind|
|
8
|
+
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
9
|
+
def #{kind}(local: false)
|
10
|
+
return @#{kind} if local
|
11
|
+
@#{kind} || parent_transaction.#{kind}
|
12
|
+
end
|
13
|
+
|
14
|
+
private def #{kind}=(timeout)
|
15
|
+
return if @#{kind} == timeout
|
16
|
+
|
17
|
+
@#{kind} = timeout
|
18
|
+
return unless materialized?
|
19
|
+
connection.set(#{kind.inspect}, "\#{timeout}s", local: true)
|
20
|
+
end
|
21
|
+
RUBY
|
22
|
+
end
|
23
|
+
|
24
|
+
def materialize!
|
25
|
+
PostgreSQLAdapter::TIMEOUTS.each do |kind|
|
26
|
+
next if (timeout = send(kind, local: true)).nil?
|
27
|
+
|
28
|
+
connection.set(kind, "#{timeout}s", local: true)
|
29
|
+
end
|
30
|
+
super
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Contains general additions to NullTransaction
|
35
|
+
module NullTransaction
|
36
|
+
PostgreSQLAdapter::TIMEOUTS.each do |kind|
|
37
|
+
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
38
|
+
def #{kind}
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
RUBY
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -270,12 +270,20 @@ describe ActiveRecord::ConnectionAdapters::PostgreSQLAdapter do
|
|
270
270
|
end
|
271
271
|
|
272
272
|
describe "#with_statement_timeout" do
|
273
|
+
around do |example|
|
274
|
+
# these specs were written before we supported deferring setting timeouts
|
275
|
+
# until the transaction materializes
|
276
|
+
connection.disable_lazy_transactions!
|
277
|
+
example.call
|
278
|
+
connection.enable_lazy_transactions!
|
279
|
+
end
|
280
|
+
|
273
281
|
it "stops long-running queries" do
|
274
282
|
expect do
|
275
283
|
connection.with_statement_timeout(0.01) do
|
276
284
|
connection.execute("SELECT pg_sleep(3)")
|
277
285
|
end
|
278
|
-
end.to raise_error(ActiveRecord::
|
286
|
+
end.to raise_error(ActiveRecord::QueryCanceled)
|
279
287
|
end
|
280
288
|
|
281
289
|
it "re-raises other errors" do
|
@@ -283,16 +291,20 @@ describe ActiveRecord::ConnectionAdapters::PostgreSQLAdapter do
|
|
283
291
|
connection.with_statement_timeout(1) do
|
284
292
|
connection.execute("bad sql")
|
285
293
|
end
|
286
|
-
end.to
|
294
|
+
end.to(raise_error { |e| expect(e.cause).to be_a(PG::SyntaxError) })
|
287
295
|
end
|
288
296
|
|
289
297
|
context "without executing" do
|
298
|
+
around do |example|
|
299
|
+
connection.dont_execute(&example)
|
300
|
+
end
|
301
|
+
|
290
302
|
it "converts integer to ms" do
|
291
303
|
connection.with_statement_timeout(30) { nil }
|
292
304
|
expect(connection.executed_statements).to eq(
|
293
305
|
[
|
294
306
|
"BEGIN",
|
295
|
-
"SET LOCAL statement_timeout
|
307
|
+
"SET LOCAL statement_timeout TO '30s'",
|
296
308
|
"COMMIT"
|
297
309
|
]
|
298
310
|
)
|
@@ -303,7 +315,7 @@ describe ActiveRecord::ConnectionAdapters::PostgreSQLAdapter do
|
|
303
315
|
expect(connection.executed_statements).to eq(
|
304
316
|
[
|
305
317
|
"BEGIN",
|
306
|
-
"SET LOCAL statement_timeout
|
318
|
+
"SET LOCAL statement_timeout TO '5.5s'",
|
307
319
|
"COMMIT"
|
308
320
|
]
|
309
321
|
)
|
@@ -314,7 +326,7 @@ describe ActiveRecord::ConnectionAdapters::PostgreSQLAdapter do
|
|
314
326
|
expect(connection.executed_statements).to eq(
|
315
327
|
[
|
316
328
|
"BEGIN",
|
317
|
-
"SET LOCAL statement_timeout
|
329
|
+
"SET LOCAL statement_timeout TO '5s'",
|
318
330
|
"COMMIT"
|
319
331
|
]
|
320
332
|
)
|
@@ -325,7 +337,7 @@ describe ActiveRecord::ConnectionAdapters::PostgreSQLAdapter do
|
|
325
337
|
expect(connection.executed_statements).to eq(
|
326
338
|
[
|
327
339
|
"BEGIN",
|
328
|
-
"SET LOCAL statement_timeout
|
340
|
+
"SET LOCAL statement_timeout TO '30s'",
|
329
341
|
"COMMIT"
|
330
342
|
]
|
331
343
|
)
|
@@ -333,6 +345,81 @@ describe ActiveRecord::ConnectionAdapters::PostgreSQLAdapter do
|
|
333
345
|
end
|
334
346
|
end
|
335
347
|
|
348
|
+
describe "#statement_timeout=" do
|
349
|
+
around do |example|
|
350
|
+
connection.dont_execute(&example)
|
351
|
+
end
|
352
|
+
|
353
|
+
it "raises if a transaction isn't active" do
|
354
|
+
expect { connection.statement_timeout = 30 }.to raise_error(ArgumentError)
|
355
|
+
end
|
356
|
+
|
357
|
+
it "does nothing if the transaction never materializes" do
|
358
|
+
connection.transaction do
|
359
|
+
connection.statement_timeout = 30
|
360
|
+
expect(connection.statement_timeout).to eq 30
|
361
|
+
end
|
362
|
+
expect(connection.statement_timeout).to be_nil
|
363
|
+
|
364
|
+
expect(connection.executed_statements).to be_empty
|
365
|
+
end
|
366
|
+
|
367
|
+
it "sets the timeout if the transaction is materialized" do
|
368
|
+
connection.transaction do
|
369
|
+
connection.select_value("SELECT 1")
|
370
|
+
connection.statement_timeout = 30
|
371
|
+
expect(connection.statement_timeout).to eq 30
|
372
|
+
end
|
373
|
+
expect(connection.statement_timeout).to be_nil
|
374
|
+
|
375
|
+
expect(connection.executed_statements).to eq(
|
376
|
+
["BEGIN",
|
377
|
+
"SELECT 1",
|
378
|
+
"SET LOCAL statement_timeout TO '30s'",
|
379
|
+
"COMMIT"]
|
380
|
+
)
|
381
|
+
end
|
382
|
+
|
383
|
+
it "sets the timeout if the transaction materializes" do
|
384
|
+
connection.transaction do
|
385
|
+
connection.statement_timeout = 30
|
386
|
+
connection.select_value("SELECT 1")
|
387
|
+
expect(connection.statement_timeout).to eq 30
|
388
|
+
end
|
389
|
+
expect(connection.statement_timeout).to be_nil
|
390
|
+
|
391
|
+
expect(connection.executed_statements).to eq(
|
392
|
+
["BEGIN",
|
393
|
+
"SET LOCAL statement_timeout TO '30s'",
|
394
|
+
"SELECT 1",
|
395
|
+
"COMMIT"]
|
396
|
+
)
|
397
|
+
end
|
398
|
+
|
399
|
+
it "works with nested transactions" do
|
400
|
+
connection.transaction do
|
401
|
+
connection.statement_timeout = 30
|
402
|
+
connection.transaction(requires_new: true) do
|
403
|
+
connection.statement_timeout = 15
|
404
|
+
connection.select_value("SELECT 1")
|
405
|
+
expect(connection.statement_timeout).to eq 15
|
406
|
+
end
|
407
|
+
expect(connection.statement_timeout).to eq 30
|
408
|
+
end
|
409
|
+
expect(connection.statement_timeout).to be_nil
|
410
|
+
|
411
|
+
expect(connection.executed_statements).to eq(
|
412
|
+
["BEGIN",
|
413
|
+
"SET LOCAL statement_timeout TO '30s'",
|
414
|
+
"SAVEPOINT active_record_1",
|
415
|
+
"SET LOCAL statement_timeout TO '15s'",
|
416
|
+
"SELECT 1",
|
417
|
+
"RELEASE SAVEPOINT active_record_1",
|
418
|
+
"COMMIT"]
|
419
|
+
)
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
336
423
|
unless Rails.version >= "6.1"
|
337
424
|
describe "#add_check_constraint" do
|
338
425
|
around do |example|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-pg-extensions
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cody Cutrer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-09-
|
11
|
+
date: 2021-09-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -180,6 +180,7 @@ files:
|
|
180
180
|
- lib/active_record/pg_extensions/pessimistic_migrations.rb
|
181
181
|
- lib/active_record/pg_extensions/postgresql_adapter.rb
|
182
182
|
- lib/active_record/pg_extensions/railtie.rb
|
183
|
+
- lib/active_record/pg_extensions/transaction.rb
|
183
184
|
- lib/active_record/pg_extensions/version.rb
|
184
185
|
- lib/activerecord-pg-extensions.rb
|
185
186
|
- spec/pessimistic_migrations_spec.rb
|