13
13
14
14
15
15
class TestCacheInvalidation (tb .ConnectedTestCase ):
16
+
17
+ def _get_cached_statements (self , connection = None ):
18
+ if connection is None :
19
+ connection = self .con
20
+ return list (connection ._stmt_cache .iter_statements ())
21
+
22
+ def _check_statements_are_not_closed (self , statements ):
23
+ self .assertGreater (len (statements ), 0 )
24
+ self .assertTrue (all (not s .closed for s in statements ))
25
+
26
+ def _check_statements_are_closed (self , statements ):
27
+ self .assertGreater (len (statements ), 0 )
28
+ self .assertTrue (all (s .closed for s in statements ))
29
+
16
30
async def test_prepare_cache_invalidation_silent (self ):
17
31
await self .con .execute ('CREATE TABLE tab1(a int, b int)' )
18
32
@@ -21,11 +35,16 @@ async def test_prepare_cache_invalidation_silent(self):
21
35
result = await self .con .fetchrow ('SELECT * FROM tab1' )
22
36
self .assertEqual (result , (1 , 2 ))
23
37
38
+ statements = self ._get_cached_statements ()
39
+ self ._check_statements_are_not_closed (statements )
40
+
24
41
await self .con .execute (
25
42
'ALTER TABLE tab1 ALTER COLUMN b SET DATA TYPE text' )
26
43
27
44
result = await self .con .fetchrow ('SELECT * FROM tab1' )
28
45
self .assertEqual (result , (1 , '2' ))
46
+
47
+ self ._check_statements_are_closed (statements )
29
48
finally :
30
49
await self .con .execute ('DROP TABLE tab1' )
31
50
@@ -37,6 +56,9 @@ async def test_prepare_cache_invalidation_in_transaction(self):
37
56
result = await self .con .fetchrow ('SELECT * FROM tab1' )
38
57
self .assertEqual (result , (1 , 2 ))
39
58
59
+ statements = self ._get_cached_statements ()
60
+ self ._check_statements_are_not_closed (statements )
61
+
40
62
await self .con .execute (
41
63
'ALTER TABLE tab1 ALTER COLUMN b SET DATA TYPE text' )
42
64
@@ -45,6 +67,8 @@ async def test_prepare_cache_invalidation_in_transaction(self):
45
67
async with self .con .transaction ():
46
68
result = await self .con .fetchrow ('SELECT * FROM tab1' )
47
69
70
+ self ._check_statements_are_closed (statements )
71
+
48
72
# This is now OK,
49
73
result = await self .con .fetchrow ('SELECT * FROM tab1' )
50
74
self .assertEqual (result , (1 , '2' ))
@@ -69,6 +93,12 @@ async def test_prepare_cache_invalidation_in_pool(self):
69
93
result = await con2 .fetchrow ('SELECT * FROM tab1' )
70
94
self .assertEqual (result , (1 , 2 ))
71
95
96
+ statements1 = self ._get_cached_statements (con1 )
97
+ self ._check_statements_are_not_closed (statements1 )
98
+
99
+ statements2 = self ._get_cached_statements (con2 )
100
+ self ._check_statements_are_not_closed (statements2 )
101
+
72
102
await self .con .execute (
73
103
'ALTER TABLE tab1 ALTER COLUMN b SET DATA TYPE text' )
74
104
@@ -77,6 +107,9 @@ async def test_prepare_cache_invalidation_in_pool(self):
77
107
result = await con1 .fetchrow ('SELECT * FROM tab1' )
78
108
self .assertEqual (result , (1 , '2' ))
79
109
110
+ self ._check_statements_are_closed (statements1 )
111
+ self ._check_statements_are_closed (statements2 )
112
+
80
113
async with con2 .transaction ():
81
114
# This should work, as con1 should have invalidated
82
115
# the plan cache.
@@ -98,11 +131,17 @@ async def test_type_cache_invalidation_in_transaction(self):
98
131
result = await self .con .fetchrow ('SELECT * FROM tab1' )
99
132
self .assertEqual (result , (1 , (2 , 3 )))
100
133
134
+ statements = self ._get_cached_statements ()
135
+ self ._check_statements_are_not_closed (statements )
136
+
101
137
async with self .con .transaction ():
102
138
await self .con .execute ('ALTER TYPE typ1 ADD ATTRIBUTE c text' )
103
139
with self .assertRaisesRegex (
104
140
asyncpg .OutdatedSchemaCacheError , ERRNUM ):
105
141
await self .con .fetchrow ('SELECT * FROM tab1' )
142
+
143
+ self ._check_statements_are_closed (statements )
144
+
106
145
# The second request must be correct (cache was dropped):
107
146
result = await self .con .fetchrow ('SELECT * FROM tab1' )
108
147
self .assertEqual (result , (1 , (2 , 3 , None )))
@@ -123,13 +162,19 @@ async def test_type_cache_invalidation_in_cancelled_transaction(self):
123
162
result = await self .con .fetchrow ('SELECT * FROM tab1' )
124
163
self .assertEqual (result , (1 , (2 , 3 )))
125
164
165
+ statements = self ._get_cached_statements ()
166
+ self ._check_statements_are_not_closed (statements )
167
+
126
168
try :
127
169
async with self .con .transaction ():
128
170
await self .con .execute (
129
171
'ALTER TYPE typ1 ADD ATTRIBUTE c text' )
130
172
with self .assertRaisesRegex (
131
173
asyncpg .OutdatedSchemaCacheError , ERRNUM ):
132
174
await self .con .fetchrow ('SELECT * FROM tab1' )
175
+
176
+ self ._check_statements_are_closed (statements )
177
+
133
178
# The second request must be correct (cache was dropped):
134
179
result = await self .con .fetchrow ('SELECT * FROM tab1' )
135
180
self .assertEqual (result , (1 , (2 , 3 , None )))
@@ -158,13 +203,19 @@ async def test_prepared_type_cache_invalidation(self):
158
203
result = await prep .fetchrow ()
159
204
self .assertEqual (result , (1 , (2 , 3 )))
160
205
206
+ statements = self ._get_cached_statements ()
207
+ self ._check_statements_are_not_closed (statements )
208
+
161
209
try :
162
210
async with self .con .transaction ():
163
211
await self .con .execute (
164
212
'ALTER TYPE typ1 ADD ATTRIBUTE c text' )
165
213
with self .assertRaisesRegex (
166
214
asyncpg .OutdatedSchemaCacheError , ERRNUM ):
167
215
await prep .fetchrow ()
216
+
217
+ self ._check_statements_are_closed (statements )
218
+
168
219
# PS has its local cache for types codecs, even after the
169
220
# cache cleanup it is not possible to use it.
170
221
# That's why it is marked as closed.
@@ -206,11 +257,16 @@ async def test_type_cache_invalidation_on_drop_type_attr(self):
206
257
result = await self .con .fetchrow ('SELECT * FROM tab1' )
207
258
self .assertEqual (result , (1 , (2 , 3 , 'x' )))
208
259
260
+ statements = self ._get_cached_statements ()
261
+ self ._check_statements_are_not_closed (statements )
262
+
209
263
await self .con .execute ('ALTER TYPE typ1 DROP ATTRIBUTE x' )
210
264
with self .assertRaisesRegex (
211
265
asyncpg .OutdatedSchemaCacheError , ERRNUM ):
212
266
await self .con .fetchrow ('SELECT * FROM tab1' )
213
267
268
+ self ._check_statements_are_closed (statements )
269
+
214
270
# This is now OK, the cache is filled after being dropped.
215
271
result = await self .con .fetchrow ('SELECT * FROM tab1' )
216
272
self .assertEqual (result , (1 , (3 , 'x' )))
@@ -228,6 +284,9 @@ async def test_type_cache_invalidation_on_change_attr(self):
228
284
result = await self .con .fetchrow ('SELECT * FROM tab1' )
229
285
self .assertEqual (result , (1 , (2 , 3 )))
230
286
287
+ statements = self ._get_cached_statements ()
288
+ self ._check_statements_are_not_closed (statements )
289
+
231
290
# It is slightly artificial, but can take place in transactional
232
291
# schema changing. Nevertheless, if the code checks and raises it
233
292
# the most probable reason is a difference with the cache type.
@@ -237,6 +296,8 @@ async def test_type_cache_invalidation_on_change_attr(self):
237
296
asyncpg .OutdatedSchemaCacheError , ERRTYP ):
238
297
await self .con .fetchrow ('SELECT * FROM tab1' )
239
298
299
+ self ._check_statements_are_closed (statements )
300
+
240
301
# This is now OK, the cache is filled after being dropped.
241
302
result = await self .con .fetchrow ('SELECT * FROM tab1' )
242
303
self .assertEqual (result , (1 , (2 , None )))
@@ -265,9 +326,15 @@ async def test_type_cache_invalidation_in_pool(self):
265
326
result = await con1 .fetchrow ('SELECT * FROM tab1' )
266
327
self .assertEqual (result , (1 , (2 , 3 )))
267
328
329
+ statements1 = self ._get_cached_statements (con1 )
330
+ self ._check_statements_are_not_closed (statements1 )
331
+
268
332
result = await con2 .fetchrow ('SELECT * FROM tab1' )
269
333
self .assertEqual (result , (1 , (2 , 3 )))
270
334
335
+ statements2 = self ._get_cached_statements (con2 )
336
+ self ._check_statements_are_not_closed (statements2 )
337
+
271
338
# Create the same schema in the "testdb", fetch data which caches
272
339
# type info.
273
340
con_chk = await pool_chk .acquire ()
@@ -277,6 +344,9 @@ async def test_type_cache_invalidation_in_pool(self):
277
344
result = await con_chk .fetchrow ('SELECT * FROM tab1' )
278
345
self .assertEqual (result , (1 , (2 , 3 )))
279
346
347
+ statements_chk = self ._get_cached_statements (con_chk )
348
+ self ._check_statements_are_not_closed (statements_chk )
349
+
280
350
# Change schema in the databases.
281
351
await self .con .execute ('ALTER TYPE typ1 ADD ATTRIBUTE c text' )
282
352
await con_chk .execute ('ALTER TYPE typ1 ADD ATTRIBUTE c text' )
@@ -287,6 +357,9 @@ async def test_type_cache_invalidation_in_pool(self):
287
357
asyncpg .OutdatedSchemaCacheError , ERRNUM ):
288
358
await con1 .fetchrow ('SELECT * FROM tab1' )
289
359
360
+ self ._check_statements_are_closed (statements1 )
361
+ self ._check_statements_are_closed (statements2 )
362
+
290
363
async with con2 .transaction ():
291
364
# This should work, as con1 should have invalidated all caches.
292
365
result = await con2 .fetchrow ('SELECT * FROM tab1' )
@@ -298,10 +371,14 @@ async def test_type_cache_invalidation_in_pool(self):
298
371
299
372
# Check the invalidation is database-specific, i.e. cache entries
300
373
# for pool_chk/con_chk was not dropped via pool/con1.
374
+
375
+ self ._check_statements_are_not_closed (statements_chk )
376
+
301
377
with self .assertRaisesRegex (
302
378
asyncpg .OutdatedSchemaCacheError , ERRNUM ):
303
379
await con_chk .fetchrow ('SELECT * FROM tab1' )
304
380
381
+ self ._check_statements_are_closed (statements_chk )
305
382
finally :
306
383
await self .con .execute ('DROP TABLE tab1' )
307
384
await self .con .execute ('DROP TYPE typ1' )
0 commit comments