postgres.git / summary / log / commit / refs

commit    b6ccd30d8ff6422ad0f79ce2fc801f2437d90664
Author:   Peter Eisentraut <peter@eisentraut.org>
Date:     Tue Apr 07 09:07:59 2026 +0000

    Add isolation tests for UPDATE/DELETE FOR PORTION OF
    
    Add documentation about concurrency issues related to UPDATE/DELETE
    FOR PORTION OF as well as supporting isolation tests.
    
    Author: Paul A. Jungwirth <pj@illuminatedcomputing.com>
    Reviewed-by: Peter Eisentraut <peter@eisentraut.org>
    Discussion: https://www.postgresql.org/message-id/flat/ec498c3d-5f2b-48ec-b989-5561c8aa2024%40illuminatedcomputing.com


doc/src/sgml/dml.sgml | 38 + doc/src/sgml/images/Makefile | 3 +- doc/src/sgml/images/meson.build | 1 + doc/src/sgml/images/temporal-isolation.svg | 47 + doc/src/sgml/images/temporal-isolation.txt | 44 + src/backend/executor/nodeModifyTable.c | 4 + src/test/isolation/expected/for-portion-of.out | 4089 ++++++++++++++++++++++++ src/test/isolation/isolation_schedule | 1 + src/test/isolation/specs/for-portion-of.spec | 597 ++++ 9 files changed, 4823 insertions(+), 1 deletion(-) diff --git a/doc/src/sgml/dml.sgml b/doc/src/sgml/dml.sgml index 86d3865ae90..429aae9bd7b 100644 --- a/doc/src/sgml/dml.sgml +++ b/doc/src/sgml/dml.sgml @@ -394,6 +394,44 @@ DELETE FROM products triggers are fired, but permission checks for inserting rows are skipped. </para> + + <para> + In <literal>READ COMMITTED</literal> mode, temporal updates and deletes can + yield unexpected results when they concurrently touch the same row. It is + possible to lose all or part of the second update or delete. The scenario + is illustrated in <xref linkend="temporal-isolation-figure"/>. Session 2 + searches for rows to change, and it finds one that Session 1 has already + modified. It waits for Session 1 to commit. Then it re-checks whether the + row still matches its search criteria (including the start/end times + targeted by <literal>FOR PORTION OF</literal>). Session 1 may have changed + those times so that they no longer qualify. + </para> + + <para> + In addition, the temporal leftovers inserted by Session 1 are not visible + within Session 2's transaction, because they are not yet committed. + Therefore there is nothing for Session 2 to update/delete: neither the + modified row nor the leftovers. The portion of history that Session 2 + intended to change is not affected. + </para> + + <figure id="temporal-isolation-figure"> + <title>Temporal Isolation Example</title> + <mediaobject> + <imageobject> + <imagedata fileref="images/temporal-isolation.svg" format="SVG" width="35%"/> + </imageobject> + </mediaobject> + </figure> + + <para> + To solve these problems, precede every temporal update/delete with a + <literal>SELECT FOR UPDATE</literal> matching the same criteria (including + the targeted portion of application time). That way the actual + update/delete doesn't begin until the lock is held, and all concurrent + leftovers will be visible. In higher transaction isolation levels, this + lock is not required. + </para> </sect1> <sect1 id="dml-returning"> diff --git a/doc/src/sgml/images/Makefile b/doc/src/sgml/images/Makefile index 7b8ac0fbb32..9362c30998e 100644 --- a/doc/src/sgml/images/Makefile +++ b/doc/src/sgml/images/Makefile @@ -10,7 +10,8 @@ ALL_IMAGES = \ temporal-entities.svg \ temporal-references.svg \ temporal-update.svg \ - temporal-delete.svg + temporal-delete.svg \ + temporal-isolation.svg DITAA = ditaa DOT = dot diff --git a/doc/src/sgml/images/meson.build b/doc/src/sgml/images/meson.build index ea1fdee3901..220e3eaafb8 100644 --- a/doc/src/sgml/images/meson.build +++ b/doc/src/sgml/images/meson.build @@ -18,6 +18,7 @@ all_files = [ 'temporal-references.txt', 'temporal-update.txt', 'temporal-delete.txt', + 'temporal-isolation.txt', ] foreach file : all_files diff --git a/doc/src/sgml/images/temporal-isolation.svg b/doc/src/sgml/images/temporal-isolation.svg new file mode 100644 index 00000000000..6f88c763800 --- /dev/null +++ b/doc/src/sgml/images/temporal-isolation.svg @@ -0,0 +1,47 @@ +<?xml version="1.0"?> +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 350 672" width="350" height="672" shape-rendering="geometricPrecision" version="1.0"> + <defs> + <filter id="f2" x="0" y="0" width="200%" height="200%"> + <feOffset result="offOut" in="SourceGraphic" dx="5" dy="5"/> + <feGaussianBlur result="blurOut" in="offOut" stdDeviation="3"/> + <feBlend in="SourceGraphic" in2="blurOut" mode="normal"/> + </filter> + </defs> + <g stroke-width="1" stroke-linecap="square" stroke-linejoin="round"> + <rect x="0" y="0" width="350" height="672" style="fill: #ffffff"/> + <path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#ffff33" d="M205.0 301.0 L205.0 371.0 L325.0 371.0 L325.0 301.0 z"/> + <path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#ffff33" d="M325.0 469.0 L325.0 539.0 L205.0 539.0 L205.0 469.0 z"/> + <path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#99dd99" d="M25.0 35.0 L25.0 91.0 L145.0 91.0 L145.0 35.0 z"/> + <path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#99dd99" d="M25.0 133.0 L25.0 189.0 L145.0 189.0 L145.0 133.0 z"/> + <path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#99dd99" d="M25.0 287.0 L145.0 287.0 L145.0 231.0 L25.0 231.0 z"/> + <path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#ffff33" d="M205.0 133.0 L205.0 189.0 L325.0 189.0 L325.0 133.0 z"/> + <path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#ffff33" d="M325.0 581.0 L205.0 581.0 L205.0 637.0 L325.0 637.0 z"/> + <path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#ffff33" d="M205.0 91.0 L325.0 91.0 L325.0 35.0 L205.0 35.0 z"/> + <path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#99dd99" d="M145.0 399.0 L145.0 455.0 L25.0 455.0 L25.0 399.0 z"/> + <path stroke="none" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#000000" d="M80.0 112.0 L85.0 126.0 L90.0 112.0 z"/> + <path stroke="none" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#000000" d="M260.0 112.0 L265.0 126.0 L270.0 112.0 z"/> + <path stroke="none" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#000000" d="M80.0 210.0 L85.0 224.0 L90.0 210.0 z"/> + <path stroke="none" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#000000" d="M260.0 280.0 L265.0 294.0 L270.0 280.0 z"/> + <path stroke="none" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#000000" d="M80.0 378.0 L85.0 392.0 L90.0 378.0 z"/> + <path stroke="none" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#000000" d="M260.0 448.0 L265.0 462.0 L270.0 448.0 z"/> + <path stroke="none" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="#000000" d="M260.0 560.0 L265.0 574.0 L270.0 560.0 z"/> + <path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="none" d="M85.0 91.0 L85.0 119.0 "/> + <path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="none" d="M85.0 189.0 L85.0 217.0 "/> + <path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="none" d="M265.0 539.0 L265.0 567.0 "/> + <path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="none" d="M265.0 91.0 L265.0 119.0 "/> + <path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="none" d="M265.0 371.0 L265.0 455.0 "/> + <path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="none" d="M85.0 385.0 L85.0 287.0 "/> + <path stroke="#000000" stroke-width="1.000000" stroke-linecap="round" stroke-linejoin="round" fill="none" d="M265.0 189.0 L265.0 287.0 "/> + <text x="46" y="68" font-family="Courier" font-size="13" stroke="none" fill="#000000">Session 1</text> + <text x="220" y="334" font-family="Courier" font-size="13" stroke="none" fill="#000000">UPDATE;</text> + <text x="220" y="348" font-family="Courier" font-size="13" stroke="none" fill="#000000">waits...</text> + <text x="48" y="166" font-family="Courier" font-size="13" stroke="none" fill="#000000">BEGIN;</text> + <text x="228" y="166" font-family="Courier" font-size="13" stroke="none" fill="#000000">BEGIN;</text> + <text x="226" y="68" font-family="Courier" font-size="13" stroke="none" fill="#000000">Session 2</text> + <text x="47" y="264" font-family="Courier" font-size="13" stroke="none" fill="#000000">UPDATE;</text> + <text x="46" y="432" font-family="Courier" font-size="13" stroke="none" fill="#000000">COMMIT;</text> + <text x="226" y="614" font-family="Courier" font-size="13" stroke="none" fill="#000000">COMMIT;</text> + <text x="220" y="502" font-family="Courier" font-size="13" stroke="none" fill="#000000">...resumes</text> + <text x="220" y="516" font-family="Courier" font-size="13" stroke="none" fill="#000000">rechecks</text> + </g> +</svg> diff --git a/doc/src/sgml/images/temporal-isolation.txt b/doc/src/sgml/images/temporal-isolation.txt new file mode 100644 index 00000000000..870db7b0025 --- /dev/null +++ b/doc/src/sgml/images/temporal-isolation.txt @@ -0,0 +1,44 @@ ++-----------+ +-----------+ +| cGRE | | cYEL | +|Session 1 | |Session 2 | +| | | | ++-----+-----+ +-----+-----+ + | | + v v ++-----+-----+ +-----+-----+ +| cGRE | | cYEL | +| BEGIN; | | BEGIN; | +| | | | ++-----+-----+ +-----+-----+ + | | + v | ++-----+-----+ | +| cGRE | | +| UPDATE; | | +| | | ++-----+-----+ v + | +-----+-----+ + | | cYEL | + | | UPDATE; | + | | waits... | + | | | + | +-----+-----+ + v | ++-----+-----+ | +| cGRE | | +| COMMIT; | | +| | | ++-----------+ v + +-----+-----+ + | cYEL | + | ...resumes| + | rechecks | + | | + +-----+-----+ + | + v + +-----+-----+ + | cYEL | + | COMMIT; | + | | + +-----------+ diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index dfd7b33aa9b..ef2a6bc6e9d 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1465,6 +1465,10 @@ ExecForPortionOfLeftovers(ModifyTableContext *context, * We have already locked the tuple in ExecUpdate/ExecDelete, and it has * passed EvalPlanQual. This ensures that concurrent updates in READ * COMMITTED can't insert conflicting temporal leftovers. + * + * It does *not* protect against concurrent update/deletes overlooking + * each others' leftovers though. See our isolation tests for details + * about that and a viable workaround. */ if (!table_tuple_fetch_row_version(resultRelInfo->ri_RelationDesc, tupleid, SnapshotAny, oldtupleSlot)) elog(ERROR, "failed to fetch tuple for FOR PORTION OF"); diff --git a/src/test/isolation/expected/for-portion-of.out b/src/test/isolation/expected/for-portion-of.out new file mode 100644 index 00000000000..2438c469599 --- /dev/null +++ b/src/test/isolation/expected/for-portion-of.out @@ -0,0 +1,4089 @@ +Parsed test spec with 2 sessions + +starting permutation: s1rc s2rc s2lock2027 s2upd2027 s2c s1lock2025 s1upd2025 s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2lock2027: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2027-01-01,2028-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2upd2027: + UPDATE products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2027-01-01)| 5.00 +(1 row) + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2027-01-01)| 5.00 +[1,2)|[2027-01-01,2028-01-01)|10.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(5 rows) + + +starting permutation: s1rc s2rc s2lock202503 s2upd202503 s2c s1lock2025 s1upd2025 s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2lock202503: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-03-01,2025-04-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2upd202503: + UPDATE products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-03-01)| 5.00 +[1,2)|[2025-03-01,2025-04-01)|10.00 +[1,2)|[2025-04-01,2030-01-01)| 5.00 +(3 rows) + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2025-03-01)| 8.00 +[1,2)|[2025-03-01,2025-04-01)| 8.00 +[1,2)|[2025-04-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(5 rows) + + +starting permutation: s1rc s2rc s2lock20252026 s2upd20252026 s2c s1lock2025 s1upd2025 s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2lock20252026: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-06-01,2026-06-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2upd20252026: + UPDATE products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-06-01)| 5.00 +[1,2)|[2025-06-01,2026-06-01)|10.00 +(2 rows) + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2025-06-01)| 8.00 +[1,2)|[2025-06-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2026-06-01)|10.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(5 rows) + + +starting permutation: s1rc s2rc s1lock2025 s1upd2025 s1c s2lock2027 s2upd2027 s2c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2lock2027: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2027-01-01,2028-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(1 row) + +step s2upd2027: + UPDATE products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2027-01-01)| 5.00 +[1,2)|[2027-01-01,2028-01-01)|10.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(5 rows) + + +starting permutation: s1rc s2rc s1lock2025 s1upd2025 s1c s2lock202503 s2upd202503 s2c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2lock202503: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-03-01,2025-04-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2025-01-01,2026-01-01)| 8.00 +(1 row) + +step s2upd202503: + UPDATE products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2025-03-01)| 8.00 +[1,2)|[2025-03-01,2025-04-01)|10.00 +[1,2)|[2025-04-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(5 rows) + + +starting permutation: s1rc s2rc s1lock2025 s1upd2025 s1c s2lock20252026 s2upd20252026 s2c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2lock20252026: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-06-01,2026-06-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2025-01-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(2 rows) + +step s2upd20252026: + UPDATE products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2025-06-01)| 8.00 +[1,2)|[2025-06-01,2026-01-01)|10.00 +[1,2)|[2026-01-01,2026-06-01)|10.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(5 rows) + + +starting permutation: s1rc s2rc s2upd2027 s1upd2025 s2c s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2upd2027: + UPDATE products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1upd2025: <... completed> +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2027-01-01)| 5.00 +[1,2)|[2027-01-01,2028-01-01)|10.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rc s2rc s2lock2027 s2upd2027 s1lock2025 s2c s1upd2025 s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2lock2027: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2027-01-01,2028-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2upd2027: + UPDATE products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + <waiting ...> +step s2c: COMMIT; +step s1lock2025: <... completed> +id|valid_at|price +--+--------+----- +(0 rows) + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2027-01-01)| 5.00 +[1,2)|[2027-01-01,2028-01-01)|10.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(5 rows) + + +starting permutation: s1rc s2rc s2upd202503 s1upd2025 s2c s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2upd202503: + UPDATE products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1upd2025: <... completed> +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-03-01)| 5.00 +[1,2)|[2025-03-01,2025-04-01)| 8.00 +[1,2)|[2025-04-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rc s2rc s2lock202503 s2upd202503 s1lock2025 s2c s1upd2025 s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2lock202503: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-03-01,2025-04-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2upd202503: + UPDATE products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + <waiting ...> +step s2c: COMMIT; +step s1lock2025: <... completed> +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2025-03-01,2025-04-01)|10.00 +(1 row) + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2025-03-01)| 8.00 +[1,2)|[2025-03-01,2025-04-01)| 8.00 +[1,2)|[2025-04-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(5 rows) + + +starting permutation: s1rc s2rc s2upd20252026 s1upd2025 s2c s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2upd20252026: + UPDATE products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1upd2025: <... completed> +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-06-01)| 5.00 +[1,2)|[2025-06-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2026-06-01)|10.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(4 rows) + + +starting permutation: s1rc s2rc s2lock20252026 s2upd20252026 s1lock2025 s2c s1upd2025 s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2lock20252026: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-06-01,2026-06-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2upd20252026: + UPDATE products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + <waiting ...> +step s2c: COMMIT; +step s1lock2025: <... completed> +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2025-06-01,2026-06-01)|10.00 +(1 row) + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2025-06-01)| 8.00 +[1,2)|[2025-06-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2026-06-01)|10.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(5 rows) + + +starting permutation: s1rc s2rc s2lock2027 s2del2027 s2c s1lock2025 s1upd2025 s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2lock2027: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2027-01-01,2028-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2del2027: + DELETE FROM products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2027-01-01)| 5.00 +(1 row) + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2027-01-01)| 5.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(4 rows) + + +starting permutation: s1rc s2rc s2lock202503 s2del202503 s2c s1lock2025 s1upd2025 s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2lock202503: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-03-01,2025-04-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2del202503: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-03-01)| 5.00 +[1,2)|[2025-04-01,2030-01-01)| 5.00 +(2 rows) + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2025-03-01)| 8.00 +[1,2)|[2025-04-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(4 rows) + + +starting permutation: s1rc s2rc s2lock20252026 s2del20252026 s2c s1lock2025 s1upd2025 s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2lock20252026: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-06-01,2026-06-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2del20252026: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-06-01)| 5.00 +(1 row) + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2025-06-01)| 8.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rc s2rc s1lock2025 s1upd2025 s1c s2lock2027 s2del2027 s2c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2lock2027: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2027-01-01,2028-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(1 row) + +step s2del2027: + DELETE FROM products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2027-01-01)| 5.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(4 rows) + + +starting permutation: s1rc s2rc s1lock2025 s1upd2025 s1c s2lock202503 s2del202503 s2c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2lock202503: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-03-01,2025-04-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2025-01-01,2026-01-01)| 8.00 +(1 row) + +step s2del202503: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2025-03-01)| 8.00 +[1,2)|[2025-04-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(4 rows) + + +starting permutation: s1rc s2rc s1lock2025 s1upd2025 s1c s2lock20252026 s2del20252026 s2c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2lock20252026: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-06-01,2026-06-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2025-01-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(2 rows) + +step s2del20252026: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2025-06-01)| 8.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rc s2rc s2del2027 s1upd2025 s2c s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2del2027: + DELETE FROM products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + WHERE id = '[1,2)'; + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1upd2025: <... completed> +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2027-01-01)| 5.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rc s2rc s2lock2027 s2del2027 s1lock2025 s2c s1upd2025 s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2lock2027: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2027-01-01,2028-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2del2027: + DELETE FROM products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + WHERE id = '[1,2)'; + +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + <waiting ...> +step s2c: COMMIT; +step s1lock2025: <... completed> +id|valid_at|price +--+--------+----- +(0 rows) + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2027-01-01)| 5.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(4 rows) + + +starting permutation: s1rc s2rc s2del202503 s1upd2025 s2c s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2del202503: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + WHERE id = '[1,2)'; + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1upd2025: <... completed> +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-03-01)| 5.00 +[1,2)|[2025-04-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rc s2rc s2lock202503 s2del202503 s1lock2025 s2c s1upd2025 s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2lock202503: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-03-01,2025-04-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2del202503: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + WHERE id = '[1,2)'; + +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + <waiting ...> +step s2c: COMMIT; +step s1lock2025: <... completed> +id|valid_at|price +--+--------+----- +(0 rows) + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2025-03-01)| 8.00 +[1,2)|[2025-04-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(4 rows) + + +starting permutation: s1rc s2rc s2del20252026 s1upd2025 s2c s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2del20252026: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + WHERE id = '[1,2)'; + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1upd2025: <... completed> +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-06-01)| 5.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rc s2rc s2lock20252026 s2del20252026 s1lock2025 s2c s1upd2025 s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2lock20252026: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-06-01,2026-06-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2del20252026: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + WHERE id = '[1,2)'; + +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + <waiting ...> +step s2c: COMMIT; +step s1lock2025: <... completed> +id|valid_at|price +--+--------+----- +(0 rows) + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2025-06-01)| 8.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rc s2rc s2lock2027 s2upd2027 s2c s1lock2025 s1del2025 s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2lock2027: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2027-01-01,2028-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2upd2027: + UPDATE products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2027-01-01)| 5.00 +(1 row) + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-01-01,2027-01-01)| 5.00 +[1,2)|[2027-01-01,2028-01-01)|10.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(4 rows) + + +starting permutation: s1rc s2rc s2lock202503 s2upd202503 s2c s1lock2025 s1del2025 s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2lock202503: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-03-01,2025-04-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2upd202503: + UPDATE products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-03-01)| 5.00 +[1,2)|[2025-03-01,2025-04-01)|10.00 +[1,2)|[2025-04-01,2030-01-01)| 5.00 +(3 rows) + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rc s2rc s2lock20252026 s2upd20252026 s2c s1lock2025 s1del2025 s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2lock20252026: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-06-01,2026-06-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2upd20252026: + UPDATE products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-06-01)| 5.00 +[1,2)|[2025-06-01,2026-06-01)|10.00 +(2 rows) + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-01-01,2026-06-01)|10.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rc s2rc s1lock2025 s1del2025 s1c s2lock2027 s2upd2027 s2c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2lock2027: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2027-01-01,2028-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(1 row) + +step s2upd2027: + UPDATE products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-01-01,2027-01-01)| 5.00 +[1,2)|[2027-01-01,2028-01-01)|10.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(4 rows) + + +starting permutation: s1rc s2rc s1lock2025 s1del2025 s1c s2lock202503 s2upd202503 s2c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2lock202503: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-03-01,2025-04-01)' + ORDER BY valid_at FOR UPDATE; + +id|valid_at|price +--+--------+----- +(0 rows) + +step s2upd202503: + UPDATE products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rc s2rc s1lock2025 s1del2025 s1c s2lock20252026 s2upd20252026 s2c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2lock20252026: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-06-01,2026-06-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(1 row) + +step s2upd20252026: + UPDATE products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-01-01,2026-06-01)|10.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rc s2rc s2upd2027 s1del2025 s2c s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2upd2027: + UPDATE products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1del2025: <... completed> +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2027-01-01)| 5.00 +[1,2)|[2027-01-01,2028-01-01)|10.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rc s2rc s2lock2027 s2upd2027 s1lock2025 s2c s1del2025 s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2lock2027: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2027-01-01,2028-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2upd2027: + UPDATE products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + <waiting ...> +step s2c: COMMIT; +step s1lock2025: <... completed> +id|valid_at|price +--+--------+----- +(0 rows) + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-01-01,2027-01-01)| 5.00 +[1,2)|[2027-01-01,2028-01-01)|10.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(4 rows) + + +starting permutation: s1rc s2rc s2upd202503 s1del2025 s2c s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2upd202503: + UPDATE products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1del2025: <... completed> +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-03-01)| 5.00 +[1,2)|[2025-04-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rc s2rc s2lock202503 s2upd202503 s1lock2025 s2c s1del2025 s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2lock202503: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-03-01,2025-04-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2upd202503: + UPDATE products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + <waiting ...> +step s2c: COMMIT; +step s1lock2025: <... completed> +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2025-03-01,2025-04-01)|10.00 +(1 row) + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rc s2rc s2upd20252026 s1del2025 s2c s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2upd20252026: + UPDATE products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1del2025: <... completed> +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-06-01)| 5.00 +[1,2)|[2026-01-01,2026-06-01)|10.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rc s2rc s2lock20252026 s2upd20252026 s1lock2025 s2c s1del2025 s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2lock20252026: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-06-01,2026-06-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2upd20252026: + UPDATE products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + <waiting ...> +step s2c: COMMIT; +step s1lock2025: <... completed> +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2025-06-01,2026-06-01)|10.00 +(1 row) + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-01-01,2026-06-01)|10.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rc s2rc s2lock2027 s2del2027 s2c s1lock2025 s1del2025 s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2lock2027: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2027-01-01,2028-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2del2027: + DELETE FROM products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2027-01-01)| 5.00 +(1 row) + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-01-01,2027-01-01)| 5.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rc s2rc s2lock202503 s2del202503 s2c s1lock2025 s1del2025 s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2lock202503: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-03-01,2025-04-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2del202503: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-03-01)| 5.00 +[1,2)|[2025-04-01,2030-01-01)| 5.00 +(2 rows) + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rc s2rc s2lock20252026 s2del20252026 s2c s1lock2025 s1del2025 s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2lock20252026: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-06-01,2026-06-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2del20252026: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-06-01)| 5.00 +(1 row) + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rc s2rc s1lock2025 s1del2025 s1c s2lock2027 s2del2027 s2c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2lock2027: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2027-01-01,2028-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(1 row) + +step s2del2027: + DELETE FROM products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-01-01,2027-01-01)| 5.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rc s2rc s1lock2025 s1del2025 s1c s2lock202503 s2del202503 s2c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2lock202503: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-03-01,2025-04-01)' + ORDER BY valid_at FOR UPDATE; + +id|valid_at|price +--+--------+----- +(0 rows) + +step s2del202503: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rc s2rc s1lock2025 s1del2025 s1c s2lock20252026 s2del20252026 s2c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2lock20252026: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-06-01,2026-06-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(1 row) + +step s2del20252026: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rc s2rc s2del2027 s1del2025 s2c s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2del2027: + DELETE FROM products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + WHERE id = '[1,2)'; + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1del2025: <... completed> +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2027-01-01)| 5.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rc s2rc s2lock2027 s2del2027 s1lock2025 s2c s1del2025 s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2lock2027: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2027-01-01,2028-01-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2del2027: + DELETE FROM products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + WHERE id = '[1,2)'; + +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + <waiting ...> +step s2c: COMMIT; +step s1lock2025: <... completed> +id|valid_at|price +--+--------+----- +(0 rows) + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-01-01,2027-01-01)| 5.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rc s2rc s2del202503 s1del2025 s2c s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2del202503: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + WHERE id = '[1,2)'; + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1del2025: <... completed> +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-03-01)| 5.00 +[1,2)|[2025-04-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rc s2rc s2lock202503 s2del202503 s1lock2025 s2c s1del2025 s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2lock202503: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-03-01,2025-04-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2del202503: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + WHERE id = '[1,2)'; + +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + <waiting ...> +step s2c: COMMIT; +step s1lock2025: <... completed> +id|valid_at|price +--+--------+----- +(0 rows) + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rc s2rc s2del20252026 s1del2025 s2c s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2del20252026: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + WHERE id = '[1,2)'; + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1del2025: <... completed> +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-06-01)| 5.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rc s2rc s2lock20252026 s2del20252026 s1lock2025 s2c s1del2025 s1c s1q +step s1rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2rc: BEGIN ISOLATION LEVEL READ COMMITTED; +step s2lock20252026: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-06-01,2026-06-01)' + ORDER BY valid_at FOR UPDATE; + +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2del20252026: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + WHERE id = '[1,2)'; + +step s1lock2025: + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; + <waiting ...> +step s2c: COMMIT; +step s1lock2025: <... completed> +id|valid_at|price +--+--------+----- +(0 rows) + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rr s2rr s2upd2027 s2c s1upd2025 s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2upd2027: + UPDATE products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2027-01-01)| 5.00 +[1,2)|[2027-01-01,2028-01-01)|10.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(5 rows) + + +starting permutation: s1rr s2rr s2upd202503 s2c s1upd2025 s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2upd202503: + UPDATE products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2025-03-01)| 8.00 +[1,2)|[2025-03-01,2025-04-01)| 8.00 +[1,2)|[2025-04-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(5 rows) + + +starting permutation: s1rr s2rr s2upd20252026 s2c s1upd2025 s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2upd20252026: + UPDATE products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2025-06-01)| 8.00 +[1,2)|[2025-06-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2026-06-01)|10.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(5 rows) + + +starting permutation: s1rr s2rr s1upd2025 s1c s2upd2027 s2c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2upd2027: + UPDATE products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2027-01-01)| 5.00 +[1,2)|[2027-01-01,2028-01-01)|10.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(5 rows) + + +starting permutation: s1rr s2rr s1upd2025 s1c s2upd202503 s2c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2upd202503: + UPDATE products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2025-03-01)| 8.00 +[1,2)|[2025-03-01,2025-04-01)|10.00 +[1,2)|[2025-04-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(5 rows) + + +starting permutation: s1rr s2rr s1upd2025 s1c s2upd20252026 s2c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2upd20252026: + UPDATE products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2025-06-01)| 8.00 +[1,2)|[2025-06-01,2026-01-01)|10.00 +[1,2)|[2026-01-01,2026-06-01)|10.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(5 rows) + + +starting permutation: s1rr s2rr s2upd2027 s1upd2025 s2c s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2upd2027: + UPDATE products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1upd2025: <... completed> +ERROR: could not serialize access due to concurrent update +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2027-01-01)| 5.00 +[1,2)|[2027-01-01,2028-01-01)|10.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rr s2rr s2upd202503 s1upd2025 s2c s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2upd202503: + UPDATE products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1upd2025: <... completed> +ERROR: could not serialize access due to concurrent update +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-03-01)| 5.00 +[1,2)|[2025-03-01,2025-04-01)|10.00 +[1,2)|[2025-04-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rr s2rr s2upd20252026 s1upd2025 s2c s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2upd20252026: + UPDATE products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1upd2025: <... completed> +ERROR: could not serialize access due to concurrent update +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-06-01)| 5.00 +[1,2)|[2025-06-01,2026-06-01)|10.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rr s2rr s1q s2upd2027 s2c s1upd2025 s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2upd2027: + UPDATE products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +ERROR: could not serialize access due to concurrent update +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2027-01-01)| 5.00 +[1,2)|[2027-01-01,2028-01-01)|10.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rr s2rr s1q s2upd202503 s2c s1upd2025 s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2upd202503: + UPDATE products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +ERROR: could not serialize access due to concurrent update +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-03-01)| 5.00 +[1,2)|[2025-03-01,2025-04-01)|10.00 +[1,2)|[2025-04-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rr s2rr s1q s2upd20252026 s2c s1upd2025 s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2upd20252026: + UPDATE products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +ERROR: could not serialize access due to concurrent update +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-06-01)| 5.00 +[1,2)|[2025-06-01,2026-06-01)|10.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rr s2rr s1q s1upd2025 s1c s2upd2027 s2c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2upd2027: + UPDATE products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2027-01-01)| 5.00 +[1,2)|[2027-01-01,2028-01-01)|10.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(5 rows) + + +starting permutation: s1rr s2rr s1q s1upd2025 s1c s2upd202503 s2c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2upd202503: + UPDATE products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2025-03-01)| 8.00 +[1,2)|[2025-03-01,2025-04-01)|10.00 +[1,2)|[2025-04-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(5 rows) + + +starting permutation: s1rr s2rr s1q s1upd2025 s1c s2upd20252026 s2c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2upd20252026: + UPDATE products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2025-06-01)| 8.00 +[1,2)|[2025-06-01,2026-01-01)|10.00 +[1,2)|[2026-01-01,2026-06-01)|10.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(5 rows) + + +starting permutation: s1rr s2rr s1q s2upd2027 s1upd2025 s2c s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2upd2027: + UPDATE products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1upd2025: <... completed> +ERROR: could not serialize access due to concurrent update +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2027-01-01)| 5.00 +[1,2)|[2027-01-01,2028-01-01)|10.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rr s2rr s1q s2upd202503 s1upd2025 s2c s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2upd202503: + UPDATE products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1upd2025: <... completed> +ERROR: could not serialize access due to concurrent update +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-03-01)| 5.00 +[1,2)|[2025-03-01,2025-04-01)|10.00 +[1,2)|[2025-04-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rr s2rr s1q s2upd20252026 s1upd2025 s2c s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2upd20252026: + UPDATE products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1upd2025: <... completed> +ERROR: could not serialize access due to concurrent update +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-06-01)| 5.00 +[1,2)|[2025-06-01,2026-06-01)|10.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rr s2rr s2del2027 s2c s1upd2025 s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2del2027: + DELETE FROM products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2027-01-01)| 5.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(4 rows) + + +starting permutation: s1rr s2rr s2del202503 s2c s1upd2025 s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2del202503: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2025-03-01)| 8.00 +[1,2)|[2025-04-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(4 rows) + + +starting permutation: s1rr s2rr s2del20252026 s2c s1upd2025 s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2del20252026: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2025-06-01)| 8.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rr s2rr s1upd2025 s1c s2del2027 s2c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2del2027: + DELETE FROM products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2027-01-01)| 5.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(4 rows) + + +starting permutation: s1rr s2rr s1upd2025 s1c s2del202503 s2c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2del202503: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2025-03-01)| 8.00 +[1,2)|[2025-04-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(4 rows) + + +starting permutation: s1rr s2rr s1upd2025 s1c s2del20252026 s2c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2del20252026: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2025-06-01)| 8.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rr s2rr s2del2027 s1upd2025 s2c s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2del2027: + DELETE FROM products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + WHERE id = '[1,2)'; + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1upd2025: <... completed> +ERROR: could not serialize access due to concurrent delete +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2027-01-01)| 5.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rr s2rr s2del202503 s1upd2025 s2c s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2del202503: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + WHERE id = '[1,2)'; + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1upd2025: <... completed> +ERROR: could not serialize access due to concurrent delete +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-03-01)| 5.00 +[1,2)|[2025-04-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rr s2rr s2del20252026 s1upd2025 s2c s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2del20252026: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + WHERE id = '[1,2)'; + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1upd2025: <... completed> +ERROR: could not serialize access due to concurrent delete +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-06-01)| 5.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rr s2rr s1q s2del2027 s2c s1upd2025 s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2del2027: + DELETE FROM products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +ERROR: could not serialize access due to concurrent delete +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2027-01-01)| 5.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rr s2rr s1q s2del202503 s2c s1upd2025 s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2del202503: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +ERROR: could not serialize access due to concurrent delete +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-03-01)| 5.00 +[1,2)|[2025-04-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rr s2rr s1q s2del20252026 s2c s1upd2025 s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2del20252026: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +ERROR: could not serialize access due to concurrent delete +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-06-01)| 5.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rr s2rr s1q s1upd2025 s1c s2del2027 s2c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2del2027: + DELETE FROM products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2027-01-01)| 5.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(4 rows) + + +starting permutation: s1rr s2rr s1q s1upd2025 s1c s2del202503 s2c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2del202503: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2025-03-01)| 8.00 +[1,2)|[2025-04-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(4 rows) + + +starting permutation: s1rr s2rr s1q s1upd2025 s1c s2del20252026 s2c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2del20252026: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2025-06-01)| 8.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rr s2rr s1q s2del2027 s1upd2025 s2c s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2del2027: + DELETE FROM products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + WHERE id = '[1,2)'; + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1upd2025: <... completed> +ERROR: could not serialize access due to concurrent delete +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2027-01-01)| 5.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rr s2rr s1q s2del202503 s1upd2025 s2c s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2del202503: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + WHERE id = '[1,2)'; + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1upd2025: <... completed> +ERROR: could not serialize access due to concurrent delete +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-03-01)| 5.00 +[1,2)|[2025-04-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rr s2rr s1q s2del20252026 s1upd2025 s2c s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2del20252026: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + WHERE id = '[1,2)'; + +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1upd2025: <... completed> +ERROR: could not serialize access due to concurrent delete +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-06-01)| 5.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rr s2rr s2upd2027 s2c s1del2025 s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2upd2027: + UPDATE products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-01-01,2027-01-01)| 5.00 +[1,2)|[2027-01-01,2028-01-01)|10.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(4 rows) + + +starting permutation: s1rr s2rr s2upd202503 s2c s1del2025 s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2upd202503: + UPDATE products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rr s2rr s2upd20252026 s2c s1del2025 s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2upd20252026: + UPDATE products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-01-01,2026-06-01)|10.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rr s2rr s1del2025 s1c s2upd2027 s2c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2upd2027: + UPDATE products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-01-01,2027-01-01)| 5.00 +[1,2)|[2027-01-01,2028-01-01)|10.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(4 rows) + + +starting permutation: s1rr s2rr s1del2025 s1c s2upd202503 s2c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2upd202503: + UPDATE products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rr s2rr s1del2025 s1c s2upd20252026 s2c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2upd20252026: + UPDATE products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-01-01,2026-06-01)|10.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rr s2rr s2upd2027 s1del2025 s2c s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2upd2027: + UPDATE products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1del2025: <... completed> +ERROR: could not serialize access due to concurrent update +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2027-01-01)| 5.00 +[1,2)|[2027-01-01,2028-01-01)|10.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rr s2rr s2upd202503 s1del2025 s2c s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2upd202503: + UPDATE products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1del2025: <... completed> +ERROR: could not serialize access due to concurrent update +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-03-01)| 5.00 +[1,2)|[2025-03-01,2025-04-01)|10.00 +[1,2)|[2025-04-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rr s2rr s2upd20252026 s1del2025 s2c s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2upd20252026: + UPDATE products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1del2025: <... completed> +ERROR: could not serialize access due to concurrent update +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-06-01)| 5.00 +[1,2)|[2025-06-01,2026-06-01)|10.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rr s2rr s1q s2upd2027 s2c s1del2025 s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2upd2027: + UPDATE products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +ERROR: could not serialize access due to concurrent update +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2027-01-01)| 5.00 +[1,2)|[2027-01-01,2028-01-01)|10.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rr s2rr s1q s2upd202503 s2c s1del2025 s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2upd202503: + UPDATE products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +ERROR: could not serialize access due to concurrent update +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-03-01)| 5.00 +[1,2)|[2025-03-01,2025-04-01)|10.00 +[1,2)|[2025-04-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rr s2rr s1q s2upd20252026 s2c s1del2025 s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2upd20252026: + UPDATE products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +ERROR: could not serialize access due to concurrent update +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-06-01)| 5.00 +[1,2)|[2025-06-01,2026-06-01)|10.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rr s2rr s1q s1del2025 s1c s2upd2027 s2c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2upd2027: + UPDATE products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-01-01,2027-01-01)| 5.00 +[1,2)|[2027-01-01,2028-01-01)|10.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(4 rows) + + +starting permutation: s1rr s2rr s1q s1del2025 s1c s2upd202503 s2c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2upd202503: + UPDATE products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rr s2rr s1q s1del2025 s1c s2upd20252026 s2c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2upd20252026: + UPDATE products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-01-01,2026-06-01)|10.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rr s2rr s1q s2upd2027 s1del2025 s2c s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2upd2027: + UPDATE products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1del2025: <... completed> +ERROR: could not serialize access due to concurrent update +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2027-01-01)| 5.00 +[1,2)|[2027-01-01,2028-01-01)|10.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rr s2rr s1q s2upd202503 s1del2025 s2c s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2upd202503: + UPDATE products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1del2025: <... completed> +ERROR: could not serialize access due to concurrent update +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-03-01)| 5.00 +[1,2)|[2025-03-01,2025-04-01)|10.00 +[1,2)|[2025-04-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rr s2rr s1q s2upd20252026 s1del2025 s2c s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2upd20252026: + UPDATE products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1del2025: <... completed> +ERROR: could not serialize access due to concurrent update +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-06-01)| 5.00 +[1,2)|[2025-06-01,2026-06-01)|10.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rr s2rr s2del2027 s2c s1del2025 s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2del2027: + DELETE FROM products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-01-01,2027-01-01)| 5.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rr s2rr s2del202503 s2c s1del2025 s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2del202503: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rr s2rr s2del20252026 s2c s1del2025 s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2del20252026: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rr s2rr s1del2025 s1c s2del2027 s2c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2del2027: + DELETE FROM products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-01-01,2027-01-01)| 5.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rr s2rr s1del2025 s1c s2del202503 s2c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2del202503: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rr s2rr s1del2025 s1c s2del20252026 s2c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2del20252026: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rr s2rr s2del2027 s1del2025 s2c s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2del2027: + DELETE FROM products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + WHERE id = '[1,2)'; + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1del2025: <... completed> +ERROR: could not serialize access due to concurrent delete +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2027-01-01)| 5.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rr s2rr s2del202503 s1del2025 s2c s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2del202503: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + WHERE id = '[1,2)'; + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1del2025: <... completed> +ERROR: could not serialize access due to concurrent delete +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-03-01)| 5.00 +[1,2)|[2025-04-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rr s2rr s2del20252026 s1del2025 s2c s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2del20252026: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + WHERE id = '[1,2)'; + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1del2025: <... completed> +ERROR: could not serialize access due to concurrent delete +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-06-01)| 5.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rr s2rr s1q s2del2027 s2c s1del2025 s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2del2027: + DELETE FROM products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +ERROR: could not serialize access due to concurrent delete +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2027-01-01)| 5.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rr s2rr s1q s2del202503 s2c s1del2025 s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2del202503: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +ERROR: could not serialize access due to concurrent delete +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-03-01)| 5.00 +[1,2)|[2025-04-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rr s2rr s1q s2del20252026 s2c s1del2025 s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2del20252026: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +ERROR: could not serialize access due to concurrent delete +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-06-01)| 5.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rr s2rr s1q s1del2025 s1c s2del2027 s2c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2del2027: + DELETE FROM products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-01-01,2027-01-01)| 5.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(3 rows) + + +starting permutation: s1rr s2rr s1q s1del2025 s1c s2del202503 s2c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2del202503: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-01-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rr s2rr s1q s1del2025 s1c s2del20252026 s2c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s2del20252026: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rr s2rr s1q s2del2027 s1del2025 s2c s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2del2027: + DELETE FROM products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + WHERE id = '[1,2)'; + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1del2025: <... completed> +ERROR: could not serialize access due to concurrent delete +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2027-01-01)| 5.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rr s2rr s1q s2del202503 s1del2025 s2c s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2del202503: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + WHERE id = '[1,2)'; + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1del2025: <... completed> +ERROR: could not serialize access due to concurrent delete +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-03-01)| 5.00 +[1,2)|[2025-04-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1rr s2rr s1q s2del20252026 s1del2025 s2c s1c s1q +step s1rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s2rr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2030-01-01)| 5.00 +(1 row) + +step s2del20252026: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + WHERE id = '[1,2)'; + +step s1del2025: + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; + <waiting ...> +step s2c: COMMIT; +step s1del2025: <... completed> +ERROR: could not serialize access due to concurrent delete +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-06-01)| 5.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(2 rows) + + +starting permutation: s1ser s2ser s2upd2027 s2c s1upd2025 s1c s1q +step s1ser: BEGIN ISOLATION LEVEL SERIALIZABLE; +step s2ser: BEGIN ISOLATION LEVEL SERIALIZABLE; +step s2upd2027: + UPDATE products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2027-01-01)| 5.00 +[1,2)|[2027-01-01,2028-01-01)|10.00 +[1,2)|[2028-01-01,2030-01-01)| 5.00 +(5 rows) + + +starting permutation: s1ser s2ser s2upd20252026 s2c s1upd2025 s1c s1q +step s1ser: BEGIN ISOLATION LEVEL SERIALIZABLE; +step s2ser: BEGIN ISOLATION LEVEL SERIALIZABLE; +step s2upd20252026: + UPDATE products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + SET price = 10.00 + WHERE id = '[1,2)'; + +step s2c: COMMIT; +step s1upd2025: + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; + +step s1c: COMMIT; +step s1q: SELECT * FROM products ORDER BY id, valid_at; +id |valid_at |price +-----+-----------------------+----- +[1,2)|[2020-01-01,2025-01-01)| 5.00 +[1,2)|[2025-01-01,2025-06-01)| 8.00 +[1,2)|[2025-06-01,2026-01-01)| 8.00 +[1,2)|[2026-01-01,2026-06-01)|10.00 +[1,2)|[2026-06-01,2030-01-01)| 5.00 +(5 rows) + diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule index c1a999bf1d2..1578ba191c8 100644 --- a/src/test/isolation/isolation_schedule +++ b/src/test/isolation/isolation_schedule @@ -125,3 +125,4 @@ test: serializable-parallel-2 test: serializable-parallel-3 test: matview-write-skew test: lock-nowait +test: for-portion-of diff --git a/src/test/isolation/specs/for-portion-of.spec b/src/test/isolation/specs/for-portion-of.spec new file mode 100644 index 00000000000..1ab9b257551 --- /dev/null +++ b/src/test/isolation/specs/for-portion-of.spec @@ -0,0 +1,597 @@ +# UPDATE/DELETE FOR PORTION OF test +# +# Test inserting temporal leftovers from a FOR PORTION OF update/delete. +# +# In READ COMMITTED mode, concurrent updates/deletes to the same records cause +# weird results. Portions of history that should have been updated/deleted don't +# get changed. That's because the leftovers from one operation are added too +# late to be seen by the other. EvalPlanQual will reload the changed-in-common +# row, but it won't re-scan to find new leftovers. +# +# MariaDB similarly gives undesirable results in READ COMMITTED mode (although +# not the same results). DB2 doesn't have READ COMMITTED, but it gives correct +# results at all levels, in particular READ STABILITY (which seems closest). +# +# A workaround is to lock the part of history you want before changing it (using +# SELECT FOR UPDATE). That way the search for rows is late enough to see +# leftovers from the other session(s). This shouldn't impose any new deadlock +# risks, since the locks are the same as before. Adding a third/fourth/etc. +# connection also doesn't change the semantics. The READ COMMITTED tests here +# demonstrate the problem and also show that solving it with manual locks is +# viable and not vitiated by any bugs. Incidentally, this approach also works in +# MariaDB. +# +# We run the same tests under REPEATABLE READ to show the problem goes away. +# In general they do what you'd want with no explicit locking required, but some +# orderings raise a concurrent update/delete failure (as expected). If there is +# a prior read by s1, concurrent update/delete failures are more common. +# +# To save on test time, we only run a couple SERIALIZABLE tests (for the more +# problematic permutations). +# +# We test updates where s2 updates history that is: +# +# - non-overlapping with s1, +# - contained entirely in s1, +# - partly contained in s1. +# +# We don't need to test where s2 entirely contains s1 because of symmetry: +# we test both when s1 precedes s2 and when s2 precedes s1, so that scenario is +# covered. +# +# We test various orderings of the update/delete/commit from s1 and s2. +# Note that `s1lock s2lock s1change` is boring because it's the same as +# `s1lock s1change s2lock`. In other words it doesn't matter if something +# interposes between the lock and its change (as long as everyone is following +# the same policy). + +setup +{ + CREATE TABLE products ( + id int4range NOT NULL, + valid_at daterange NOT NULL, + price decimal NOT NULL, + PRIMARY KEY (id, valid_at WITHOUT OVERLAPS)); + INSERT INTO products VALUES + ('[1,2)', '[2020-01-01,2030-01-01)', 5.00); +} + +teardown { DROP TABLE products; } + +session s1 +setup { SET datestyle TO ISO, YMD; } +step s1rc { BEGIN ISOLATION LEVEL READ COMMITTED; } +step s1rr { BEGIN ISOLATION LEVEL REPEATABLE READ; } +step s1ser { BEGIN ISOLATION LEVEL SERIALIZABLE; } +step s1lock2025 { + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-01-01,2026-01-01)' + ORDER BY valid_at FOR UPDATE; +} +step s1upd2025 { + UPDATE products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + SET price = 8.00 + WHERE id = '[1,2)'; +} +step s1del2025 { + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-01-01' TO '2026-01-01' + WHERE id = '[1,2)'; +} +step s1q { SELECT * FROM products ORDER BY id, valid_at; } +step s1c { COMMIT; } + +session s2 +setup { SET datestyle TO ISO, YMD; } +step s2rc { BEGIN ISOLATION LEVEL READ COMMITTED; } +step s2rr { BEGIN ISOLATION LEVEL REPEATABLE READ; } +step s2ser { BEGIN ISOLATION LEVEL SERIALIZABLE; } +step s2lock202503 { + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-03-01,2025-04-01)' + ORDER BY valid_at FOR UPDATE; +} +step s2lock20252026 { + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2025-06-01,2026-06-01)' + ORDER BY valid_at FOR UPDATE; +} +step s2lock2027 { + SELECT * FROM products + WHERE id = '[1,2)' AND valid_at && '[2027-01-01,2028-01-01)' + ORDER BY valid_at FOR UPDATE; +} +step s2upd202503 { + UPDATE products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + SET price = 10.00 + WHERE id = '[1,2)'; +} +step s2upd20252026 { + UPDATE products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + SET price = 10.00 + WHERE id = '[1,2)'; +} +step s2upd2027 { + UPDATE products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + SET price = 10.00 + WHERE id = '[1,2)'; +} +step s2del202503 { + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-04-01' + WHERE id = '[1,2)'; +} +step s2del20252026 { + DELETE FROM products + FOR PORTION OF valid_at FROM '2025-06-01' TO '2026-06-01' + WHERE id = '[1,2)'; +} +step s2del2027 { + DELETE FROM products + FOR PORTION OF valid_at FROM '2027-01-01' TO '2028-01-01' + WHERE id = '[1,2)'; +} +step s2c { COMMIT; } + +# ######################################## +# READ COMMITTED tests, UPDATE+UPDATE: +# ######################################## + +# s1 sees the leftovers +permutation s1rc s2rc s2lock2027 s2upd2027 s2c s1lock2025 s1upd2025 s1c s1q + +# s1 reloads the updated row and sees its leftovers +permutation s1rc s2rc s2lock202503 s2upd202503 s2c s1lock2025 s1upd2025 s1c s1q + +# s1 reloads the updated row and sees its leftovers +permutation s1rc s2rc s2lock20252026 s2upd20252026 s2c s1lock2025 s1upd2025 s1c s1q + +# s2 sees the leftovers +permutation s1rc s2rc s1lock2025 s1upd2025 s1c s2lock2027 s2upd2027 s2c s1q + +# s2 loads the updated row +permutation s1rc s2rc s1lock2025 s1upd2025 s1c s2lock202503 s2upd202503 s2c s1q + +# s2 loads the updated row and sees its leftovers +permutation s1rc s2rc s1lock2025 s1upd2025 s1c s2lock20252026 s2upd20252026 s2c s1q + +# Problem: +# s1 (without locking) overlooks the leftovers from s2 +# and EvalPlanQual no longer matches the row to be updated either. +permutation s1rc s2rc s2upd2027 s1upd2025 s2c s1c s1q + +# Workaround: +# s1 updates the leftovers from s2 +# Locking is required or s1 won't see the leftovers. +permutation s1rc s2rc s2lock2027 s2upd2027 s1lock2025 s2c s1upd2025 s1c s1q + +# Problem: +# s1 (without locking) overlooks the leftovers from s2 +# but EvalPlanQual still matches the row to be updated. +permutation s1rc s2rc s2upd202503 s1upd2025 s2c s1c s1q + +# Workaround: +# s1 overwrites the row from s2 and sees its leftovers +permutation s1rc s2rc s2lock202503 s2upd202503 s1lock2025 s2c s1upd2025 s1c s1q + +# Problem: +# s1 (without locking) overlooks the leftovers from s2 +# but EvalPlanQual still matches the row to be updated, +# and s1's leftovers don't conflict with s2's. +permutation s1rc s2rc s2upd20252026 s1upd2025 s2c s1c s1q + +# Workaround: +# s1 overwrites the row from s2 and sees its leftovers +# Locking is required or s1 won't see the leftovers. +permutation s1rc s2rc s2lock20252026 s2upd20252026 s1lock2025 s2c s1upd2025 s1c s1q + +# ######################################## +# READ COMMITTED tests, UPDATE+DELETE: +# ######################################## + +# s1 sees the leftovers +permutation s1rc s2rc s2lock2027 s2del2027 s2c s1lock2025 s1upd2025 s1c s1q + +# s1 ignores the deleted row and sees its leftovers +permutation s1rc s2rc s2lock202503 s2del202503 s2c s1lock2025 s1upd2025 s1c s1q + +# s1 ignores the deleted row and sees its leftovers +permutation s1rc s2rc s2lock20252026 s2del20252026 s2c s1lock2025 s1upd2025 s1c s1q + +# s2 sees the leftovers +permutation s1rc s2rc s1lock2025 s1upd2025 s1c s2lock2027 s2del2027 s2c s1q + +# s2 loads the updated row +permutation s1rc s2rc s1lock2025 s1upd2025 s1c s2lock202503 s2del202503 s2c s1q + +# s2 loads the updated row and sees its leftovers +permutation s1rc s2rc s1lock2025 s1upd2025 s1c s2lock20252026 s2del20252026 s2c s1q + +# Problem: +# s1 (without locking) overlooks the leftovers from s2 +# and EvalPlanQual no longer matches the row to be updated either. +permutation s1rc s2rc s2del2027 s1upd2025 s2c s1c s1q + +# Workaround: +# s1 updates the leftovers from s2 +# Locking is required or s1 won't see the leftovers. +permutation s1rc s2rc s2lock2027 s2del2027 s1lock2025 s2c s1upd2025 s1c s1q + +# Problem: +# s1 (without locking) overlooks the leftovers from s2 +# and EvalPlanQual no longer matches the row to be updated either. +permutation s1rc s2rc s2del202503 s1upd2025 s2c s1c s1q + +# Workaround: +# s1 sees the leftovers from s2 +# Locking is required or s1 won't see the leftovers. +permutation s1rc s2rc s2lock202503 s2del202503 s1lock2025 s2c s1upd2025 s1c s1q + +# Problem: +# s1 (without locking) overlooks the leftovers from s2 +# and EvalPlanQual no longer matches the row to be updated either. +permutation s1rc s2rc s2del20252026 s1upd2025 s2c s1c s1q + +# Workaround: +# s1 sees the leftovers from s2 +# Locking is required or s1 won't see the leftovers. +permutation s1rc s2rc s2lock20252026 s2del20252026 s1lock2025 s2c s1upd2025 s1c s1q + +# ######################################## +# READ COMMITTED tests, DELETE+UPDATE: +# ######################################## + +# s1 sees the leftovers +permutation s1rc s2rc s2lock2027 s2upd2027 s2c s1lock2025 s1del2025 s1c s1q + +# s1 reloads the updated row and sees its leftovers +permutation s1rc s2rc s2lock202503 s2upd202503 s2c s1lock2025 s1del2025 s1c s1q + +# s1 reloads the updated row and sees its leftovers +permutation s1rc s2rc s2lock20252026 s2upd20252026 s2c s1lock2025 s1del2025 s1c s1q + +# s2 sees the leftovers +permutation s1rc s2rc s1lock2025 s1del2025 s1c s2lock2027 s2upd2027 s2c s1q + +# s2 ignores the deleted row +permutation s1rc s2rc s1lock2025 s1del2025 s1c s2lock202503 s2upd202503 s2c s1q + +# s2 ignores the deleted row and sees its leftovers +permutation s1rc s2rc s1lock2025 s1del2025 s1c s2lock20252026 s2upd20252026 s2c s1q + +# Problem: +# s1 (without locking) overlooks the leftovers from s2 +# and EvalPlanQual no longer matches the row to be deleted either. +permutation s1rc s2rc s2upd2027 s1del2025 s2c s1c s1q + +# Workaround: +# s1 deletes the leftovers from s2 +# Locking is required or s1 won't see the leftovers. +permutation s1rc s2rc s2lock2027 s2upd2027 s1lock2025 s2c s1del2025 s1c s1q + +# Problem: +# s1 (without locking) overlooks the leftovers from s2 +# but EvalPlanQual still matches the row to be deleted. +permutation s1rc s2rc s2upd202503 s1del2025 s2c s1c s1q + +# Workaround: +# s1 deletes the new row from s2 and its leftovers +# Locking is required or s1 won't see the leftovers. +permutation s1rc s2rc s2lock202503 s2upd202503 s1lock2025 s2c s1del2025 s1c s1q + +# Problem: +# s1 (without locking) overlooks the leftovers from s2 +# but EvalPlanQual still matches the row to be deleted, +# and s1 leaves leftovers from the row created by s2. +permutation s1rc s2rc s2upd20252026 s1del2025 s2c s1c s1q + +# Workaround: +# s1 deletes the new row from s2 and its leftovers +# Locking is required or s1 won't see the leftovers. +permutation s1rc s2rc s2lock20252026 s2upd20252026 s1lock2025 s2c s1del2025 s1c s1q + +# ######################################## +# READ COMMITTED tests, DELETE+DELETE: +# ######################################## + +# s1 sees the leftovers +permutation s1rc s2rc s2lock2027 s2del2027 s2c s1lock2025 s1del2025 s1c s1q + +# s1 ignores the deleted row and sees its leftovers +permutation s1rc s2rc s2lock202503 s2del202503 s2c s1lock2025 s1del2025 s1c s1q + +# s1 ignores the deleted row and sees its leftovers +permutation s1rc s2rc s2lock20252026 s2del20252026 s2c s1lock2025 s1del2025 s1c s1q + +# s2 sees the leftovers +permutation s1rc s2rc s1lock2025 s1del2025 s1c s2lock2027 s2del2027 s2c s1q + +# s2 ignores the deleted row +permutation s1rc s2rc s1lock2025 s1del2025 s1c s2lock202503 s2del202503 s2c s1q + +# s2 ignores the deleted row and sees its leftovers +permutation s1rc s2rc s1lock2025 s1del2025 s1c s2lock20252026 s2del20252026 s2c s1q + +# Problem: +# s1 (without locking) overlooks the leftovers from s2 +# and EvalPlanQual no longer matches the row to be deleted either. +permutation s1rc s2rc s2del2027 s1del2025 s2c s1c s1q + +# Workaround: +# s1 deletes the leftovers from s2 +# Locking is required or s1 won't see the leftovers. +permutation s1rc s2rc s2lock2027 s2del2027 s1lock2025 s2c s1del2025 s1c s1q + +# Problem: +# s1 (without locking) overlooks the leftovers from s2 +# and EvalPlanQual no longer matches the row to be deleted either. +permutation s1rc s2rc s2del202503 s1del2025 s2c s1c s1q + +# Workaround: +# s1 deletes the leftovers from s2 +# Locking is required or s1 won't see the leftovers. +permutation s1rc s2rc s2lock202503 s2del202503 s1lock2025 s2c s1del2025 s1c s1q + +# Problem: +# s1 (without locking) overlooks the leftovers from s2 +# and EvalPlanQual no longer matches the row to be deleted either. +permutation s1rc s2rc s2del20252026 s1del2025 s2c s1c s1q + +# Workaround: +# s1 deletes the leftovers from s2 +# Locking is required or s1 won't see the leftovers. +permutation s1rc s2rc s2lock20252026 s2del20252026 s1lock2025 s2c s1del2025 s1c s1q + +# ######################################## +# REPEATABLE READ tests, UPDATE+UPDATE: +# ######################################## + +# s1 sees the leftovers +permutation s1rr s2rr s2upd2027 s2c s1upd2025 s1c s1q + +# s1 reloads the updated row and sees its leftovers +permutation s1rr s2rr s2upd202503 s2c s1upd2025 s1c s1q + +# s1 reloads the updated row and sees its leftovers +permutation s1rr s2rr s2upd20252026 s2c s1upd2025 s1c s1q + +# s2 sees the leftovers +permutation s1rr s2rr s1upd2025 s1c s2upd2027 s2c s1q + +# s2 loads the updated row and sees its leftovers +permutation s1rr s2rr s1upd2025 s1c s2upd202503 s2c s1q + +# s2 loads the updated row and sees its leftovers +permutation s1rr s2rr s1upd2025 s1c s2upd20252026 s2c s1q + +# s1 fails from concurrent update +permutation s1rr s2rr s2upd2027 s1upd2025 s2c s1c s1q + +# s1 fails from concurrent update +permutation s1rr s2rr s2upd202503 s1upd2025 s2c s1c s1q + +# s1 fails from concurrent update +permutation s1rr s2rr s2upd20252026 s1upd2025 s2c s1c s1q + +## with prior read by s1: + +# s1 fails from concurrent update +permutation s1rr s2rr s1q s2upd2027 s2c s1upd2025 s1c s1q + +# s1 fails from concurrent update +permutation s1rr s2rr s1q s2upd202503 s2c s1upd2025 s1c s1q + +# s1 fails from concurrent update +permutation s1rr s2rr s1q s2upd20252026 s2c s1upd2025 s1c s1q + +# s2 sees the leftovers +permutation s1rr s2rr s1q s1upd2025 s1c s2upd2027 s2c s1q + +# s2 loads the updated row +permutation s1rr s2rr s1q s1upd2025 s1c s2upd202503 s2c s1q + +# s2 loads the updated row and sees its leftovers +permutation s1rr s2rr s1q s1upd2025 s1c s2upd20252026 s2c s1q + +# s1 fails from concurrent update +permutation s1rr s2rr s1q s2upd2027 s1upd2025 s2c s1c s1q + +# s1 fails from concurrent update +permutation s1rr s2rr s1q s2upd202503 s1upd2025 s2c s1c s1q + +# s1 fails from concurrent update +permutation s1rr s2rr s1q s2upd20252026 s1upd2025 s2c s1c s1q + +# ######################################## +# REPEATABLE READ tests, UPDATE+DELETE: +# ######################################## + +# s1 sees the leftovers +permutation s1rr s2rr s2del2027 s2c s1upd2025 s1c s1q + +# s1 ignores the deleted row and sees its leftovers +permutation s1rr s2rr s2del202503 s2c s1upd2025 s1c s1q + +# s1 ignores the deleted row and sees its leftovers +permutation s1rr s2rr s2del20252026 s2c s1upd2025 s1c s1q + +# s2 sees the leftovers +permutation s1rr s2rr s1upd2025 s1c s2del2027 s2c s1q + +# s2 loads the updated row +permutation s1rr s2rr s1upd2025 s1c s2del202503 s2c s1q + +# s2 loads the updated row and sees its leftovers +permutation s1rr s2rr s1upd2025 s1c s2del20252026 s2c s1q + +# s1 fails from concurrent delete +permutation s1rr s2rr s2del2027 s1upd2025 s2c s1c s1q + +# s1 fails from concurrent delete +permutation s1rr s2rr s2del202503 s1upd2025 s2c s1c s1q + +# s1 fails from concurrent delete +permutation s1rr s2rr s2del20252026 s1upd2025 s2c s1c s1q + +## with prior read by s1: + +# s1 fails from concurrent delete +permutation s1rr s2rr s1q s2del2027 s2c s1upd2025 s1c s1q + +# s1 fails from concurrent delete +permutation s1rr s2rr s1q s2del202503 s2c s1upd2025 s1c s1q + +# s1 fails from concurrent delete +permutation s1rr s2rr s1q s2del20252026 s2c s1upd2025 s1c s1q + +# s2 sees the leftovers +permutation s1rr s2rr s1q s1upd2025 s1c s2del2027 s2c s1q + +# s2 loads the updated row +permutation s1rr s2rr s1q s1upd2025 s1c s2del202503 s2c s1q + +# s2 loads the updated row and sees its leftovers +permutation s1rr s2rr s1q s1upd2025 s1c s2del20252026 s2c s1q + +# s1 fails from concurrent delete +permutation s1rr s2rr s1q s2del2027 s1upd2025 s2c s1c s1q + +# s1 fails from concurrent delete +permutation s1rr s2rr s1q s2del202503 s1upd2025 s2c s1c s1q + +# s1 fails from concurrent delete +permutation s1rr s2rr s1q s2del20252026 s1upd2025 s2c s1c s1q + +# ######################################## +# REPEATABLE READ tests, DELETE+UPDATE: +# ######################################## + +# s1 sees the leftovers +permutation s1rr s2rr s2upd2027 s2c s1del2025 s1c s1q + +# s1 reloads the updated row and sees its leftovers +permutation s1rr s2rr s2upd202503 s2c s1del2025 s1c s1q + +# s1 reloads the updated row and sees its leftovers +permutation s1rr s2rr s2upd20252026 s2c s1del2025 s1c s1q + +# s2 sees the leftovers +permutation s1rr s2rr s1del2025 s1c s2upd2027 s2c s1q + +# s2 ignores the deleted row +permutation s1rr s2rr s1del2025 s1c s2upd202503 s2c s1q + +# s2 ignores the deleted row and sees its leftovers +permutation s1rr s2rr s1del2025 s1c s2upd20252026 s2c s1q + +# s1 fails from concurrent update +permutation s1rr s2rr s2upd2027 s1del2025 s2c s1c s1q + +# s1 fails from concurrent update +permutation s1rr s2rr s2upd202503 s1del2025 s2c s1c s1q + +# s1 fails from concurrent update +permutation s1rr s2rr s2upd20252026 s1del2025 s2c s1c s1q + +## with prior read by s1: + +# s1 fails from concurrent update +permutation s1rr s2rr s1q s2upd2027 s2c s1del2025 s1c s1q + +# s1 fails from concurrent update +permutation s1rr s2rr s1q s2upd202503 s2c s1del2025 s1c s1q + +# s1 fails from concurrent update +permutation s1rr s2rr s1q s2upd20252026 s2c s1del2025 s1c s1q + +# s2 sees the leftovers +permutation s1rr s2rr s1q s1del2025 s1c s2upd2027 s2c s1q + +# s2 ignores the deleted row +permutation s1rr s2rr s1q s1del2025 s1c s2upd202503 s2c s1q + +# s2 ignores the deleted row and sees its leftovers +permutation s1rr s2rr s1q s1del2025 s1c s2upd20252026 s2c s1q + +# s1 fails from concurrent update +permutation s1rr s2rr s1q s2upd2027 s1del2025 s2c s1c s1q + +# s1 fails from concurrent update +permutation s1rr s2rr s1q s2upd202503 s1del2025 s2c s1c s1q + +# s1 fails from concurrent update +permutation s1rr s2rr s1q s2upd20252026 s1del2025 s2c s1c s1q + +# ######################################## +# REPEATABLE READ tests, DELETE+DELETE: +# ######################################## + +# s1 sees the leftovers +permutation s1rr s2rr s2del2027 s2c s1del2025 s1c s1q + +# s1 reloads the updated row and sees its leftovers +permutation s1rr s2rr s2del202503 s2c s1del2025 s1c s1q + +# s1 reloads the updated row and sees its leftovers +permutation s1rr s2rr s2del20252026 s2c s1del2025 s1c s1q + +# s2 sees the leftovers +permutation s1rr s2rr s1del2025 s1c s2del2027 s2c s1q + +# s2 ignores the deleted row +permutation s1rr s2rr s1del2025 s1c s2del202503 s2c s1q + +# s2 ignores the deleted row and sees its leftovers +permutation s1rr s2rr s1del2025 s1c s2del20252026 s2c s1q + +# s1 fails from concurrent delete +permutation s1rr s2rr s2del2027 s1del2025 s2c s1c s1q + +# s1 fails from concurrent delete +permutation s1rr s2rr s2del202503 s1del2025 s2c s1c s1q + +# s1 fails from concurrent delete +permutation s1rr s2rr s2del20252026 s1del2025 s2c s1c s1q + +## with prior read by s1: + +# s1 fails from concurrent delete +permutation s1rr s2rr s1q s2del2027 s2c s1del2025 s1c s1q + +# s1 fails from concurrent delete +permutation s1rr s2rr s1q s2del202503 s2c s1del2025 s1c s1q + +# s1 fails from concurrent delete +permutation s1rr s2rr s1q s2del20252026 s2c s1del2025 s1c s1q + +# s2 sees the leftovers +permutation s1rr s2rr s1q s1del2025 s1c s2del2027 s2c s1q + +# s2 ignores the deleted row +permutation s1rr s2rr s1q s1del2025 s1c s2del202503 s2c s1q + +# s2 ignores the deleted row and sees its leftovers +permutation s1rr s2rr s1q s1del2025 s1c s2del20252026 s2c s1q + +# s1 fails from concurrent delete +permutation s1rr s2rr s1q s2del2027 s1del2025 s2c s1c s1q + +# s1 fails from concurrent delete +permutation s1rr s2rr s1q s2del202503 s1del2025 s2c s1c s1q + +# s1 fails from concurrent delete +permutation s1rr s2rr s1q s2del20252026 s1del2025 s2c s1c s1q + +# ######################################## +# SERIALIZABLE tests, UPDATE+UPDATE: +# ######################################## + +# s1 sees the leftovers +permutation s1ser s2ser s2upd2027 s2c s1upd2025 s1c s1q + +# s1 reloads the updated row and sees its leftovers +permutation s1ser s2ser s2upd20252026 s2c s1upd2025 s1c s1q [parent: 5bcc3fbd196c]