@@ -525,6 +525,50 @@ def test_parallel_rowcount_threaded(self):
525
525
self .test_parallel_rowcount ()
526
526
threading .current_thread ().testing = True
527
527
528
+ def test_parallel_execute_retry_on_serialization_failure (self ):
529
+ TEST_TABLE_NAME = "_upgrade_serialization_failure_test_table"
530
+ N_ROWS = 10
531
+
532
+ cr = self .env .cr
533
+
534
+ cr .execute (
535
+ util .format_query (
536
+ cr ,
537
+ """
538
+ DROP TABLE IF EXISTS {table};
539
+
540
+ CREATE TABLE {table} (
541
+ id SERIAL PRIMARY KEY,
542
+ other_id INTEGER,
543
+ FOREIGN KEY (other_id) REFERENCES {table} ON DELETE CASCADE
544
+ );
545
+
546
+ INSERT INTO {table} SELECT GENERATE_SERIES(1, %s);
547
+
548
+ -- map odd numbers `n` to `n + 1` and viceversa (`n + 1` to `n`)
549
+ UPDATE {table} SET other_id = id + (MOD(id, 2) - 0.5)*2;
550
+ """
551
+ % N_ROWS ,
552
+ table = TEST_TABLE_NAME ,
553
+ )
554
+ )
555
+
556
+ threading .current_thread ().testing = False
557
+ # exploded queries will generate a SerializationFailed error, causing some of the queries to be retried
558
+ util .explode_execute (
559
+ cr , util .format_query (cr , "DELETE FROM {}" , TEST_TABLE_NAME ), TEST_TABLE_NAME , bucket_size = 1
560
+ )
561
+ threading .current_thread ().testing = True
562
+
563
+ if hasattr (self , "_savepoint_id" ):
564
+ # `explode_execute` causes the cursor to be committed, losing the automatic checkpoint
565
+ # Force a new one to avoid issues when cleaning up
566
+ self .addCleanup (cr .execute , f"SAVEPOINT test_{ self ._savepoint_id } " )
567
+ self .addCleanup (cr .execute , util .format_query (cr , "DROP TABLE IF EXISTS {}" , TEST_TABLE_NAME ))
568
+
569
+ cr .execute (util .format_query (cr , "SELECT 1 FROM {}" , TEST_TABLE_NAME ))
570
+ self .assertFalse (cr .rowcount )
571
+
528
572
def test_create_column_with_fk (self ):
529
573
cr = self .env .cr
530
574
self .assertFalse (util .column_exists (cr , "res_partner" , "_test_lang_id" ))
0 commit comments