Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wLdBY-001ujq-1H for pgsql-hackers@arkaria.postgresql.org; Sat, 09 May 2026 08:37:20 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wLdBU-00Cms6-1N for pgsql-hackers@arkaria.postgresql.org; Sat, 09 May 2026 08:37:16 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wLdBT-00Cmrw-2T for pgsql-hackers@lists.postgresql.org; Sat, 09 May 2026 08:37:16 +0000 Received: from mail-pl1-x62c.google.com ([2607:f8b0:4864:20::62c]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1wLdBO-00000000xN9-0NlL for pgsql-hackers@postgresql.org; Sat, 09 May 2026 08:37:14 +0000 Received: by mail-pl1-x62c.google.com with SMTP id d9443c01a7336-2bc78d56d3aso2717675ad.2 for ; Sat, 09 May 2026 01:37:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778315830; x=1778920630; darn=postgresql.org; h=references:to:cc:in-reply-to:date:subject:mime-version:message-id :from:from:to:cc:subject:date:message-id:reply-to; bh=QCCNIAeXFnzhWeYvHFTyS45wJQfh3FYcsP5tw1zTCy4=; b=cYQTPv++1XqQpN/f7Gcc0llQKMLjhrNGYebbGO81gUM6iT6BTZa264hGL/NISKA+LW jjuczqUIxS21GNjyhLRtZXhOzUCG8CMx9QrDYnsbzLYWabaqv2IsieIJMWXtnRJSqxHe z2Lk6D7YRSy3a2UzNHXaVIurPEam/437Ick+NaGP+JVMIW7FHOCrQNDLTH2gw54izJg+ i5jMDfB/KhEci2bDi8yaQB3INIMVJVnQGwfDfMNcVAolPdoxjmrJhA/j7ZVRfzRibOf8 rbiuH55jl/NcyPsGYv3hxf6UzTGgh51WU1UsJvEaqStxWRCK1PnrtvE1Rg2Ux9OhHFBa VIZA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778315830; x=1778920630; h=references:to:cc:in-reply-to:date:subject:mime-version:message-id :from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=QCCNIAeXFnzhWeYvHFTyS45wJQfh3FYcsP5tw1zTCy4=; b=glQV1gEQ/RiWZvIKnegT+Y2J1VSvP6hDMshqrYAM6GaVHKJ98YKoqWi2fOQmZ5I1Hl QzEPTEf9UUk6V9ppdQdT9i2XJfdx258AWBFqsJ4JnpA6RrVjsSeSL+xanFCSW6hUCcKX +G09uRhLjBfKNoyAUGU2EEzs/9oKxtjt9R+sFk+kAXmYhcdznjlbgL2JYqkNSoErrIR1 KbXrd0alYUy0dKwrBiE/ElDfgSHJ/T79tCXzsYaDLV6DNjCZwjXeg1ff6l6EDz5OSqdO XscfdWGRKgIBMvR5hGUtFc76QhkiET2fM/iF/8jJ8QmdJWYo4z9JLMfsa3qCghgjrcnL 5zgQ== X-Gm-Message-State: AOJu0YyKdIpFKs9+UJ/XAHiPHnpj+Qdi0BzxvGOc0vL+WtgWeJ5Z994R auNz120ISceqKms2yKFUgkhb8jJ13bYMv0gg+0ZDK8WB2jIzxai1SCsk X-Gm-Gg: Acq92OHDW3G73GjD6Ys+UjQAzCf5q7PCiwS3E8MwCeYTZifJfgsL81OfajcC7cuFk0z 7K3l0xXIvp9R6ewoPfv2/yHEJkl4OZ1RaLSUpwsypBfMnrDBxrNRfgWvgJNwyBzVTEIlJWjJHX2 3kazfNlqkEciU2LVeraM6LsK3JnOTDJBUJ4igVi96IZYOyh4yh6KNlR8EF8NoeaYFwPOe9sNYK5 b6c7NO8FcPAs1xloFSOZzbbUZUBgG/nQm+dKgL3uoznpjwvMgCzhDf2588yeIkPDyfJWvcB9A1W aWW8/OeplgRxJF6gv83MOiZR8gTtx9nQTDJ+jg8cMtQDUWZ1NfoSbMB/s2fRH5GyIC2PSbuscLX 2QvuZUM9KwHPmPGE9Zty5jxCeVmokJJvvvBO59nnN5VOUf/RS9rxWCJso7BsGT8+4IVJYoiJ4Zy LeXpwjuQicQzkOXQfQWpVRx4X3rbeEHumZKTI8bO/3gXaG0V9N X-Received: by 2002:a17:902:8641:b0:2ba:6ffa:bde0 with SMTP id d9443c01a7336-2ba79c20bb9mr129448435ad.19.1778315829483; Sat, 09 May 2026 01:37:09 -0700 (PDT) Received: from smtpclient.apple ([27.223.148.169]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2baf1e35632sm48101385ad.53.2026.05.09.01.37.07 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sat, 09 May 2026 01:37:08 -0700 (PDT) From: Chao Li Message-Id: Content-Type: multipart/mixed; boundary="Apple-Mail=_F3963B4C-E623-49EC-96D8-CE8A89FD8D0C" Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3864.400.21\)) Subject: Re: Fix REPACK with WITHOUT OVERLAPS replica identity indexes Date: Sat, 9 May 2026 16:36:34 +0800 In-Reply-To: Cc: PostgreSQL-development To: Kirill Reshke References: <7B0EC0EC-5461-41EF-9B31-F9BBE608DEA5@gmail.com> X-Mailer: Apple Mail (2.3864.400.21) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --Apple-Mail=_F3963B4C-E623-49EC-96D8-CE8A89FD8D0C Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=utf-8 > On May 9, 2026, at 01:47, Kirill Reshke = wrote: >=20 > On Fri, 8 May 2026 at 09:22, Chao Li wrote: >>=20 >> Hi, >>=20 >> While testing UPDATE FOR PORTION OF, I started wondering whether = REPACK supports temporal tables. In theory, it should, because temporal = WITHOUT OVERLAPS indexes can be used as replica identity indexes. So I = created a test script, repack_temporal.spec, which is included in the = attached patch, and it failed. >>=20 >> I found that REPACK hard-codes BTEqualStrategyNumber when calling = get_opfamily_member(). That seems wrong, because = build_replindex_scan_key() uses IndexAmTranslateCompareType() to get the = equality strategy for COMPARE_EQ. >>=20 >> After fixing the hard-coded BTEqualStrategyNumber, the temporal test = passed. Then I added another test for multirange, = repack_temporal_multirange.spec, which also failed. The reason is that = find_target_tuple() uses the identity index to find the first tuple and = returns it directly, but a lossy index scan may return false positives = and require recheck. >>=20 >> Please see the attached patch for the fix details and test scripts. >>=20 >> Best regards, >> -- >> Chao Li (Evan) >> HighGo Software Co., Ltd. >> https://www.highgo.com/ >>=20 >=20 > your analysis appears correct to me Hi Kirill, thanks for your review. >=20 >> + while (index_getnext_slot(scan, ForwardScanDirection, retrieved)) >> + { >> + if (scan->xs_recheck && !identity_key_equal(chgcxt, locator, = retrieved)) >> + continue; >> + >> + retval =3D true; >> + break; >> + } >=20 > Should we add CFI() ? >=20 Oh, I didn=E2=80=99t consider that at all, because I thought there = should not be a lot of candidate rows needing recheck. I am okay to add = that. >=20 > Also, do we really need isolation tests and inj points here? I think so. Without the injection point, the first phase of copying a = new heap would be very fast, it would be hard to run an update in the = second session. I think that=E2=80=99s way the repack code intentionally = added an injection point before the first round of replay: ``` /* * During testing, wait for another backend to perform concurrent data * changes which we will process below. */ INJECTION_POINT("repack-concurrently-before-lock", NULL); ``` > Doesn't a > simple regression test for REPACK execute the same code? >=20 It seems we intentionally avoid to run repack test in the regress test, = see [1] and [2]. PFA v2: added the CFI as Kirill suggested. [1] https://postgr.es/m/769631.1777575242@sss.pgh.pa.us [2] = https://git.postgresql.org/cgit/postgresql.git/commit/?id=3D2fd787d0aac1cb= 00a42ebce92ebb1d7534035ee3 Best regards, -- Chao Li (Evan) HighGo Software Co., Ltd. https://www.highgo.com/ --Apple-Mail=_F3963B4C-E623-49EC-96D8-CE8A89FD8D0C Content-Disposition: attachment; filename=v2-0001-Fix-REPACK-with-WITHOUT-OVERLAPS-replica-identity.patch Content-Type: application/octet-stream; x-unix-mode=0644; name="v2-0001-Fix-REPACK-with-WITHOUT-OVERLAPS-replica-identity.patch" Content-Transfer-Encoding: quoted-printable =46rom=20d480e5d6ee5bfd05ba59642fe62a25b1e7e66da6=20Mon=20Sep=2017=20= 00:00:00=202001=0AFrom:=20"Chao=20Li=20(Evan)"=20=0A= Date:=20Fri,=208=20May=202026=2011:40:18=20+0800=0ASubject:=20[PATCH=20= v2]=20Fix=20REPACK=20with=20WITHOUT=20OVERLAPS=20replica=20identity=20= indexes=0A=0AREPACK=20replay=20builds=20scan=20keys=20for=20the=20= replica=20identity=20index,=20but=20it=0Ahard-coded=20= BTEqualStrategyNumber=20when=20looking=20up=20the=20equality=20operator.=0A= That=20is=20not=20correct=20for=20non-btree=20identity=20indexes,=20such=20= as=20the=20GiST=0Aindexes=20created=20for=20WITHOUT=20OVERLAPS=20primary=20= keys.=20=20In=20addition,=0Afind_target_tuple()=20accepted=20the=20first=20= tuple=20returned=20by=20the=20identity=0Aindex=20scan,=20which=20is=20= unsafe=20for=20lossy=20index=20scans=20because=20the=20index=20AM=20may=0A= return=20false=20positives=20with=20xs_recheck=20set.=0A=0AFix=20this=20= by=20using=20IndexAmTranslateCompareType()=20to=20translate=20COMPARE_EQ=0A= to=20the=20equality=20strategy=20number=20for=20the=20index=20AM,=20and=20= by=20continuing=20the=0Ascan=20when=20recheck=20is=20required=20until=20= a=20candidate=20tuple=20matches=20the=20locator=0Atuple=20on=20all=20= replica=20identity=20key=20columns.=0A=0AThe=20recheck=20uses=20the=20= same=20equality=20operator=20functions=20as=20the=20identity=0Aindex=20= scan=20keys,=20preserving=20ScanKey=20argument=20ordering.=0A=0AAuthor:=20= Chao=20Li=20=0AReviewed-by:=20Kirill=20Reshke=20= =0ADiscussion:=20= https://postgr.es/m/7B0EC0EC-5461-41EF-9B31-F9BBE608DEA5@gmail.com=0A---=0A= =20src/backend/commands/repack.c=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20|=20=2066=20+++++++++++-=0A=20= src/test/modules/injection_points/Makefile=20=20=20=20|=20=20=202=20+=0A=20= .../expected/repack_temporal.out=20=20=20=20=20=20=20=20=20=20=20=20=20=20= |=20=2068=20++++++++++++=0A=20= .../expected/repack_temporal_multirange.out=20=20=20|=20=2074=20= +++++++++++++=0A=20src/test/modules/injection_points/meson.build=20|=20=20= =202=20+=0A=20.../specs/repack_temporal.spec=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20|=20=2090=20++++++++++++++++=0A=20= .../specs/repack_temporal_multirange.spec=20=20=20=20=20|=20102=20= ++++++++++++++++++=0A=207=20files=20changed,=20400=20insertions(+),=204=20= deletions(-)=0A=20create=20mode=20100644=20= src/test/modules/injection_points/expected/repack_temporal.out=0A=20= create=20mode=20100644=20= src/test/modules/injection_points/expected/repack_temporal_multirange.out=0A= =20create=20mode=20100644=20= src/test/modules/injection_points/specs/repack_temporal.spec=0A=20create=20= mode=20100644=20= src/test/modules/injection_points/specs/repack_temporal_multirange.spec=0A= =0Adiff=20--git=20a/src/backend/commands/repack.c=20= b/src/backend/commands/repack.c=0Aindex=209a199dd9bfb..42e5d9e7558=20= 100644=0A---=20a/src/backend/commands/repack.c=0A+++=20= b/src/backend/commands/repack.c=0A@@=20-182,6=20+182,9=20@@=20static=20= void=20adjust_toast_pointers(Relation=20relation,=20TupleTableSlot=20= *dest,=0A=20static=20bool=20find_target_tuple(Relation=20rel,=20= ChangeContext=20*chgcxt,=0A=20=09=09=09=09=09=09=09=20=20TupleTableSlot=20= *locator,=0A=20=09=09=09=09=09=09=09=20=20TupleTableSlot=20*retrieved);=0A= +static=20bool=20identity_key_equal(ChangeContext=20*chgcxt,=0A+=09=09=09= =09=09=09=09=20=20=20TupleTableSlot=20*locator,=0A+=09=09=09=09=09=09=09=20= =20=20TupleTableSlot=20*candidate);=0A=20static=20void=20= process_concurrent_changes(XLogRecPtr=20end_of_wal,=0A=20=09=09=09=09=09=09= =09=09=09=20=20=20ChangeContext=20*chgcxt,=0A=20=09=09=09=09=09=09=09=09=09= =20=20=20bool=20done);=0A@@=20-2807,7=20+2810,7=20@@=20= find_target_tuple(Relation=20rel,=20ChangeContext=20*chgcxt,=20= TupleTableSlot=20*locator,=0A=20{=0A=20=09Form_pg_index=20idx=20=3D=20= chgcxt->cc_ident_index->rd_index;=0A=20=09IndexScanDesc=20scan;=0A-=09= bool=09=09retval;=0A+=09bool=09=09retval=20=3D=20false;=0A=20=0A=20=09/*=0A= =20=09=20*=20Scan=20key=20is=20passed=20by=20caller,=20so=20it=20does=20= not=20have=20to=20be=20constructed=0A@@=20-2829,12=20+2832,60=20@@=20= find_target_tuple(Relation=20rel,=20ChangeContext=20*chgcxt,=20= TupleTableSlot=20*locator,=0A=20=09scan=20=3D=20index_beginscan(rel,=20= chgcxt->cc_ident_index,=20GetActiveSnapshot(),=0A=20=09=09=09=09=09=09=20= =20=20NULL,=20chgcxt->cc_ident_key_nentries,=200,=200);=0A=20=09= index_rescan(scan,=20chgcxt->cc_ident_key,=20= chgcxt->cc_ident_key_nentries,=20NULL,=200);=0A-=09retval=20=3D=20= index_getnext_slot(scan,=20ForwardScanDirection,=20retrieved);=0A+=09= while=20(index_getnext_slot(scan,=20ForwardScanDirection,=20retrieved))=0A= +=09{=0A+=09=09if=20(scan->xs_recheck=20&&=20!identity_key_equal(chgcxt,=20= locator,=20retrieved))=0A+=09=09{=0A+=09=09=09CHECK_FOR_INTERRUPTS();=0A= +=09=09=09continue;=0A+=09=09}=0A+=0A+=09=09retval=20=3D=20true;=0A+=09=09= break;=0A+=09}=0A=20=09index_endscan(scan);=0A=20=0A=20=09return=20= retval;=0A=20}=0A=20=0A+/*=0A+=20*=20Check=20whether=20the=20candidate=20= tuple=20matches=20the=20locator=20tuple=20on=20all=20replica=0A+=20*=20= identity=20key=20columns,=20using=20the=20same=20equality=20operators=20= as=20the=20identity=0A+=20*=20index=20scan.=20=20This=20is=20needed=20to=20= filter=20lossy=20index=20matches,=20such=20as=20GiST=0A+=20*=20= multirange=20scans.=0A+=20*/=0A+static=20bool=0A= +identity_key_equal(ChangeContext=20*chgcxt,=20TupleTableSlot=20= *locator,=0A+=09=09=09=09=20=20=20TupleTableSlot=20*candidate)=0A+{=0A+=09= Form_pg_index=20idx=20=3D=20chgcxt->cc_ident_index->rd_index;=0A+=0A+=09= slot_getallattrs(locator);=0A+=09slot_getallattrs(candidate);=0A+=0A+=09= for=20(int=20i=20=3D=200;=20i=20<=20chgcxt->cc_ident_key_nentries;=20= i++)=0A+=09{=0A+=09=09ScanKey=09=09entry=20=3D=20= &chgcxt->cc_ident_key[i];=0A+=09=09AttrNumber=09attno=20=3D=20= idx->indkey.values[i];=0A+=0A+=09=09Assert(attno=20>=200);=0A+=0A+=09=09= if=20(locator->tts_isnull[attno=20-=201]=20!=3D=20= candidate->tts_isnull[attno=20-=201])=0A+=09=09=09return=20false;=0A+=0A= +=09=09if=20(locator->tts_isnull[attno=20-=201])=0A+=09=09=09continue;=0A= +=0A+=09=09if=20(!DatumGetBool(FunctionCall2Coll(&entry->sk_func,=0A+=09=09= =09=09=09=09=09=09=09=09=09entry->sk_collation,=0A+=09=09=09=09=09=09=09=09= =09=09=09candidate->tts_values[attno=20-=201],=0A+=09=09=09=09=09=09=09=09= =09=09=09entry->sk_argument)))=0A+=09=09=09return=20false;=0A+=09}=0A+=0A= +=09return=20true;=0A+}=0A+=0A=20/*=0A=20=20*=20Decode=20and=20apply=20= concurrent=20changes,=20up=20to=20(and=20including)=20the=20record=20= whose=0A=20=20*=20LSN=20is=20'end_of_wal'.=0A@@=20-2944,13=20+2995,20=20= @@=20initialize_change_context(ChangeContext=20*chgcxt,=0A=20=09=09=09=09= =09=09opcintype,=0A=20=09=09=09=09=09=09opno,=0A=20=09=09=09=09=09=09= opcode;=0A+=09=09=09StrategyNumber=20eq_strategy;=0A=20=0A=20=09=09=09= entry=20=3D=20&chgcxt->cc_ident_key[i];=0A=20=0A=20=09=09=09opfamily=20=3D= =20chgcxt->cc_ident_index->rd_opfamily[i];=0A=20=09=09=09opcintype=20=3D=20= chgcxt->cc_ident_index->rd_opcintype[i];=0A+=09=09=09eq_strategy=20=3D=20= IndexAmTranslateCompareType(COMPARE_EQ,=0A+=09=09=09=09=09=09=09=09=09=09= =09=09=09=20=20chgcxt->cc_ident_index->rd_rel->relam,=0A+=09=09=09=09=09=09= =09=09=09=09=09=09=09=20=20opfamily,=20false);=0A+=09=09=09if=20= (eq_strategy=20=3D=3D=20InvalidStrategy)=0A+=09=09=09=09elog(ERROR,=20= "failed=20to=20find=20equality=20strategy=20for=20index=20operator=20= family=20%u=20for=20type=20%u",=0A+=09=09=09=09=09=20opfamily,=20= opcintype);=0A=20=09=09=09opno=20=3D=20get_opfamily_member(opfamily,=20= opcintype,=20opcintype,=0A-=09=09=09=09=09=09=09=09=09=20=20=20= BTEqualStrategyNumber);=0A+=09=09=09=09=09=09=09=09=09=20=20=20= eq_strategy);=0A=20=09=09=09if=20(!OidIsValid(opno))=0A=20=09=09=09=09= elog(ERROR,=20"failed=20to=20find=20=3D=20operator=20for=20type=20%u",=20= opcintype);=0A=20=09=09=09opcode=20=3D=20get_opcode(opno);=0A@@=20= -2960,7=20+3018,7=20@@=20initialize_change_context(ChangeContext=20= *chgcxt,=0A=20=09=09=09/*=20Initialize=20everything=20but=20argument.=20= */=0A=20=09=09=09ScanKeyInit(entry,=0A=20=09=09=09=09=09=09i=20+=201,=0A= -=09=09=09=09=09=09BTEqualStrategyNumber,=20opcode,=0A+=09=09=09=09=09=09= eq_strategy,=20opcode,=0A=20=09=09=09=09=09=09(Datum)=200);=0A=20=09=09=09= entry->sk_collation=20=3D=20chgcxt->cc_ident_index->rd_indcollation[i];=0A= =20=09=09}=0Adiff=20--git=20a/src/test/modules/injection_points/Makefile=20= b/src/test/modules/injection_points/Makefile=0Aindex=20= f057d143d1a..c01d2fb095c=20100644=0A---=20= a/src/test/modules/injection_points/Makefile=0A+++=20= b/src/test/modules/injection_points/Makefile=0A@@=20-15,6=20+15,8=20@@=20= REGRESS_OPTS=20=3D=20--dlpath=3D$(top_builddir)/src/test/regress=0A=20= ISOLATION=20=3D=20basic=20\=0A=20=09=20=20=20=20inplace=20\=0A=20=09=20=20= =20=20repack=20\=0A+=09=20=20=20=20repack_temporal=20\=0A+=09=20=20=20=20= repack_temporal_multirange=20\=0A=20=09=20=20=20=20repack_toast=20\=0A=20= =09=20=20=20=20syscache-update-pruned=20\=0A=20=09=20=20=20=20= heap_lock_update=0Adiff=20--git=20= a/src/test/modules/injection_points/expected/repack_temporal.out=20= b/src/test/modules/injection_points/expected/repack_temporal.out=0Anew=20= file=20mode=20100644=0Aindex=2000000000000..e6b06c00cec=0A---=20= /dev/null=0A+++=20= b/src/test/modules/injection_points/expected/repack_temporal.out=0A@@=20= -0,0=20+1,68=20@@=0A+Parsed=20test=20spec=20with=202=20sessions=0A+=0A= +starting=20permutation:=20wait_before_lock=20update_target=20= check_after_update=20wakeup_before_lock=20check_after_repack=0A= +injection_points_attach=0A+-----------------------=0A+=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=0A+(1=20row)=0A+=0A= +step=20wait_before_lock:=20=0A+=09REPACK=20(CONCURRENTLY)=20= repack_temporal=20USING=20INDEX=20repack_temporal_pkey;=0A+=20=0A+step=20update_target:=20=0A+=09UPDATE=20repack_temporal=0A+=09= SET=20label=20=3D=20'updated'=0A+=09WHERE=20id=20=3D=20'[2,3)'=20AND=20= valid_at=20=3D=20'[2000-01-10,2000-01-20)';=0A+=0A+step=20= check_after_update:=20=0A+=09INSERT=20INTO=20relfilenodes(node)=0A+=09= SELECT=20relfilenode=20FROM=20pg_class=20WHERE=20relname=20=3D=20= 'repack_temporal';=0A+=0A+=09--=20Expect=202=20rows=0A+=09SELECT=20id,=20= valid_at,=20label=0A+=09FROM=20repack_temporal=0A+=09ORDER=20BY=20id,=20= valid_at,=20label;=0A+=0A+id=20=20=20=20|valid_at=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20|label=20=20=0A= +------+-----------------------+-------=0A= +[1,10)|[01-01-2000,02-01-2000)|other=20=20=0A+[2,3)=20= |[01-10-2000,01-20-2000)|updated=0A+(2=20rows)=0A+=0A+step=20= wakeup_before_lock:=20=0A+=09SELECT=20= injection_points_wakeup('repack-concurrently-before-lock');=0A+=0A= +injection_points_wakeup=0A+-----------------------=0A+=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=0A+(1=20row)=0A+=0A= +step=20wait_before_lock:=20<...=20completed>=0A+step=20= check_after_repack:=20=0A+=09INSERT=20INTO=20relfilenodes(node)=0A+=09= SELECT=20relfilenode=20FROM=20pg_class=20WHERE=20relname=20=3D=20= 'repack_temporal';=0A+=0A+=09--=20Expect=202,=20proving=20that=20repack=20= has=20rewritten=20the=20table=0A+=09SELECT=20count(DISTINCT=20node)=20= FROM=20relfilenodes;=0A+=0A+=09--=20Expect=202=20rows=0A+=09SELECT=20id,=20= valid_at,=20label=0A+=09FROM=20repack_temporal=0A+=09ORDER=20BY=20id,=20= valid_at,=20label;=0A+=0A+count=0A+-----=0A+=20=20=20=202=0A+(1=20row)=0A= +=0A+id=20=20=20=20|valid_at=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= |label=20=20=0A+------+-----------------------+-------=0A= +[1,10)|[01-01-2000,02-01-2000)|other=20=20=0A+[2,3)=20= |[01-10-2000,01-20-2000)|updated=0A+(2=20rows)=0A+=0A= +injection_points_detach=0A+-----------------------=0A+=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=0A+(1=20row)=0A+=0A= diff=20--git=20= a/src/test/modules/injection_points/expected/repack_temporal_multirange.ou= t=20= b/src/test/modules/injection_points/expected/repack_temporal_multirange.ou= t=0Anew=20file=20mode=20100644=0Aindex=2000000000000..3f5ff8cfb0f=0A---=20= /dev/null=0A+++=20= b/src/test/modules/injection_points/expected/repack_temporal_multirange.ou= t=0A@@=20-0,0=20+1,74=20@@=0A+Parsed=20test=20spec=20with=202=20sessions=0A= +=0A+starting=20permutation:=20wait_before_lock=20update_target=20= check_after_update=20wakeup_before_lock=20final_check=0A= +injection_points_attach=0A+-----------------------=0A+=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=0A+(1=20row)=0A+=0A= +step=20wait_before_lock:=20=0A+=09REPACK=20(CONCURRENTLY)=20= repack_temporal_multirange=0A+=09=09USING=20INDEX=20= repack_temporal_multirange_pkey;=0A+=20=0A+step=20= update_target:=20=0A+=09UPDATE=20repack_temporal_multirange=0A+=09SET=20= label=20=3D=20'updated'=0A+=09WHERE=20id=20=3D=20= int4multirange(int4range(1,=207))=0A+=09=20=20AND=20valid_at=20=3D=20= datemultirange(daterange('2000-01-01',=20'2000-02-01'));=0A+=0A+step=20= check_after_update:=20=0A+=09INSERT=20INTO=20relfilenodes(node)=0A+=09= SELECT=20relfilenode=0A+=09FROM=20pg_class=0A+=09WHERE=20relname=20=3D=20= 'repack_temporal_multirange';=0A+=0A+=09--=20Expect=202=20rows=0A+=09= SELECT=20id,=20valid_at,=20label=0A+=09FROM=20repack_temporal_multirange=0A= +=09ORDER=20BY=20id,=20valid_at,=20label;=0A+=0A+id=20=20=20=20=20=20=20=20= =20=20=20|valid_at=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= |label=20=20=0A+-------------+-------------------------+-------=0A= +{[1,3),[5,7)}|{[01-01-2000,02-01-2000)}|other=20=20=0A+{[1,7)}=20=20=20=20= =20=20|{[01-01-2000,02-01-2000)}|updated=0A+(2=20rows)=0A+=0A+step=20= wakeup_before_lock:=20=0A+=09SELECT=20= injection_points_wakeup('repack-concurrently-before-lock');=0A+=0A= +injection_points_wakeup=0A+-----------------------=0A+=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=0A+(1=20row)=0A+=0A= +step=20wait_before_lock:=20<...=20completed>=0A+step=20final_check:=20=0A= +=09INSERT=20INTO=20relfilenodes(node)=0A+=09SELECT=20relfilenode=0A+=09= FROM=20pg_class=0A+=09WHERE=20relname=20=3D=20= 'repack_temporal_multirange';=0A+=0A+=09--=20Expect=202,=20proving=20= that=20repack=20has=20rewritten=20the=20table=0A+=09SELECT=20= count(DISTINCT=20node)=20FROM=20relfilenodes;=0A+=0A+=09--=20Expect=202=20= rows=0A+=09SELECT=20id,=20valid_at,=20label=0A+=09FROM=20= repack_temporal_multirange=0A+=09ORDER=20BY=20id,=20valid_at,=20label;=0A= +=0A+count=0A+-----=0A+=20=20=20=202=0A+(1=20row)=0A+=0A+id=20=20=20=20=20= =20=20=20=20=20=20|valid_at=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20|label=20=20=0A+-------------+-------------------------+-------=0A= +{[1,3),[5,7)}|{[01-01-2000,02-01-2000)}|other=20=20=0A+{[1,7)}=20=20=20=20= =20=20|{[01-01-2000,02-01-2000)}|updated=0A+(2=20rows)=0A+=0A= +injection_points_detach=0A+-----------------------=0A+=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=0A+(1=20row)=0A+=0A= diff=20--git=20a/src/test/modules/injection_points/meson.build=20= b/src/test/modules/injection_points/meson.build=0Aindex=20= fb1418e2caa..59dba1cb023=20100644=0A---=20= a/src/test/modules/injection_points/meson.build=0A+++=20= b/src/test/modules/injection_points/meson.build=0A@@=20-46,6=20+46,8=20= @@=20tests=20+=3D=20{=0A=20=20=20=20=20=20=20'basic',=0A=20=20=20=20=20=20= =20'inplace',=0A=20=20=20=20=20=20=20'repack',=0A+=20=20=20=20=20=20= 'repack_temporal',=0A+=20=20=20=20=20=20'repack_temporal_multirange',=0A=20= =20=20=20=20=20=20'repack_toast',=0A=20=20=20=20=20=20=20= 'syscache-update-pruned',=0A=20=20=20=20=20=20=20'heap_lock_update',=0A= diff=20--git=20= a/src/test/modules/injection_points/specs/repack_temporal.spec=20= b/src/test/modules/injection_points/specs/repack_temporal.spec=0Anew=20= file=20mode=20100644=0Aindex=2000000000000..9629d502ec1=0A---=20= /dev/null=0A+++=20= b/src/test/modules/injection_points/specs/repack_temporal.spec=0A@@=20= -0,0=20+1,90=20@@=0A+#=20REPACK=20(CONCURRENTLY)=20on=20a=20temporal=20= replica=20identity=20index.=0A+#=0A+#=20The=20table's=20replica=20= identity=20is=20a=20GiST=20index=20created=20by=20a=20temporal=20primary=0A= +#=20key.=20=20A=20concurrent=20UPDATE=20changes=20a=20non-key=20column=20= of=20one=20row,=20while=20another=0A+#=20row=20overlaps=20it=20on=20all=20= indexed=20columns.=20=20Replay=20must=20still=20find=20the=20exact=0A+#=20= target=20row.=0A+setup=0A+{=0A+=09CREATE=20EXTENSION=20injection_points;=0A= +=0A+=09CREATE=20TABLE=20repack_temporal=20(=0A+=09=09id=20int4range,=0A= +=09=09valid_at=20daterange,=0A+=09=09label=20text,=0A+=09=09PRIMARY=20= KEY=20(id,=20valid_at=20WITHOUT=20OVERLAPS)=0A+=09);=0A+=0A+=09ALTER=20= TABLE=20repack_temporal=20REPLICA=20IDENTITY=20USING=20INDEX=20= repack_temporal_pkey;=0A+=0A+=09INSERT=20INTO=20repack_temporal(id,=20= valid_at,=20label)=0A+=09VALUES=0A+=09=09('[1,10)',=20= '[2000-01-01,2000-02-01)',=20'other'),=0A+=09=09('[2,3)',=20=20= '[2000-01-10,2000-01-20)',=20'target');=0A+=0A+=09CREATE=20TABLE=20= relfilenodes(node=20oid);=0A+}=0A+=0A+teardown=0A+{=0A+=09DROP=20TABLE=20= repack_temporal;=0A+=09DROP=20EXTENSION=20injection_points;=0A+=09DROP=20= TABLE=20relfilenodes;=0A+}=0A+=0A+session=20s1=0A+setup=0A+{=0A+=09= SELECT=20injection_points_set_local();=0A+=09SELECT=20= injection_points_attach('repack-concurrently-before-lock',=20'wait');=0A= +}=0A+step=20wait_before_lock=0A+{=0A+=09REPACK=20(CONCURRENTLY)=20= repack_temporal=20USING=20INDEX=20repack_temporal_pkey;=0A+}=0A+step=20= check_after_repack=0A+{=0A+=09INSERT=20INTO=20relfilenodes(node)=0A+=09= SELECT=20relfilenode=20FROM=20pg_class=20WHERE=20relname=20=3D=20= 'repack_temporal';=0A+=0A+=09--=20Expect=202,=20proving=20that=20repack=20= has=20rewritten=20the=20table=0A+=09SELECT=20count(DISTINCT=20node)=20= FROM=20relfilenodes;=0A+=0A+=09--=20Expect=202=20rows=0A+=09SELECT=20id,=20= valid_at,=20label=0A+=09FROM=20repack_temporal=0A+=09ORDER=20BY=20id,=20= valid_at,=20label;=0A+}=0A+teardown=0A+{=0A+=09SELECT=20= injection_points_detach('repack-concurrently-before-lock');=0A+}=0A+=0A= +session=20s2=0A+step=20update_target=0A+{=0A+=09UPDATE=20= repack_temporal=0A+=09SET=20label=20=3D=20'updated'=0A+=09WHERE=20id=20=3D= =20'[2,3)'=20AND=20valid_at=20=3D=20'[2000-01-10,2000-01-20)';=0A+}=0A= +step=20check_after_update=0A+{=0A+=09INSERT=20INTO=20relfilenodes(node)=0A= +=09SELECT=20relfilenode=20FROM=20pg_class=20WHERE=20relname=20=3D=20= 'repack_temporal';=0A+=0A+=09--=20Expect=202=20rows=0A+=09SELECT=20id,=20= valid_at,=20label=0A+=09FROM=20repack_temporal=0A+=09ORDER=20BY=20id,=20= valid_at,=20label;=0A+}=0A+step=20wakeup_before_lock=0A+{=0A+=09SELECT=20= injection_points_wakeup('repack-concurrently-before-lock');=0A+}=0A+=0A= +permutation=0A+=09wait_before_lock=0A+=09update_target=0A+=09= check_after_update=0A+=09wakeup_before_lock=0A+=09check_after_repack=0A= diff=20--git=20= a/src/test/modules/injection_points/specs/repack_temporal_multirange.spec=20= b/src/test/modules/injection_points/specs/repack_temporal_multirange.spec=0A= new=20file=20mode=20100644=0Aindex=2000000000000..dfff1f2234d=0A---=20= /dev/null=0A+++=20= b/src/test/modules/injection_points/specs/repack_temporal_multirange.spec=0A= @@=20-0,0=20+1,102=20@@=0A+#=20REPACK=20(CONCURRENTLY)=20on=20a=20= temporal=20replica=20identity=20index=20with=20lossy=0A+#=20multirange=20= equality.=0A+#=0A+#=20The=20leading=20identity=20column=20is=20an=20= int4multirange.=20Two=20distinct=20rows=20have=0A+#=20different=20= multirange=20values=20but=20the=20same=20union=20range,=20so=20GiST=20= equality=20can=0A+#=20produce=20both=20as=20candidates=20and=20requires=20= exact=20recheck.=0A+setup=0A+{=0A+=09CREATE=20EXTENSION=20= injection_points;=0A+=0A+=09CREATE=20TABLE=20repack_temporal_multirange=20= (=0A+=09=09id=20int4multirange,=0A+=09=09valid_at=20datemultirange,=0A+=09= =09label=20text,=0A+=09=09PRIMARY=20KEY=20(id,=20valid_at=20WITHOUT=20= OVERLAPS)=0A+=09);=0A+=0A+=09ALTER=20TABLE=20repack_temporal_multirange=0A= +=09=09REPLICA=20IDENTITY=20USING=20INDEX=20= repack_temporal_multirange_pkey;=0A+=0A+=09--=20(1,3)+(5+7)=20is=20the=20= same=20uninon=20range=20of=20(1-7),=20but=20needs=20recheck=0A+=09INSERT=20= INTO=20repack_temporal_multirange(id,=20valid_at,=20label)=0A+=09VALUES=0A= +=09=09(int4multirange(int4range(1,=203),=20int4range(5,=207)),=0A+=09=09= =20datemultirange(daterange('2000-01-01',=20'2000-02-01')),=0A+=09=09=20= 'other'),=0A+=09=09(int4multirange(int4range(1,=207)),=0A+=09=09=20= datemultirange(daterange('2000-01-01',=20'2000-02-01')),=0A+=09=09=20= 'target');=0A+=0A+=09CREATE=20TABLE=20relfilenodes(node=20oid);=0A+}=0A+=0A= +teardown=0A+{=0A+=09DROP=20TABLE=20repack_temporal_multirange;=0A+=09= DROP=20EXTENSION=20injection_points;=0A+=09DROP=20TABLE=20relfilenodes;=0A= +}=0A+=0A+session=20s1=0A+setup=0A+{=0A+=09SELECT=20= injection_points_set_local();=0A+=09SELECT=20= injection_points_attach('repack-concurrently-before-lock',=20'wait');=0A= +}=0A+step=20wait_before_lock=0A+{=0A+=09REPACK=20(CONCURRENTLY)=20= repack_temporal_multirange=0A+=09=09USING=20INDEX=20= repack_temporal_multirange_pkey;=0A+}=0A+step=20final_check=0A+{=0A+=09= INSERT=20INTO=20relfilenodes(node)=0A+=09SELECT=20relfilenode=0A+=09FROM=20= pg_class=0A+=09WHERE=20relname=20=3D=20'repack_temporal_multirange';=0A+=0A= +=09--=20Expect=202,=20proving=20that=20repack=20has=20rewritten=20the=20= table=0A+=09SELECT=20count(DISTINCT=20node)=20FROM=20relfilenodes;=0A+=0A= +=09--=20Expect=202=20rows=0A+=09SELECT=20id,=20valid_at,=20label=0A+=09= FROM=20repack_temporal_multirange=0A+=09ORDER=20BY=20id,=20valid_at,=20= label;=0A+}=0A+teardown=0A+{=0A+=09SELECT=20= injection_points_detach('repack-concurrently-before-lock');=0A+}=0A+=0A= +session=20s2=0A+step=20update_target=0A+{=0A+=09UPDATE=20= repack_temporal_multirange=0A+=09SET=20label=20=3D=20'updated'=0A+=09= WHERE=20id=20=3D=20int4multirange(int4range(1,=207))=0A+=09=20=20AND=20= valid_at=20=3D=20datemultirange(daterange('2000-01-01',=20= '2000-02-01'));=0A+}=0A+step=20check_after_update=0A+{=0A+=09INSERT=20= INTO=20relfilenodes(node)=0A+=09SELECT=20relfilenode=0A+=09FROM=20= pg_class=0A+=09WHERE=20relname=20=3D=20'repack_temporal_multirange';=0A+=0A= +=09--=20Expect=202=20rows=0A+=09SELECT=20id,=20valid_at,=20label=0A+=09= FROM=20repack_temporal_multirange=0A+=09ORDER=20BY=20id,=20valid_at,=20= label;=0A+}=0A+step=20wakeup_before_lock=0A+{=0A+=09SELECT=20= injection_points_wakeup('repack-concurrently-before-lock');=0A+}=0A+=0A= +permutation=0A+=09wait_before_lock=0A+=09update_target=0A+=09= check_after_update=0A+=09wakeup_before_lock=0A+=09final_check=0A--=20=0A= 2.50.1=20(Apple=20Git-155)=0A=0A= --Apple-Mail=_F3963B4C-E623-49EC-96D8-CE8A89FD8D0C--