-
Notifications
You must be signed in to change notification settings - Fork 3.8k
/
savepoints
539 lines (470 loc) · 15 KB
/
savepoints
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
# This test exercises the savepoint state in the conn executor.
subtest implicit_release_at_end
# It's OK to leave savepoints open when the txn commits.
# This releases everything.
sql
BEGIN
SAVEPOINT foo
SAVEPOINT bar
SAVEPOINT baz
COMMIT
----
1: BEGIN -- 0 rows
-- NoTxn -> Open #.... (none)
2: SAVEPOINT foo -- 0 rows
-- Open -> Open ##... foo
3: SAVEPOINT bar -- 0 rows
-- Open -> Open ###.. foo>bar
4: SAVEPOINT baz -- 0 rows
-- Open -> Open ####. foo>bar>baz
5: COMMIT -- 0 rows
-- Open -> NoTxn ##### (none)
# Ditto rollbacks.
sql
BEGIN
SAVEPOINT foo
SAVEPOINT bar
SAVEPOINT baz
ROLLBACK
----
1: BEGIN -- 0 rows
-- NoTxn -> Open #.... (none)
2: SAVEPOINT foo -- 0 rows
-- Open -> Open ##... foo
3: SAVEPOINT bar -- 0 rows
-- Open -> Open ###.. foo>bar
4: SAVEPOINT baz -- 0 rows
-- Open -> Open ####. foo>bar>baz
5: ROLLBACK -- 0 rows
-- Open -> NoTxn #.... (none)
subtest end
subtest savepoint_stack
sql
BEGIN
SAVEPOINT foo
SAVEPOINT foo
SAVEPOINT bar
SAVEPOINT baz
ROLLBACK TO SAVEPOINT foo
SAVEPOINT baz
RELEASE SAVEPOINT foo
SAVEPOINT bar
RELEASE SAVEPOINT foo
COMMIT
----
1: BEGIN -- 0 rows
-- NoTxn -> Open #.......... (none)
2: SAVEPOINT foo -- 0 rows
-- Open -> Open ##......... foo
3: SAVEPOINT foo -- 0 rows
-- Open -> Open ###........ foo>foo
4: SAVEPOINT bar -- 0 rows
-- Open -> Open ####....... foo>foo>bar
5: SAVEPOINT baz -- 0 rows
-- Open -> Open #####...... foo>foo>bar>baz
6: ROLLBACK TO SAVEPOINT foo -- 0 rows
-- Open -> Open ###........ foo>foo
7: SAVEPOINT baz -- 0 rows
-- Open -> Open ###...#.... foo>foo>baz
8: RELEASE SAVEPOINT foo -- 0 rows
-- Open -> Open ###...##... foo
9: SAVEPOINT bar -- 0 rows
-- Open -> Open ###...###.. foo>bar
10: RELEASE SAVEPOINT foo -- 0 rows
-- Open -> Open ###...####. (none)
11: COMMIT -- 0 rows
-- Open -> NoTxn ###...##### (none)
subtest end
subtest savepoint_release_vs_rollback
# A rollback keeps the savepoint active.
sql
BEGIN
SAVEPOINT foo
ROLLBACK TO SAVEPOINT foo
ROLLBACK TO SAVEPOINT foo
COMMIT
----
1: BEGIN -- 0 rows
-- NoTxn -> Open #.... (none)
2: SAVEPOINT foo -- 0 rows
-- Open -> Open ##... foo
3: ROLLBACK TO SAVEPOINT foo -- 0 rows
-- Open -> Open ##... foo
4: ROLLBACK TO SAVEPOINT foo -- 0 rows
-- Open -> Open ##... foo
5: COMMIT -- 0 rows
-- Open -> NoTxn ##..# (none)
# A release does not.
sql
BEGIN
SAVEPOINT foo
RELEASE SAVEPOINT foo
RELEASE SAVEPOINT foo
COMMIT
----
1: BEGIN -- 0 rows
-- NoTxn -> Open #.... (none)
2: SAVEPOINT foo -- 0 rows
-- Open -> Open ##... foo
3: RELEASE SAVEPOINT foo -- 0 rows
-- Open -> Open ###.. (none)
4: RELEASE SAVEPOINT foo -- pq: savepoint foo does not exist
-- Open -> Aborted XXXXX (none)
5: COMMIT -- 0 rows
-- Aborted -> NoTxn #.... (none)
subtest end
subtest rollback_after_sql_error
sql
BEGIN
SAVEPOINT foo
SELECT nonexistent
ROLLBACK TO SAVEPOINT foo
SELECT 123
COMMIT
----
1: BEGIN -- 0 rows
-- NoTxn -> Open #..... (none)
2: SAVEPOINT foo -- 0 rows
-- Open -> Open ##.... foo
3: SELECT nonexistent -- pq: column "nonexistent" does not exist
-- Open -> Aborted XXXXXX foo
4: ROLLBACK TO SAVEPOINT foo -- 0 rows
-- Aborted -> Open ##.... foo
5: SELECT 123 -- 1 row
-- Open -> Open ##..#. foo
6: COMMIT -- 0 rows
-- Open -> NoTxn ##..## (none)
subtest end
subtest rollback_after_dup_error
sql
CREATE TABLE t(x INT UNIQUE)
INSERT INTO t(x) VALUES (1)
BEGIN
SAVEPOINT foo
INSERT INTO t(x) VALUES (1)
ROLLBACK TO SAVEPOINT foo
INSERT INTO t(x) VALUES (2)
COMMIT
----
1: CREATE TABLE t(x INT UNIQUE) -- 0 rows
-- NoTxn -> NoTxn #....... (none)
2: INSERT INTO t(x) VALUES (1) -- 1 row
-- NoTxn -> NoTxn ##...... (none)
3: BEGIN -- 0 rows
-- NoTxn -> Open ###..... (none)
4: SAVEPOINT foo -- 0 rows
-- Open -> Open ####.... foo
5: INSERT INTO t(x) VALUES (1) -- pq: duplicate key value (x)=(1) violates unique constraint "t_x_key"
-- Open -> Aborted XXXXXXXX foo
6: ROLLBACK TO SAVEPOINT foo -- 0 rows
-- Aborted -> Open ####.... foo
7: INSERT INTO t(x) VALUES (2) -- 1 row
-- Open -> Open ####..#. foo
8: COMMIT -- 0 rows
-- Open -> NoTxn ####..## (none)
sql
DROP TABLE t
----
1: DROP TABLE t -- 0 rows
-- NoTxn -> NoTxn # (none)
subtest end
subtest rollback_after_ddl
# DDL under savepoints is fine as long as there is no rollback.
# Note: we do two DDL; the first one is there just to anchor
# the txn on the config range. The second DDL is the one
# exercised in the test.
sql
BEGIN; CREATE TABLE unused(x INT)
SAVEPOINT foo
CREATE TABLE t(x INT)
RELEASE SAVEPOINT foo
COMMIT
----
1: BEGIN; CREATE TABLE unused(x INT) -- 0 rows
-- NoTxn -> Open #.... (none)
2: SAVEPOINT foo -- 0 rows
-- Open -> Open ##... foo
3: CREATE TABLE t(x INT) -- 0 rows
-- Open -> Open ###.. foo
4: RELEASE SAVEPOINT foo -- 0 rows
-- Open -> Open ####. (none)
5: COMMIT -- 0 rows
-- Open -> NoTxn ##### (none)
sql
DROP TABLE unused
DROP TABLE t
----
1: DROP TABLE unused -- 0 rows
-- NoTxn -> NoTxn #. (none)
2: DROP TABLE t -- 0 rows
-- NoTxn -> NoTxn ## (none)
# Rollback is unsupported after DDL for now.
# TODO(knz): Lift this limitation.
sql
BEGIN; CREATE TABLE unused(x INT)
SAVEPOINT foo
CREATE TABLE t(x INT)
ROLLBACK TO SAVEPOINT foo
----
1: BEGIN; CREATE TABLE unused(x INT) -- 0 rows
-- NoTxn -> Open #... (none)
2: SAVEPOINT foo -- 0 rows
-- Open -> Open ##.. foo
3: CREATE TABLE t(x INT) -- 0 rows
-- Open -> Open ###. foo
4: ROLLBACK TO SAVEPOINT foo -- pq: unimplemented: ROLLBACK TO SAVEPOINT not yet supported after DDL statements
-- Open -> Aborted XXXX foo
subtest end
subtest cockroach_restart_cant_be_nested
sql
BEGIN
SAVEPOINT foo
SAVEPOINT cockroach_restart
----
1: BEGIN -- 0 rows
-- NoTxn -> Open #.. (none)
2: SAVEPOINT foo -- 0 rows
-- Open -> Open ##. foo
3: SAVEPOINT cockroach_restart -- pq: SAVEPOINT cockroach_restart cannot be nested
-- Open -> Aborted XXX foo
subtest end
subtest invalid_uses
sql
SAVEPOINT foo
ROLLBACK TO SAVEPOINT foo
RELEASE SAVEPOINT foo
----
1: SAVEPOINT foo -- pq: there is no transaction in progress
-- NoTxn -> NoTxn #.. (none)
2: ROLLBACK TO SAVEPOINT foo -- pq: savepoint foo does not exist
-- NoTxn -> NoTxn ##. (none)
3: RELEASE SAVEPOINT foo -- pq: there is no transaction in progress
-- NoTxn -> NoTxn ### (none)
sql
BEGIN
SAVEPOINT foo
RELEASE SAVEPOINT bar
----
1: BEGIN -- 0 rows
-- NoTxn -> Open #.. (none)
2: SAVEPOINT foo -- 0 rows
-- Open -> Open ##. foo
3: RELEASE SAVEPOINT bar -- pq: savepoint bar does not exist
-- Open -> Aborted XXX foo
sql
BEGIN
SAVEPOINT foo
ROLLBACK TO SAVEPOINT bar
----
1: BEGIN -- 0 rows
-- NoTxn -> Open #.. (none)
2: SAVEPOINT foo -- 0 rows
-- Open -> Open ##. foo
3: ROLLBACK TO SAVEPOINT bar -- pq: savepoint bar does not exist
-- Open -> Aborted XXX foo
subtest end
subtest rollback_after_error
# check that we can rollback after an error
sql
BEGIN; SAVEPOINT foo
SELECT * FROM bogus_name
ROLLBACK TO SAVEPOINT foo
ROLLBACK
----
1: BEGIN; SAVEPOINT foo -- 0 rows
-- NoTxn -> Open #... foo(r)
2: SELECT * FROM bogus_name -- pq: relation "bogus_name" does not exist
-- Open -> Aborted XXXX foo(r)
3: ROLLBACK TO SAVEPOINT foo -- 0 rows
-- Aborted -> Open #... foo(r)
4: ROLLBACK -- 0 rows
-- Open -> NoTxn #... (none)
# check that we can rollback after a retriable error to an initial savepoint
sql
BEGIN; SAVEPOINT init
SELECT crdb_internal.force_retry('100ms')
ROLLBACK TO SAVEPOINT init
ROLLBACK
----
1: BEGIN; SAVEPOINT init -- 0 rows
-- NoTxn -> Open #... init(r)
2: SELECT crdb_internal.force_retry('100ms') -- pq: restart transaction: crdb_internal.force_retry(): TransactionRetryWithProtoRefreshError: forced by crdb_internal.force_retry()
-- Open -> Aborted XXXX init(r)
3: ROLLBACK TO SAVEPOINT init -- 0 rows
-- Aborted -> Open #... init(r)
4: ROLLBACK -- 0 rows
-- Open -> NoTxn #... (none)
# Check that, after a retriable error, rolling back to anything an initial
# savepoint fails with a retriable error.
sql
CREATE TABLE t(x INT)
BEGIN; SAVEPOINT init; SELECT count(1) from t; SAVEPOINT inner_savepoint
SELECT crdb_internal.force_retry('100ms')
ROLLBACK TO SAVEPOINT inner_savepoint
ROLLBACK TO SAVEPOINT init
ROLLBACK; DROP TABLE t
----
1: CREATE TABLE t(x INT) -- 0 rows
-- NoTxn -> NoTxn #..... (none)
2: BEGIN; SAVEPOINT init; SELECT count(1) from t; SAVEPOINT inner_savepoint -- 0 rows
-- NoTxn -> Open ##.... init(r)>inner_savepoint
3: SELECT crdb_internal.force_retry('100ms') -- pq: restart transaction: crdb_internal.force_retry(): TransactionRetryWithProtoRefreshError: forced by crdb_internal.force_retry()
-- Open -> Aborted XXXXXX init(r)>inner_savepoint
4: ROLLBACK TO SAVEPOINT inner_savepoint -- pq: restart transaction: TransactionRetryWithProtoRefreshError: cannot rollback to savepoint because the transaction experience a serializable restart
-- Aborted -> Aborted XXXXXX init(r)>inner_savepoint
5: ROLLBACK TO SAVEPOINT init -- 0 rows
-- Aborted -> Open ##.... init(r)
6: ROLLBACK; DROP TABLE t -- 0 rows
-- Open -> NoTxn ##.... (none)
subtest end
subtest restart
subtest restart/must_be_first_in_txn
sql
CREATE TABLE t(x INT)
BEGIN
INSERT INTO t(x) VALUES (1)
SAVEPOINT cockroach_restart
----
1: CREATE TABLE t(x INT) -- 0 rows
-- NoTxn -> NoTxn #... (none)
2: BEGIN -- 0 rows
-- NoTxn -> Open ##.. (none)
3: INSERT INTO t(x) VALUES (1) -- 1 row
-- Open -> Open ###. (none)
4: SAVEPOINT cockroach_restart -- pq: SAVEPOINT cockroach_restart needs to be the first statement in a transaction
-- Open -> Aborted XXXX (none)
sql
DROP TABLE t
----
1: DROP TABLE t -- 0 rows
-- NoTxn -> NoTxn # (none)
subtest end
subtest restart/release_without_savepoint
sql
BEGIN
RELEASE SAVEPOINT cockroach_restart
ROLLBACK
----
1: BEGIN -- 0 rows
-- NoTxn -> Open #.. (none)
2: RELEASE SAVEPOINT cockroach_restart -- pq: savepoint cockroach_restart does not exist
-- Open -> Aborted XXX (none)
3: ROLLBACK -- 0 rows
-- Aborted -> NoTxn #.. (none)
subtest end
subtest restart/rollback_without_savepoint
# ROLLBACK TO SAVEPOINT in an open txn without a SAVEPOINT.
sql
BEGIN
ROLLBACK TO SAVEPOINT cockroach_restart
----
1: BEGIN -- 0 rows
-- NoTxn -> Open #. (none)
2: ROLLBACK TO SAVEPOINT cockroach_restart -- pq: savepoint cockroach_restart does not exist
-- Open -> Aborted XX (none)
# ROLLBACK TO SAVEPOINT in an aborted txn without a SAVEPOINT.
sql
BEGIN
SELECT * FROM bogus_name
ROLLBACK TO SAVEPOINT cockroach_restart
----
1: BEGIN -- 0 rows
-- NoTxn -> Open #.. (none)
2: SELECT * FROM bogus_name -- pq: relation "bogus_name" does not exist
-- Open -> Aborted XXX (none)
3: ROLLBACK TO SAVEPOINT cockroach_restart -- pq: savepoint cockroach_restart does not exist
-- Aborted -> Aborted XXX (none)
subtest end
subtest restart/rollbacks
sql
CREATE TABLE t(x INT);
BEGIN; SAVEPOINT cockroach_restart
ROLLBACK TO SAVEPOINT cockroach_restart
ROLLBACK TO SAVEPOINT cockroach_restart
INSERT INTO t(x) VALUES (1)
ROLLBACK TO SAVEPOINT cockroach_restart
COMMIT
----
1: CREATE TABLE t(x INT); -- 0 rows
-- NoTxn -> NoTxn #...... (none)
2: BEGIN; SAVEPOINT cockroach_restart -- 0 rows
-- NoTxn -> Open ##..... cockroach_restart(r)
3: ROLLBACK TO SAVEPOINT cockroach_restart -- 0 rows
-- Open -> Open ##..... cockroach_restart(r)
4: ROLLBACK TO SAVEPOINT cockroach_restart -- 0 rows
-- Open -> Open ##..... cockroach_restart(r)
5: INSERT INTO t(x) VALUES (1) -- 1 row
-- Open -> Open ##..#.. cockroach_restart(r)
6: ROLLBACK TO SAVEPOINT cockroach_restart -- 0 rows
-- Open -> Open ##..... cockroach_restart(r)
7: COMMIT -- 0 rows
-- Open -> NoTxn ##....# (none)
sql
DROP TABLE t
----
1: DROP TABLE t -- 0 rows
-- NoTxn -> NoTxn # (none)
subtest end
subtest restart/savepoint_under_restart
sql
BEGIN; SAVEPOINT cockroach_restart
SAVEPOINT foo
SAVEPOINT bar
ROLLBACK TO SAVEPOINT foo
SELECT crdb_internal.force_retry('1s')
ROLLBACK TO SAVEPOINT cockroach_restart
SELECT 123
COMMIT
----
1: BEGIN; SAVEPOINT cockroach_restart -- 0 rows
-- NoTxn -> Open #....... cockroach_restart(r)
2: SAVEPOINT foo -- 0 rows
-- Open -> Open ##...... cockroach_restart(r)>foo
3: SAVEPOINT bar -- 0 rows
-- Open -> Open ###..... cockroach_restart(r)>foo>bar
4: ROLLBACK TO SAVEPOINT foo -- 0 rows
-- Open -> Open ##...... cockroach_restart(r)>foo
5: SELECT crdb_internal.force_retry('1s') -- pq: restart transaction: crdb_internal.force_retry(): TransactionRetryWithProtoRefreshError: forced by crdb_internal.force_retry()
-- Open -> Aborted XXXXXXXX cockroach_restart(r)>foo
6: ROLLBACK TO SAVEPOINT cockroach_restart -- 0 rows
-- Aborted -> Open #....... cockroach_restart(r)
7: SELECT 123 -- 1 row
-- Open -> Open #.....#. cockroach_restart(r)
8: COMMIT -- 0 rows
-- Open -> NoTxn #.....## (none)
subtest end
subtest restart/all_savepoints_disabled
# Under "force_savepoint_restart", every savepoint
# is a restart savepoint.
sql
SET force_savepoint_restart = true
BEGIN; SAVEPOINT foo
SAVEPOINT bar
----
1: SET force_savepoint_restart = true -- 0 rows
-- NoTxn -> NoTxn #.. (none)
2: BEGIN; SAVEPOINT foo -- 0 rows
-- NoTxn -> Open ##. foo(r)
3: SAVEPOINT bar -- pq: SAVEPOINT cockroach_restart cannot be nested
-- Open -> Aborted XXX foo(r)
sql
SET force_savepoint_restart = false
----
1: SET force_savepoint_restart = false -- 0 rows
-- NoTxn -> NoTxn # (none)
subtest end
subtest end
# Test that the rewinding we do when performing an automatic retry restores the
# savepoint stack properly.
subtest rewing_on_automatic_restarts
# We're going to generate a retriable error that will rewind us back to the
# SELECT statement (not to the original SAVEPOINT statement since that one is
# special and we advance the rewind position past it). The test checks that,
# after every restart, the RELEASE works because the savepoint has be
# re-instituted before we rewind.
sql
BEGIN; SAVEPOINT a; SELECT 42; RELEASE a; SELECT crdb_internal.force_retry('10ms'); COMMIT;
----
1: BEGIN; SAVEPOINT a; SELECT 42; RELEASE a; SELECT crdb_internal.force_retry('10ms'); COMMIT; -- 0 rows
-- NoTxn -> NoTxn # (none)
subtest end