activerecord-pg-extensions 0.3.0 → 0.4.3
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/pessimistic_migrations.rb +2 -1
- data/lib/active_record/pg_extensions/postgresql_adapter.rb +31 -5
- 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 +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 33e9819bdc74cc2783fd5a98746354d8ab13295e66c239eb04bb240b365b829d
|
4
|
+
data.tar.gz: 4d93cb57f18d3becc1b988ab1aa3b4e6b98959649642583d5d0a5fbb09c3eb5a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 42b9849e30f65df38b23e3ce1a9290875037bf4f2af7489f30f18ae9e242440c9dbbe98e35017599d85ac5431858044db346f83a972ffea6af7000e9fdeba67c
|
7
|
+
data.tar.gz: b472403821b0b83a5e9e5010a01bd9677b3af11e9cadaac9b7bc78570fcb65a2b746654075ddca60c4505092255269f6af78d801ba1758a1b5c8d295e156fff0
|
data/CHANGELOG.md
CHANGED
@@ -12,7 +12,8 @@ module ActiveRecord
|
|
12
12
|
return super if nullness != false || open_transactions.positive?
|
13
13
|
return if columns(table).find { |c| c.name == column.to_s }&.null == false
|
14
14
|
|
15
|
-
|
15
|
+
# PG identifiers max out at 63 characters
|
16
|
+
temp_constraint_name = "chk_rails_#{table}_#{column}_not_null"[0...63]
|
16
17
|
scope = quoted_scope(temp_constraint_name)
|
17
18
|
# check for temp constraint
|
18
19
|
valid = select_value(<<~SQL, "SCHEMA")
|
@@ -236,15 +236,41 @@ module ActiveRecord
|
|
236
236
|
select_value("SELECT pg_is_in_recovery()")
|
237
237
|
end
|
238
238
|
|
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
|
239
268
|
def with_statement_timeout(timeout = nil)
|
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 * 1000).to_i}ms", 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 * 1000).to_i}ms", 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 '30000ms'",
|
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 '5500ms'",
|
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 '5000ms'",
|
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 '30000ms'",
|
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 '30000ms'",
|
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 '30000ms'",
|
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 '30000ms'",
|
414
|
+
"SAVEPOINT active_record_1",
|
415
|
+
"SET LOCAL statement_timeout TO '15000ms'",
|
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.3
|
4
|
+
version: 0.4.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cody Cutrer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-02-11 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
|
@@ -205,7 +206,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
205
206
|
- !ruby/object:Gem::Version
|
206
207
|
version: '0'
|
207
208
|
requirements: []
|
208
|
-
rubygems_version: 3.
|
209
|
+
rubygems_version: 3.1.4
|
209
210
|
signing_key:
|
210
211
|
specification_version: 4
|
211
212
|
summary: Several extensions to ActiveRecord's PostgreSQLAdapter.
|