activerecord-pg-extensions 0.4.4 → 0.5.1
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/lib/active_record/pg_extensions/pessimistic_migrations.rb +20 -12
- data/lib/active_record/pg_extensions/postgresql_adapter.rb +10 -67
- data/lib/active_record/pg_extensions/version.rb +1 -1
- metadata +16 -36
- data/config/database.yml +0 -3
- data/spec/pessimistic_migrations_spec.rb +0 -136
- data/spec/postgresql_adapter_spec.rb +0 -457
- data/spec/spec_helper.rb +0 -62
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2f00b73b6ca45b6816eb823afa1cb16213baf9a9671d02c5633c4cf07fa92026
|
4
|
+
data.tar.gz: 84f12098d7d3d638671cdbcf32e9e1f4e56ece8b64cc799f167b2eff336cc7dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 414294a360874650bedada98a2ccb4fb73fe8f2eb85d2c020387280dd84739af6807c703c016f4c7027c030ffb2f57cc4a1e493104a08ec79b599993b36f56c1
|
7
|
+
data.tar.gz: 8333b90a0265330c6f9f99d8826839003086816726f041e685916254de9cecd89db4ee2b9815975080b40cdd9be784ddd24d1f96322a8bba12c6971c2f91f3a6
|
@@ -27,10 +27,8 @@ module ActiveRecord
|
|
27
27
|
end
|
28
28
|
begin
|
29
29
|
validate_constraint(table, temp_constraint_name)
|
30
|
-
rescue
|
31
|
-
raise ActiveRecord::NotNullViolation.new(sql: e.sql, binds: e.binds)
|
32
|
-
|
33
|
-
raise
|
30
|
+
rescue PG::CheckViolation => e
|
31
|
+
raise ActiveRecord::NotNullViolation.new(sql: e.sql, binds: e.binds)
|
34
32
|
end
|
35
33
|
|
36
34
|
transaction do
|
@@ -66,7 +64,7 @@ module ActiveRecord
|
|
66
64
|
# will automatically remove a NOT VALID index before trying to add
|
67
65
|
def add_index(table_name, column_name, **options)
|
68
66
|
# catch a concurrent index add that fails because it already exists, and is invalid
|
69
|
-
if options[:algorithm] == :concurrently
|
67
|
+
if options[:algorithm] == :concurrently && options[:if_not_exists]
|
70
68
|
column_names = index_column_names(column_name)
|
71
69
|
index_name = options[:name].to_s if options.key?(:name)
|
72
70
|
index_name ||= index_name(table_name, column_names)
|
@@ -84,16 +82,26 @@ module ActiveRecord
|
|
84
82
|
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = #{index[:schema]} )
|
85
83
|
LIMIT 1
|
86
84
|
SQL
|
87
|
-
if valid ==
|
88
|
-
|
89
|
-
|
90
|
-
end
|
91
|
-
return if options[:if_not_exists] && valid == true
|
85
|
+
return if valid == true
|
86
|
+
|
87
|
+
remove_index(table_name, name: index_name, algorithm: :concurrently) if valid == false
|
92
88
|
end
|
93
|
-
# Rails.version: can stop doing this in Rails 6.2, when it's natively supported
|
94
|
-
options.delete(:if_not_exists)
|
95
89
|
super
|
96
90
|
end
|
91
|
+
|
92
|
+
def add_check_constraint(table_name, expression, if_not_exists: false, **options)
|
93
|
+
return if if_not_exists && check_constraint_for(table_name, expression, **options)
|
94
|
+
|
95
|
+
super
|
96
|
+
end
|
97
|
+
|
98
|
+
if ActiveRecord.version < Gem::Version.new("7.1")
|
99
|
+
def remove_check_constraint(table_name, expression = nil, if_exists: false, **options)
|
100
|
+
return if if_exists && !check_constraint_for(table_name, expression, **options)
|
101
|
+
|
102
|
+
super
|
103
|
+
end
|
104
|
+
end
|
97
105
|
end
|
98
106
|
end
|
99
107
|
end
|
@@ -9,8 +9,8 @@ module ActiveRecord
|
|
9
9
|
# set constraint check timing for the current transaction
|
10
10
|
# see https://www.postgresql.org/docs/current/sql-set-constraints.html
|
11
11
|
def set_constraints(deferred, *constraints)
|
12
|
-
raise ArgumentError, "deferred must be :deferred or :immediate" unless %
|
13
|
-
immediate].include?(deferred
|
12
|
+
raise ArgumentError, "deferred must be :deferred or :immediate" unless %i[deferred
|
13
|
+
immediate].include?(deferred)
|
14
14
|
|
15
15
|
constraints = constraints.map { |c| quote_table_name(c) }.join(", ")
|
16
16
|
constraints = "ALL" if constraints.empty?
|
@@ -160,7 +160,7 @@ module ActiveRecord
|
|
160
160
|
raise ArgumentError, "columns may only be specified if a analyze is specified" unless analyze
|
161
161
|
|
162
162
|
table.map do |table_name, columns|
|
163
|
-
"#{quote_table_name(table_name)} (#{Array.wrap(columns).map { |c| quote_column_name(c) }.join(
|
163
|
+
"#{quote_table_name(table_name)} (#{Array.wrap(columns).map { |c| quote_column_name(c) }.join(", ")})"
|
164
164
|
end.join(", ")
|
165
165
|
else
|
166
166
|
quote_table_name(table)
|
@@ -182,35 +182,35 @@ module ActiveRecord
|
|
182
182
|
def current_wal_lsn
|
183
183
|
return nil unless wal?
|
184
184
|
|
185
|
-
select_value("SELECT #{pre_pg10_wal_function_name(
|
185
|
+
select_value("SELECT #{pre_pg10_wal_function_name("pg_current_wal_lsn")}()")
|
186
186
|
end
|
187
187
|
|
188
188
|
# see https://www.postgresql.org/docs/current/functions-admin.html#id-1.5.8.33.5.5.2.2.2.1.1.1
|
189
189
|
def current_wal_flush_lsn
|
190
190
|
return nil unless wal?
|
191
191
|
|
192
|
-
select_value("SELECT #{pre_pg10_wal_function_name(
|
192
|
+
select_value("SELECT #{pre_pg10_wal_function_name("pg_current_wal_flush_lsn")}()")
|
193
193
|
end
|
194
194
|
|
195
195
|
# see https://www.postgresql.org/docs/current/functions-admin.html#id-1.5.8.33.5.5.2.2.3.1.1.1
|
196
196
|
def current_wal_insert_lsn
|
197
197
|
return nil unless wal?
|
198
198
|
|
199
|
-
select_value("SELECT #{pre_pg10_wal_function_name(
|
199
|
+
select_value("SELECT #{pre_pg10_wal_function_name("pg_current_wal_insert_lsn")}()")
|
200
200
|
end
|
201
201
|
|
202
202
|
# https://www.postgresql.org/docs/current/functions-admin.html#id-1.5.8.33.6.3.2.2.2.1.1.1
|
203
203
|
def last_wal_receive_lsn
|
204
204
|
return nil unless wal?
|
205
205
|
|
206
|
-
select_value("SELECT #{pre_pg10_wal_function_name(
|
206
|
+
select_value("SELECT #{pre_pg10_wal_function_name("pg_last_wal_receive_lsn")}()")
|
207
207
|
end
|
208
208
|
|
209
209
|
# see https://www.postgresql.org/docs/current/functions-admin.html#id-1.5.8.33.6.3.2.2.3.1.1.1
|
210
210
|
def last_wal_replay_lsn
|
211
211
|
return nil unless wal?
|
212
212
|
|
213
|
-
select_value("SELECT #{pre_pg10_wal_function_name(
|
213
|
+
select_value("SELECT #{pre_pg10_wal_function_name("pg_last_wal_replay_lsn")}()")
|
214
214
|
end
|
215
215
|
|
216
216
|
# see https://www.postgresql.org/docs/current/functions-admin.html#id-1.5.8.33.5.5.2.2.4.1.1.1
|
@@ -229,7 +229,7 @@ module ActiveRecord
|
|
229
229
|
end
|
230
230
|
end
|
231
231
|
|
232
|
-
select_value("SELECT #{pre_pg10_wal_function_name(
|
232
|
+
select_value("SELECT #{pre_pg10_wal_function_name("pg_wal_lsn_diff")}(#{lsns[0]}, #{lsns[1]})")
|
233
233
|
end
|
234
234
|
|
235
235
|
def in_recovery?
|
@@ -238,7 +238,7 @@ module ActiveRecord
|
|
238
238
|
|
239
239
|
def set(configuration_parameter, value, local: false)
|
240
240
|
value = value.nil? ? "DEFAULT" : quote(value)
|
241
|
-
execute("SET#{
|
241
|
+
execute("SET#{" LOCAL" if local} #{configuration_parameter} TO #{value}")
|
242
242
|
end
|
243
243
|
|
244
244
|
def reset(configuration_parameter)
|
@@ -274,65 +274,8 @@ module ActiveRecord
|
|
274
274
|
end
|
275
275
|
end
|
276
276
|
|
277
|
-
unless ::Rails.version >= "6.1"
|
278
|
-
def add_check_constraint(table_name, expression, name:, validate: true)
|
279
|
-
sql = +"ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{quote_column_name(name)} CHECK (#{expression})" # rubocop:disable Layout/LineLength
|
280
|
-
sql << " NOT VALID" unless validate
|
281
|
-
execute(sql)
|
282
|
-
end
|
283
|
-
|
284
|
-
def remove_check_constraint(table_name, name:)
|
285
|
-
execute("ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{quote_column_name(name)}")
|
286
|
-
end
|
287
|
-
end
|
288
|
-
|
289
277
|
private
|
290
278
|
|
291
|
-
if ::Rails.version < "6.1"
|
292
|
-
# significant change: add PG::TextDecoder::Numeric
|
293
|
-
def add_pg_decoders
|
294
|
-
@default_timezone = nil
|
295
|
-
@timestamp_decoder = nil
|
296
|
-
|
297
|
-
coders_by_name = {
|
298
|
-
"int2" => PG::TextDecoder::Integer,
|
299
|
-
"int4" => PG::TextDecoder::Integer,
|
300
|
-
"int8" => PG::TextDecoder::Integer,
|
301
|
-
"oid" => PG::TextDecoder::Integer,
|
302
|
-
"float4" => PG::TextDecoder::Float,
|
303
|
-
"float8" => PG::TextDecoder::Float,
|
304
|
-
"bool" => PG::TextDecoder::Boolean,
|
305
|
-
"numeric" => PG::TextDecoder::Numeric
|
306
|
-
}
|
307
|
-
|
308
|
-
if defined?(PG::TextDecoder::TimestampUtc)
|
309
|
-
# Use native PG encoders available since pg-1.1
|
310
|
-
coders_by_name["timestamp"] = PG::TextDecoder::TimestampUtc
|
311
|
-
coders_by_name["timestamptz"] = PG::TextDecoder::TimestampWithTimeZone
|
312
|
-
end
|
313
|
-
|
314
|
-
known_coder_types = coders_by_name.keys.map { |n| quote(n) }
|
315
|
-
query = format(<<~SQL, *known_coder_types.join(", "))
|
316
|
-
SELECT t.oid, t.typname
|
317
|
-
FROM pg_type as t
|
318
|
-
WHERE t.typname IN (%s)
|
319
|
-
SQL
|
320
|
-
coders = execute_and_clear(query, "SCHEMA", []) do |result|
|
321
|
-
result
|
322
|
-
.map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
|
323
|
-
.compact
|
324
|
-
end
|
325
|
-
|
326
|
-
map = PG::TypeMapByOid.new
|
327
|
-
coders.each { |coder| map.add_coder(coder) }
|
328
|
-
@connection.type_map_for_results = map
|
329
|
-
|
330
|
-
# extract timestamp decoder for use in update_typemap_for_default_timezone
|
331
|
-
@timestamp_decoder = coders.find { |coder| coder.name == "timestamp" }
|
332
|
-
update_typemap_for_default_timezone
|
333
|
-
end
|
334
|
-
end
|
335
|
-
|
336
279
|
def initialize_type_map(map = type_map)
|
337
280
|
map.register_type "pg_lsn", ActiveRecord::ConnectionAdapters::PostgreSQL::OID::SpecializedString.new(:pg_lsn)
|
338
281
|
|
metadata
CHANGED
@@ -1,55 +1,43 @@
|
|
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.5.1
|
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: 2023-09-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '6.0'
|
20
|
-
- - "<"
|
17
|
+
- - "~>"
|
21
18
|
- !ruby/object:Gem::Version
|
22
|
-
version:
|
19
|
+
version: 7.0.0
|
23
20
|
type: :runtime
|
24
21
|
prerelease: false
|
25
22
|
version_requirements: !ruby/object:Gem::Requirement
|
26
23
|
requirements:
|
27
|
-
- - "
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: '6.0'
|
30
|
-
- - "<"
|
24
|
+
- - "~>"
|
31
25
|
- !ruby/object:Gem::Version
|
32
|
-
version:
|
26
|
+
version: 7.0.0
|
33
27
|
- !ruby/object:Gem::Dependency
|
34
28
|
name: railties
|
35
29
|
requirement: !ruby/object:Gem::Requirement
|
36
30
|
requirements:
|
37
|
-
- - "
|
38
|
-
- !ruby/object:Gem::Version
|
39
|
-
version: '6.0'
|
40
|
-
- - "<"
|
31
|
+
- - "~>"
|
41
32
|
- !ruby/object:Gem::Version
|
42
|
-
version:
|
33
|
+
version: 7.0.0
|
43
34
|
type: :runtime
|
44
35
|
prerelease: false
|
45
36
|
version_requirements: !ruby/object:Gem::Requirement
|
46
37
|
requirements:
|
47
|
-
- - "
|
48
|
-
- !ruby/object:Gem::Version
|
49
|
-
version: '6.0'
|
50
|
-
- - "<"
|
38
|
+
- - "~>"
|
51
39
|
- !ruby/object:Gem::Version
|
52
|
-
version:
|
40
|
+
version: 7.0.0
|
53
41
|
- !ruby/object:Gem::Dependency
|
54
42
|
name: appraisal
|
55
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -121,19 +109,19 @@ dependencies:
|
|
121
109
|
- !ruby/object:Gem::Version
|
122
110
|
version: '3.0'
|
123
111
|
- !ruby/object:Gem::Dependency
|
124
|
-
name: rubocop
|
112
|
+
name: rubocop-inst
|
125
113
|
requirement: !ruby/object:Gem::Requirement
|
126
114
|
requirements:
|
127
115
|
- - "~>"
|
128
116
|
- !ruby/object:Gem::Version
|
129
|
-
version: '1
|
117
|
+
version: '1'
|
130
118
|
type: :development
|
131
119
|
prerelease: false
|
132
120
|
version_requirements: !ruby/object:Gem::Requirement
|
133
121
|
requirements:
|
134
122
|
- - "~>"
|
135
123
|
- !ruby/object:Gem::Version
|
136
|
-
version: '1
|
124
|
+
version: '1'
|
137
125
|
- !ruby/object:Gem::Dependency
|
138
126
|
name: rubocop-rake
|
139
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -172,7 +160,6 @@ files:
|
|
172
160
|
- CHANGELOG.md
|
173
161
|
- LICENSE.txt
|
174
162
|
- README.md
|
175
|
-
- config/database.yml
|
176
163
|
- lib/active_record/pg_extensions.rb
|
177
164
|
- lib/active_record/pg_extensions/all.rb
|
178
165
|
- lib/active_record/pg_extensions/errors.rb
|
@@ -183,9 +170,6 @@ files:
|
|
183
170
|
- lib/active_record/pg_extensions/transaction.rb
|
184
171
|
- lib/active_record/pg_extensions/version.rb
|
185
172
|
- lib/activerecord-pg-extensions.rb
|
186
|
-
- spec/pessimistic_migrations_spec.rb
|
187
|
-
- spec/postgresql_adapter_spec.rb
|
188
|
-
- spec/spec_helper.rb
|
189
173
|
homepage: https://github.com/instructure/activerecord-pg-extensions
|
190
174
|
licenses:
|
191
175
|
- MIT
|
@@ -200,19 +184,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
200
184
|
requirements:
|
201
185
|
- - ">="
|
202
186
|
- !ruby/object:Gem::Version
|
203
|
-
version: 2.
|
187
|
+
version: '2.7'
|
204
188
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
205
189
|
requirements:
|
206
190
|
- - ">="
|
207
191
|
- !ruby/object:Gem::Version
|
208
192
|
version: '0'
|
209
193
|
requirements: []
|
210
|
-
rubygems_version: 3.
|
194
|
+
rubygems_version: 3.4.19
|
211
195
|
signing_key:
|
212
196
|
specification_version: 4
|
213
197
|
summary: Several extensions to ActiveRecord's PostgreSQLAdapter.
|
214
|
-
test_files:
|
215
|
-
- spec/spec_helper.rb
|
216
|
-
- spec/postgresql_adapter_spec.rb
|
217
|
-
- spec/pessimistic_migrations_spec.rb
|
218
|
-
- config/database.yml
|
198
|
+
test_files: []
|
data/config/database.yml
DELETED
@@ -1,136 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
describe ActiveRecord::PGExtensions::PessimisticMigrations do
|
4
|
-
around do |example|
|
5
|
-
connection.dont_execute(&example)
|
6
|
-
end
|
7
|
-
|
8
|
-
describe "#change_column_null" do
|
9
|
-
# Rails 6.1 doesn't quote the constraint name when adding a check constraint??
|
10
|
-
def quote_constraint_name(name)
|
11
|
-
Rails.version >= "6.1" ? name : connection.quote_column_name(name)
|
12
|
-
end
|
13
|
-
|
14
|
-
it "does nothing extra when changing a column to nullable" do
|
15
|
-
connection.change_column_null(:table, :column, true)
|
16
|
-
expect(connection.executed_statements).to eq ['ALTER TABLE "table" ALTER COLUMN "column" DROP NOT NULL']
|
17
|
-
end
|
18
|
-
|
19
|
-
it "does nothing if we're in a transaction" do
|
20
|
-
connection.transaction do
|
21
|
-
connection.change_column_null(:table, :column, true)
|
22
|
-
end
|
23
|
-
expect(connection.executed_statements).to eq ["BEGIN",
|
24
|
-
'ALTER TABLE "table" ALTER COLUMN "column" DROP NOT NULL',
|
25
|
-
"COMMIT"]
|
26
|
-
end
|
27
|
-
|
28
|
-
it "skips entirely if the column is already NOT NULL" do
|
29
|
-
expect(connection).to receive(:columns).with(:table).and_return([double(name: "column", null: false)])
|
30
|
-
connection.change_column_null(:table, :column, false)
|
31
|
-
expect(connection.executed_statements).to eq([])
|
32
|
-
end
|
33
|
-
|
34
|
-
it "adds and removes a check constraint" do
|
35
|
-
expect(connection).to receive(:columns).and_return([])
|
36
|
-
allow(connection).to receive(:check_constraint_for!).and_return(double(name: "chk_rails_table_column_not_null"))
|
37
|
-
connection.change_column_null(:table, :column, false)
|
38
|
-
|
39
|
-
expect(connection.executed_statements).to eq [
|
40
|
-
"SELECT convalidated FROM pg_constraint INNER JOIN pg_namespace ON pg_namespace.oid=connamespace WHERE conname='chk_rails_table_column_not_null' AND nspname=ANY (current_schemas(false))\n", # rubocop:disable Layout/LineLength
|
41
|
-
%{ALTER TABLE "table" ADD CONSTRAINT #{quote_constraint_name('chk_rails_table_column_not_null')} CHECK ("column" IS NOT NULL) NOT VALID}, # rubocop:disable Layout/LineLength
|
42
|
-
'ALTER TABLE "table" VALIDATE CONSTRAINT "chk_rails_table_column_not_null"',
|
43
|
-
"BEGIN",
|
44
|
-
'ALTER TABLE "table" ALTER COLUMN "column" SET NOT NULL',
|
45
|
-
'ALTER TABLE "table" DROP CONSTRAINT "chk_rails_table_column_not_null"',
|
46
|
-
"COMMIT"
|
47
|
-
]
|
48
|
-
end
|
49
|
-
|
50
|
-
it "verifies an existing check constraint" do
|
51
|
-
expect(connection).to receive(:columns).and_return([])
|
52
|
-
allow(connection).to receive(:check_constraint_for!).and_return(double(name: "chk_rails_table_column_not_null"))
|
53
|
-
expect(connection).to receive(:select_value).and_return(false)
|
54
|
-
connection.change_column_null(:table, :column, false)
|
55
|
-
|
56
|
-
expect(connection.executed_statements).to eq [
|
57
|
-
# stubbed out <check constraint valid>
|
58
|
-
'ALTER TABLE "table" VALIDATE CONSTRAINT "chk_rails_table_column_not_null"',
|
59
|
-
"BEGIN",
|
60
|
-
'ALTER TABLE "table" ALTER COLUMN "column" SET NOT NULL',
|
61
|
-
'ALTER TABLE "table" DROP CONSTRAINT "chk_rails_table_column_not_null"',
|
62
|
-
"COMMIT"
|
63
|
-
]
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
describe "#add_foreign_key" do
|
68
|
-
it "does nothing extra if a transaction is already active" do
|
69
|
-
connection.transaction do
|
70
|
-
connection.add_foreign_key :emails, :users, delay_validation: true
|
71
|
-
end
|
72
|
-
expect(connection.executed_statements).to match(
|
73
|
-
["BEGIN",
|
74
|
-
match(/\AALTER TABLE "emails" ADD CONSTRAINT "fk_rails_[0-9a-f]+".+REFERENCES "users" \("id"\)\s*\z/m),
|
75
|
-
"COMMIT"]
|
76
|
-
)
|
77
|
-
end
|
78
|
-
|
79
|
-
it "delays validation" do
|
80
|
-
connection.add_foreign_key :emails, :users, delay_validation: true
|
81
|
-
expect(connection.executed_statements).to match(
|
82
|
-
[/convalidated/,
|
83
|
-
match(/\AALTER TABLE "emails" ADD CONSTRAINT "[a-z0-9_]+".+REFERENCES "users" \("id"\)\s+NOT VALID\z/m),
|
84
|
-
match(/^ALTER TABLE "emails" VALIDATE CONSTRAINT "fk_rails_[0-9a-f]+"/)]
|
85
|
-
)
|
86
|
-
end
|
87
|
-
|
88
|
-
it "only validates if the constraint already exists, and is not valid" do
|
89
|
-
expect(connection).to receive(:select_value).with(/convalidated/, "SCHEMA").and_return(false)
|
90
|
-
connection.add_foreign_key :emails, :users, delay_validation: true
|
91
|
-
expect(connection.executed_statements).to match(
|
92
|
-
[match(/^ALTER TABLE "emails" VALIDATE CONSTRAINT "fk_rails_[0-9a-f]+"/)]
|
93
|
-
)
|
94
|
-
end
|
95
|
-
|
96
|
-
it "does nothing if constraint already exists" do
|
97
|
-
expect(connection).to receive(:select_value).with(/convalidated/, "SCHEMA").and_return(true)
|
98
|
-
connection.add_foreign_key :emails, :users, if_not_exists: true
|
99
|
-
expect(connection.executed_statements).to eq []
|
100
|
-
end
|
101
|
-
|
102
|
-
it "still tries if delay_validation is true but if_not_exists is false and it already exists" do
|
103
|
-
expect(connection).to receive(:select_value).with(/convalidated/, "SCHEMA").and_return(true)
|
104
|
-
connection.add_foreign_key :emails, :users, delay_validation: true
|
105
|
-
expect(connection.executed_statements).to match(
|
106
|
-
[match(/\AALTER TABLE "emails" ADD CONSTRAINT "[a-z0-9_]+".+REFERENCES "users" \("id"\)\s+NOT VALID\z/m),
|
107
|
-
match(/^ALTER TABLE "emails" VALIDATE CONSTRAINT "fk_rails_[0-9a-f]+"/)]
|
108
|
-
)
|
109
|
-
end
|
110
|
-
|
111
|
-
it "does nothing if_not_exists is true and it is NOT VALID" do
|
112
|
-
expect(connection).to receive(:select_value).with(/convalidated/, "SCHEMA").and_return(false)
|
113
|
-
connection.add_foreign_key :emails, :users, if_not_exists: true
|
114
|
-
expect(connection.executed_statements).to eq []
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
describe "#add_index" do
|
119
|
-
it "removes a NOT VALID index before re-adding" do
|
120
|
-
expect(connection).to receive(:select_value).with(/indisvalid/, "SCHEMA").and_return(false)
|
121
|
-
expect(connection).to receive(:remove_index).with(:users, name: "index_users_on_name", algorithm: :concurrently)
|
122
|
-
|
123
|
-
connection.add_index :users, :name, algorithm: :concurrently
|
124
|
-
expect(connection.executed_statements).to match(
|
125
|
-
[match(/\ACREATE +INDEX CONCURRENTLY "index_users_on_name" ON "users" +\("name"\)\z/)]
|
126
|
-
)
|
127
|
-
end
|
128
|
-
|
129
|
-
it "does nothing if the index already exists" do
|
130
|
-
expect(connection).to receive(:select_value).with(/indisvalid/, "SCHEMA").and_return(true)
|
131
|
-
|
132
|
-
connection.add_index :users, :name, if_not_exists: true
|
133
|
-
expect(connection.executed_statements).to eq []
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
@@ -1,457 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
describe ActiveRecord::ConnectionAdapters::PostgreSQLAdapter do
|
4
|
-
before do
|
5
|
-
allow(connection).to receive(:reload_type_map)
|
6
|
-
end
|
7
|
-
|
8
|
-
describe "#set_constraints" do
|
9
|
-
around do |example|
|
10
|
-
connection.dont_execute(&example)
|
11
|
-
end
|
12
|
-
|
13
|
-
it "requires :deferred or :immediate" do
|
14
|
-
expect { connection.set_constraints("garbage") }.to raise_error(ArgumentError)
|
15
|
-
end
|
16
|
-
|
17
|
-
it "defaults to all" do
|
18
|
-
connection.set_constraints(:deferred)
|
19
|
-
expect(connection.executed_statements).to eq ["SET CONSTRAINTS ALL DEFERRED"]
|
20
|
-
end
|
21
|
-
|
22
|
-
it "quotes constraints" do
|
23
|
-
connection.set_constraints(:deferred, :my_fk)
|
24
|
-
expect(connection.executed_statements).to eq ['SET CONSTRAINTS "my_fk" DEFERRED']
|
25
|
-
end
|
26
|
-
|
27
|
-
it "quotes multiple constraints" do
|
28
|
-
connection.set_constraints(:deferred, :my_fk1, :my_fk2)
|
29
|
-
expect(connection.executed_statements).to eq ['SET CONSTRAINTS "my_fk1", "my_fk2" DEFERRED']
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
describe "#defer_constraints" do
|
34
|
-
around do |example|
|
35
|
-
connection.dont_execute(&example)
|
36
|
-
end
|
37
|
-
|
38
|
-
it "defers and resets" do
|
39
|
-
block_called = false
|
40
|
-
connection.defer_constraints do
|
41
|
-
block_called = true
|
42
|
-
end
|
43
|
-
expect(block_called).to eq true
|
44
|
-
expect(connection.executed_statements).to eq ["SET CONSTRAINTS ALL DEFERRED", "SET CONSTRAINTS ALL IMMEDIATE"]
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
describe "#set_replica_identity" do
|
49
|
-
around do |example|
|
50
|
-
connection.dont_execute(&example)
|
51
|
-
end
|
52
|
-
|
53
|
-
it "resets identity" do
|
54
|
-
connection.set_replica_identity(:my_table)
|
55
|
-
expect(connection.executed_statements).to eq ['ALTER TABLE "my_table" REPLICA IDENTITY DEFAULT']
|
56
|
-
end
|
57
|
-
|
58
|
-
it "sets full identity" do
|
59
|
-
connection.set_replica_identity(:my_table, :full)
|
60
|
-
expect(connection.executed_statements).to eq ['ALTER TABLE "my_table" REPLICA IDENTITY FULL']
|
61
|
-
end
|
62
|
-
|
63
|
-
it "sets an index" do
|
64
|
-
connection.set_replica_identity(:my_table, :my_index)
|
65
|
-
expect(connection.executed_statements).to eq ['ALTER TABLE "my_table" REPLICA IDENTITY USING INDEX "my_index"']
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
context "extensions" do
|
70
|
-
it "creates and drops an extension" do
|
71
|
-
connection.create_extension(:pg_trgm, schema: "public")
|
72
|
-
expect(connection.executed_statements).to eq ["CREATE EXTENSION pg_trgm SCHEMA public"]
|
73
|
-
expect(ext = connection.extension(:pg_trgm)).not_to be_nil
|
74
|
-
expect(ext.schema).to eq "public"
|
75
|
-
expect(ext.version).not_to be_nil
|
76
|
-
expect(ext.name).to eq "pg_trgm"
|
77
|
-
ensure
|
78
|
-
connection.executed_statements.clear
|
79
|
-
connection.drop_extension(:pg_trgm, if_exists: true)
|
80
|
-
expect(connection.executed_statements).to eq ["DROP EXTENSION IF EXISTS pg_trgm"]
|
81
|
-
expect(connection.extension(:pg_trgm)).to be_nil
|
82
|
-
end
|
83
|
-
|
84
|
-
it "doesn't try to recreate" do
|
85
|
-
connection.create_extension(:pg_trgm, schema: "public")
|
86
|
-
connection.create_extension(:pg_trgm, schema: "public", if_not_exists: true)
|
87
|
-
expect(connection.executed_statements).to eq ["CREATE EXTENSION pg_trgm SCHEMA public",
|
88
|
-
"CREATE EXTENSION IF NOT EXISTS pg_trgm SCHEMA public"]
|
89
|
-
ensure
|
90
|
-
connection.drop_extension(:pg_trgm, if_exists: true)
|
91
|
-
end
|
92
|
-
|
93
|
-
context "non-executing" do
|
94
|
-
around do |example|
|
95
|
-
connection.dont_execute(&example)
|
96
|
-
end
|
97
|
-
|
98
|
-
it "supports all options on create" do
|
99
|
-
connection.create_extension(:my_extension, if_not_exists: true, schema: "public", version: "2.0", cascade: true)
|
100
|
-
expect(connection.executed_statements).to eq(
|
101
|
-
["CREATE EXTENSION IF NOT EXISTS my_extension SCHEMA public VERSION '2.0' CASCADE"]
|
102
|
-
)
|
103
|
-
end
|
104
|
-
|
105
|
-
it "supports all options on drop" do
|
106
|
-
connection.drop_extension(:my_extension, if_exists: true, cascade: true)
|
107
|
-
expect(connection.executed_statements).to eq ["DROP EXTENSION IF EXISTS my_extension CASCADE"]
|
108
|
-
end
|
109
|
-
|
110
|
-
it "can update an extensions" do
|
111
|
-
connection.alter_extension(:my_extension, version: true)
|
112
|
-
expect(connection.executed_statements).to eq ["ALTER EXTENSION my_extension UPDATE"]
|
113
|
-
end
|
114
|
-
|
115
|
-
it "can update to a specific version" do
|
116
|
-
connection.alter_extension(:my_extension, version: "2.0")
|
117
|
-
expect(connection.executed_statements).to eq ["ALTER EXTENSION my_extension UPDATE TO '2.0'"]
|
118
|
-
end
|
119
|
-
|
120
|
-
it "can change schemas" do
|
121
|
-
connection.alter_extension(:my_extension, schema: "my_app")
|
122
|
-
expect(connection.executed_statements).to eq ["ALTER EXTENSION my_extension SET SCHEMA my_app"]
|
123
|
-
end
|
124
|
-
|
125
|
-
it "cannot change schema and update" do
|
126
|
-
expect { connection.alter_extension(:my_extension, schema: "my_app", version: "2.0") }
|
127
|
-
.to raise_error(ArgumentError)
|
128
|
-
end
|
129
|
-
|
130
|
-
it "can drop multiple extensions" do
|
131
|
-
connection.drop_extension(:my_extension1, :my_extension2)
|
132
|
-
expect(connection.executed_statements).to eq ["DROP EXTENSION my_extension1, my_extension2"]
|
133
|
-
end
|
134
|
-
|
135
|
-
it "does not allow dropping no extensions" do
|
136
|
-
expect { connection.drop_extension }.to raise_error(ArgumentError)
|
137
|
-
end
|
138
|
-
|
139
|
-
describe "#extension_available?" do
|
140
|
-
it "works with no version constraint" do
|
141
|
-
connection.extension_available?(:postgis)
|
142
|
-
expect(connection.executed_statements).to eq ["SELECT 1 FROM pg_available_extensions WHERE name='postgis'"]
|
143
|
-
end
|
144
|
-
|
145
|
-
it "works with a version constraint" do
|
146
|
-
connection.extension_available?(:postgis, "2.0")
|
147
|
-
expect(connection.executed_statements).to eq(
|
148
|
-
["SELECT 1 FROM pg_available_extension_versions WHERE name='postgis' AND version='2.0'"]
|
149
|
-
)
|
150
|
-
end
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
describe "#add_schema_to_search_path" do
|
156
|
-
around do |example|
|
157
|
-
original_search_path = connection.schema_search_path
|
158
|
-
connection.schema_search_path = "public"
|
159
|
-
example.call
|
160
|
-
ensure
|
161
|
-
connection.schema_search_path = original_search_path
|
162
|
-
end
|
163
|
-
|
164
|
-
it "adds a schema to search path" do
|
165
|
-
connection.add_schema_to_search_path("postgis") do
|
166
|
-
expect(connection.schema_search_path).to eq "public,postgis"
|
167
|
-
end
|
168
|
-
expect(connection.schema_search_path).to eq "public"
|
169
|
-
end
|
170
|
-
|
171
|
-
it "doesn't duplicate an existing schema" do
|
172
|
-
connection.add_schema_to_search_path("public") do
|
173
|
-
expect(connection.schema_search_path).to eq "public"
|
174
|
-
end
|
175
|
-
expect(connection.schema_search_path).to eq "public"
|
176
|
-
end
|
177
|
-
|
178
|
-
it "is cleaned up properly when the transaction rolls back manually" do
|
179
|
-
expect do
|
180
|
-
connection.add_schema_to_search_path("postgis") do
|
181
|
-
raise ActiveRecord::Rollback
|
182
|
-
end
|
183
|
-
end.to raise_error(ActiveRecord::Rollback)
|
184
|
-
expect(connection.schema_search_path).to eq "public"
|
185
|
-
end
|
186
|
-
|
187
|
-
it "is cleaned up properly when the transaction rolls back" do
|
188
|
-
expect do
|
189
|
-
connection.add_schema_to_search_path("postgis") do
|
190
|
-
connection.execute("gibberish")
|
191
|
-
end
|
192
|
-
end.to raise_error(ActiveRecord::StatementInvalid)
|
193
|
-
expect(connection.schema_search_path).to eq "public"
|
194
|
-
end
|
195
|
-
end
|
196
|
-
|
197
|
-
describe "#vacuum" do
|
198
|
-
it "does a straight vacuum of everything" do
|
199
|
-
connection.vacuum
|
200
|
-
expect(connection.executed_statements).to eq ["VACUUM"]
|
201
|
-
end
|
202
|
-
|
203
|
-
it "supports several options" do
|
204
|
-
connection.vacuum(analyze: true, verbose: true)
|
205
|
-
expect(connection.executed_statements).to eq ["VACUUM VERBOSE ANALYZE"]
|
206
|
-
end
|
207
|
-
|
208
|
-
it "validates parallel option is an integer" do
|
209
|
-
expect { connection.vacuum(parallel: :garbage) }.to raise_error(ArgumentError)
|
210
|
-
end
|
211
|
-
|
212
|
-
it "validates parallel option is postive" do
|
213
|
-
expect { connection.vacuum(parallel: -1) }.to raise_error(ArgumentError)
|
214
|
-
end
|
215
|
-
|
216
|
-
context "non-executing" do
|
217
|
-
around do |example|
|
218
|
-
connection.dont_execute(&example)
|
219
|
-
end
|
220
|
-
|
221
|
-
it "vacuums a table" do
|
222
|
-
connection.vacuum(:my_table)
|
223
|
-
expect(connection.executed_statements).to eq ['VACUUM "my_table"']
|
224
|
-
end
|
225
|
-
|
226
|
-
it "vacuums multiple tables" do
|
227
|
-
connection.vacuum(:table1, :table2)
|
228
|
-
expect(connection.executed_statements).to eq ['VACUUM "table1", "table2"']
|
229
|
-
end
|
230
|
-
|
231
|
-
it "requires analyze with specific columns" do
|
232
|
-
expect { connection.vacuum({ my_table: :column1 }) }.to raise_error(ArgumentError)
|
233
|
-
end
|
234
|
-
|
235
|
-
it "analyzes a specific column" do
|
236
|
-
connection.vacuum({ my_table: :column }, analyze: true)
|
237
|
-
expect(connection.executed_statements).to eq ['VACUUM ANALYZE "my_table" ("column")']
|
238
|
-
end
|
239
|
-
|
240
|
-
it "analyzes multiples columns" do
|
241
|
-
connection.vacuum({ my_table: %i[column1 column2] }, analyze: true)
|
242
|
-
expect(connection.executed_statements).to eq ['VACUUM ANALYZE "my_table" ("column1", "column2")']
|
243
|
-
end
|
244
|
-
|
245
|
-
it "analyzes a mixture of tables and columns" do
|
246
|
-
connection.vacuum(:table1, { my_table: %i[column1 column2] }, analyze: true)
|
247
|
-
expect(connection.executed_statements).to eq ['VACUUM ANALYZE "table1", "my_table" ("column1", "column2")']
|
248
|
-
end
|
249
|
-
end
|
250
|
-
|
251
|
-
describe "#wal_lsn_diff" do
|
252
|
-
skip unless connection.wal?
|
253
|
-
|
254
|
-
it "executes" do
|
255
|
-
expect(connection.wal_lsn_diff(:current, :current)).to eq 0
|
256
|
-
end
|
257
|
-
end
|
258
|
-
|
259
|
-
describe "#in_recovery?" do
|
260
|
-
it "works" do
|
261
|
-
expect(connection.in_recovery?).to eq false
|
262
|
-
end
|
263
|
-
end
|
264
|
-
|
265
|
-
describe "#select_value" do
|
266
|
-
it "casts numeric types" do
|
267
|
-
expect(connection.select_value("SELECT factorial(2)")).to eq 2
|
268
|
-
end
|
269
|
-
end
|
270
|
-
end
|
271
|
-
|
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
|
-
|
281
|
-
it "stops long-running queries" do
|
282
|
-
expect do
|
283
|
-
connection.with_statement_timeout(0.01) do
|
284
|
-
connection.execute("SELECT pg_sleep(3)")
|
285
|
-
end
|
286
|
-
end.to raise_error(ActiveRecord::QueryCanceled)
|
287
|
-
end
|
288
|
-
|
289
|
-
it "re-raises other errors" do
|
290
|
-
expect do
|
291
|
-
connection.with_statement_timeout(1) do
|
292
|
-
connection.execute("bad sql")
|
293
|
-
end
|
294
|
-
end.to(raise_error { |e| expect(e.cause).to be_a(PG::SyntaxError) })
|
295
|
-
end
|
296
|
-
|
297
|
-
context "without executing" do
|
298
|
-
around do |example|
|
299
|
-
connection.dont_execute(&example)
|
300
|
-
end
|
301
|
-
|
302
|
-
it "converts integer to ms" do
|
303
|
-
connection.with_statement_timeout(30) { nil }
|
304
|
-
expect(connection.executed_statements).to eq(
|
305
|
-
[
|
306
|
-
"BEGIN",
|
307
|
-
"SET LOCAL statement_timeout TO '30000ms'",
|
308
|
-
"COMMIT"
|
309
|
-
]
|
310
|
-
)
|
311
|
-
end
|
312
|
-
|
313
|
-
it "converts float to ms" do
|
314
|
-
connection.with_statement_timeout(5.5) { nil }
|
315
|
-
expect(connection.executed_statements).to eq(
|
316
|
-
[
|
317
|
-
"BEGIN",
|
318
|
-
"SET LOCAL statement_timeout TO '5500ms'",
|
319
|
-
"COMMIT"
|
320
|
-
]
|
321
|
-
)
|
322
|
-
end
|
323
|
-
|
324
|
-
it "converts ActiveSupport::Duration to ms" do
|
325
|
-
connection.with_statement_timeout(5.seconds) { nil }
|
326
|
-
expect(connection.executed_statements).to eq(
|
327
|
-
[
|
328
|
-
"BEGIN",
|
329
|
-
"SET LOCAL statement_timeout TO '5000ms'",
|
330
|
-
"COMMIT"
|
331
|
-
]
|
332
|
-
)
|
333
|
-
end
|
334
|
-
|
335
|
-
it "allows true" do
|
336
|
-
connection.with_statement_timeout(true) { nil }
|
337
|
-
expect(connection.executed_statements).to eq(
|
338
|
-
[
|
339
|
-
"BEGIN",
|
340
|
-
"SET LOCAL statement_timeout TO '30000ms'",
|
341
|
-
"COMMIT"
|
342
|
-
]
|
343
|
-
)
|
344
|
-
end
|
345
|
-
end
|
346
|
-
end
|
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
|
-
|
423
|
-
unless Rails.version >= "6.1"
|
424
|
-
describe "#add_check_constraint" do
|
425
|
-
around do |example|
|
426
|
-
connection.dont_execute(&example)
|
427
|
-
end
|
428
|
-
|
429
|
-
it "works" do
|
430
|
-
connection.add_check_constraint(:table, "column IS NOT NULL", name: :my_constraint)
|
431
|
-
expect(connection.executed_statements).to eq(
|
432
|
-
['ALTER TABLE "table" ADD CONSTRAINT "my_constraint" CHECK (column IS NOT NULL)']
|
433
|
-
)
|
434
|
-
end
|
435
|
-
|
436
|
-
it "defers validation" do
|
437
|
-
connection.add_check_constraint(:table, "column IS NOT NULL", name: :my_constraint, validate: false)
|
438
|
-
expect(connection.executed_statements).to eq(
|
439
|
-
['ALTER TABLE "table" ADD CONSTRAINT "my_constraint" CHECK (column IS NOT NULL) NOT VALID']
|
440
|
-
)
|
441
|
-
end
|
442
|
-
end
|
443
|
-
|
444
|
-
describe "#remove_check_constraint" do
|
445
|
-
around do |example|
|
446
|
-
connection.dont_execute(&example)
|
447
|
-
end
|
448
|
-
|
449
|
-
it "works" do
|
450
|
-
connection.remove_check_constraint(:table, name: :my_constraint)
|
451
|
-
expect(connection.executed_statements).to eq(
|
452
|
-
['ALTER TABLE "table" DROP CONSTRAINT "my_constraint"']
|
453
|
-
)
|
454
|
-
end
|
455
|
-
end
|
456
|
-
end
|
457
|
-
end
|
data/spec/spec_helper.rb
DELETED
@@ -1,62 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "activerecord-pg-extensions"
|
4
|
-
require "byebug"
|
5
|
-
require "active_record/railtie"
|
6
|
-
require "active_record/pg_extensions/all"
|
7
|
-
|
8
|
-
ActiveRecord::Base # rubocop:disable Lint/Void
|
9
|
-
Rails.env = "test"
|
10
|
-
|
11
|
-
class Application < Rails::Application
|
12
|
-
config.eager_load = false
|
13
|
-
end
|
14
|
-
Application.initialize!
|
15
|
-
|
16
|
-
ActiveRecord::Tasks::DatabaseTasks.create_all
|
17
|
-
|
18
|
-
module StatementCaptureConnection
|
19
|
-
def dont_execute
|
20
|
-
@dont_execute = true
|
21
|
-
yield
|
22
|
-
ensure
|
23
|
-
@dont_execute = false
|
24
|
-
end
|
25
|
-
|
26
|
-
def executed_statements
|
27
|
-
@executed_statements ||= []
|
28
|
-
end
|
29
|
-
|
30
|
-
%w[execute exec_no_cache exec_cache].each do |method|
|
31
|
-
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
32
|
-
def #{method}(statement, *#{Rails.version >= '7.0' ? ', **kwargs' : ''})
|
33
|
-
materialize_transactions # this still needs to get called, even if we skip actually executing
|
34
|
-
executed_statements << statement
|
35
|
-
return empty_pg_result if @dont_execute
|
36
|
-
|
37
|
-
super
|
38
|
-
end
|
39
|
-
RUBY
|
40
|
-
end
|
41
|
-
|
42
|
-
# we can't actually generate a dummy one of these, so we just query the db with something
|
43
|
-
# that won't return anything
|
44
|
-
def empty_pg_result
|
45
|
-
@connection.async_exec("SELECT 0 WHERE FALSE")
|
46
|
-
end
|
47
|
-
end
|
48
|
-
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(StatementCaptureConnection)
|
49
|
-
|
50
|
-
RSpec.configure do |config|
|
51
|
-
config.expect_with :rspec do |c|
|
52
|
-
c.syntax = :expect
|
53
|
-
end
|
54
|
-
|
55
|
-
def connection
|
56
|
-
ActiveRecord::Base.connection
|
57
|
-
end
|
58
|
-
|
59
|
-
config.before do
|
60
|
-
connection.executed_statements.clear
|
61
|
-
end
|
62
|
-
end
|