activerecord-pg-extensions 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|